// http://astronomy.swin.edu.au/~pbourke/geometry/polyarea/ vec2 barycenter(const Polygon2d& P) { gx_assert(P.size() > 0) ; double A = signed_area(P) ; if(::fabs(A) < 1e-30) { return P[0] ; } double x = 0.0 ; double y = 0.0 ; for(unsigned int i=0; i<P.size(); i++) { unsigned int j = (i+1) % P.size() ; const vec2& t1 = P[i] ; const vec2& t2 = P[j] ; double d = (t1.x * t2.y - t2.x * t1.y) ; x += (t1.x + t2.x) * d ; y += (t1.y + t2.y) * d ; } return vec2( x / (6.0 * A), y / (6.0 * A) ) ; }
void clip_polygon_by_half_plane( const Polygon2d& P, const vec2& q1, const vec2& q2, Polygon2d& result, bool invert ) { result.clear() ; if(P.size() == 0) { return ; } if(P.size() == 1) { if(point_is_in_half_plane(P[0], q1, q2, invert)) { result.push_back(P[0]) ; } return ; } vec2 prev_p = P[P.size() - 1] ; Sign prev_status = point_is_in_half_plane( prev_p, q1, q2, invert ) ; for(unsigned int i=0; i<P.size(); i++) { vec2 p = P[i] ; Sign status = point_is_in_half_plane( p, q1, q2, invert ) ; if( status != prev_status && status != ZERO && prev_status != ZERO ) { vec2 intersect ; if(intersect_segments(prev_p, p, q1, q2, intersect)) { result.push_back(intersect) ; } else { } } switch(status) { case NEGATIVE: break ; case ZERO: result.push_back(p) ; break ; case POSITIVE: result.push_back(p) ; break ; } prev_p = p ; prev_status = status ; } }
void convex_clip_segment( Segment2d& S, const Polygon2d& window ) { gx_parano_assert(polygon_is_convex(window)) ; bool invert = (signed_area(window) < 0) ; for(unsigned int i=0; i<window.size(); i++) { unsigned int j = ((i+1) % window.size()) ; clip_segment_by_half_plane(S, window[i], window[j], invert) ; } }
// http://astronomy.swin.edu.au/~pbourke/geometry/polyarea/ double signed_area(const Polygon2d& P) { double result = 0 ; for(unsigned int i=0; i<P.size(); i++) { unsigned int j = (i+1) % P.size() ; const vec2& t1 = P[i] ; const vec2& t2 = P[j] ; result += t1.x * t2.y - t2.x * t1.y ; } result /= 2.0 ; return result ; }
void save_polygon(const Polygon2d& P, const std::string& file_name) { std::ofstream out(file_name.c_str()) ; {for(unsigned int i=0; i<P.size(); i++) { out << "v " << P[i].x << " " << P[i].y << std::endl ; out << "vt " << P[i].x << " " << P[i].y << std::endl ; }} out << "f " ; {for(unsigned int i=0; i<P.size(); i++) { out << i+1 << "/" << i+1 << " " ; }} out << std::endl ; }
vec2 vertices_barycenter(const Polygon2d& P) { gx_assert(P.size() != 0) ; double x = 0 ; double y = 0 ; for(unsigned int i=0; i<P.size(); i++) { x += P[i].x ; y += P[i].y ; } x /= double(P.size()) ; y /= double(P.size()) ; return vec2(x,y) ; }
bool polygon_is_convex(const Polygon2d& P) { Sign s = ZERO ; for(unsigned int i=0; i<P.size(); i++) { unsigned int j = ((i+1) % P.size()) ; unsigned int k = ((j+1) % P.size()) ; Sign cur_s = orient(P[i],P[j],P[k]) ; if(s != ZERO && cur_s != ZERO && cur_s != s) { return false ; } if(cur_s != ZERO) { s = cur_s ; } } return true ; }
// Clipping with convex window using Sutherland-Hogdman reentrant clipping void convex_clip_polygon( const Polygon2d& P, const Polygon2d& clip, Polygon2d& result ) { gx_parano_assert(polygon_is_convex(clip)) ; Polygon2d tmp1 = P ; bool invert = (signed_area(tmp1) != signed_area(clip)) ; Polygon2d tmp2 ; Polygon2d* src = &tmp1 ; Polygon2d* dst = &tmp2 ; for(unsigned int i=0; i<clip.size(); i++) { unsigned int j = ((i+1) % clip.size()) ; const vec2& p1 = clip[i] ; const vec2& p2 = clip[j] ; clip_polygon_by_half_plane(*src, p1, p2, *dst, invert) ; gx_swap(src, dst) ; } result = *src ; }
bool point_is_in_kernel(const Polygon2d& P, const vec2& p) { Sign sign = ZERO ; for(unsigned int i=0 ; i<P.size() ; i++) { unsigned int j = (i+1) % P.size() ; const vec2& p1 = P[i] ; const vec2& p2 = P[j] ; Sign cur_sign = orient(p, p1, p2) ; if(sign == ZERO) { sign = cur_sign ; } else { if(cur_sign != ZERO && cur_sign != sign) { return false ; } } } return true ; }
void minimum_area_enclosing_rectangle( const Polygon2d& PP, vec2& S, vec2& T ) { // Note: this implementation has O(n2) complexity :-( // (where n is the number of vertices in the convex hull) // If this appears to be a bottleneck, use a smarter // implementation with better complexity. Polygon2d P ; convex_hull(PP, P) ; int N = P.size() ; // Add the first vertex at the end of P P.push_back(P[0]) ; double min_area = Numeric::big_double ; for(int i=1; i<=N; i++) { vec2 Si = P[i] - P[i-1] ; if( ( Si.length2() ) < 1e-20) { continue ; } vec2 Ti(-Si.y, Si.x) ; normalize(Si) ; normalize(Ti) ; double s0 = Numeric::big_double ; double s1 = -Numeric::big_double ; double t0 = Numeric::big_double ; double t1 = -Numeric::big_double ; for(int j=1; j<N; j++) { vec2 D = P[j] - P[0] ; double s = dot(Si, D) ; s0 = gx_min(s0, s) ; s1 = gx_max(s1, s) ; double t = dot(Ti, D) ; t0 = gx_min(t0, t) ; t1 = gx_max(t1, t) ; } double area = (s1 - s0) * (t1 - t0) ; if(area < min_area) { min_area = area ; if((s1 - s0) < (t1 - t0)) { S = Si ; T = Ti ; } else { S = Ti ; T = Si ; } } } }
// Compute the kernel using Sutherland-Hogdman reentrant clipping // The kernel is obtained by clipping the polygon with each // half-plane yielded by its sides. void kernel(const Polygon2d& P, Polygon2d& result) { Array1d<Sign> sign(P.size()) ; for(unsigned int i=0; i<P.size(); i++) { unsigned int j = ((i+1) % P.size()) ; unsigned int k = ((j+1) % P.size()) ; sign(j) = orient(P[i],P[j],P[k]) ; } bool invert = (signed_area(P) < 0) ; Polygon2d tmp1 = P ; Polygon2d tmp2 ; Polygon2d* src = &tmp1 ; Polygon2d* dst = &tmp2 ; for(unsigned int i=0; i<P.size(); i++) { unsigned int j = ((i+1) % P.size()) ; const vec2& p1 = P[i] ; const vec2& p2 = P[j] ; if((p2-p1).length() == 0) { std::cerr << "null edge in poly" << std::endl ; continue ; } // Optimization: do not clip by convex-convex edges // (Thanks to Rodrigo Toledo for the tip !) if(!invert && sign(i) != NEGATIVE && sign(j) != NEGATIVE) { continue ; } if(invert && sign(i) != POSITIVE && sign(j) != POSITIVE) { continue ; } clip_polygon_by_half_plane(*src, p1, p2, *dst, invert) ; gx_swap(src, dst) ; } result = *src ; }
void convex_hull(const Polygon2d& PP, Polygon2d& result) { result.clear() ; int n = PP.size() ; vec2* P = new vec2[n+1] ; { for(int i=0; i<n; i++) { P[i] = PP[i] ; }} int u = make_chain(P, n, cmpl); P[n] = P[0]; int ch = u+make_chain(P+u, n-u+1, cmph); {for(int i=0; i<ch; i++) { result.push_back(P[i]) ; }} delete[] P ; }