// tests whether or not a given polygon (in CW or CCW order) is simple // a polygon is considered simple if its sides do not intersect. bool IsSimple(const vector<PT> &p) { for (int i = 0; i < p.size(); i++) { for (int k = i+1; k < p.size(); k++) { int j = (i+1) % p.size(); int l = (k+1) % p.size(); if (i == l || j == k) continue; if (SegmentsIntersect(p[i], p[j], p[k], p[l])) return false; } } return true; }
int main() { // expected (-5,2)/(5,-2)/(-5,2)/(5,2)/(5,2) (7.5,3) (2.5,1) cerr << RotateCCW90(PT(2,5)) << endl; cerr << RotateCW90(PT(2,5)) << endl; cerr << RotateCCW(PT(2,5),M_PI/2) << endl; cerr << ProjectPointLine(PT(-5,-2), PT(10,4), PT(3,7)) << endl; cerr << ProjectPointSegment(PT(-5,-2), PT(10,4), PT(3,7)) << " " << ProjectPointSegment(PT(7.5,3), PT(10,4), PT(3,7)) << " " << ProjectPointSegment(PT(-5,-2), PT(2.5,1), PT(3,7)) << endl; // expected 6.78903/1 0 1/0 0 1/1 1 1 0/(1,2)/(1,1) cerr << DistancePointPlane(4,-4,3,2,-2,5,-8) << endl; cerr << LinesParallel(PT(1,1), PT(3,5), PT(2,1), PT(4,5)) << " " << LinesParallel(PT(1,1), PT(3,5), PT(2,0), PT(4,5)) << " " << LinesParallel(PT(1,1), PT(3,5), PT(5,9), PT(7,13)) << endl; cerr << LinesCollinear(PT(1,1), PT(3,5), PT(2,1), PT(4,5)) << " " << LinesCollinear(PT(1,1), PT(3,5), PT(2,0), PT(4,5)) << " " << LinesCollinear(PT(1,1), PT(3,5), PT(5,9), PT(7,13)) << endl; cerr << SegmentsIntersect(PT(0,0), PT(2,4), PT(3,1), PT(-1,3)) << " " << SegmentsIntersect(PT(0,0), PT(2,4), PT(4,3), PT(0,5)) << " " << SegmentsIntersect(PT(0,0), PT(2,4), PT(2,-1), PT(-2,1)) << " " << SegmentsIntersect(PT(0,0), PT(2,4), PT(5,5), PT(1,7)) << endl; cerr << ComputeLineIntersection(PT(0,0),PT(2,4),PT(3,1),PT(-1,3)) << endl; cerr << ComputeCircleCenter(PT(-3,4), PT(6,1), PT(4,5)) << endl; vector<PT> v; v.push_back(PT(0,0)); v.push_back(PT(5,0)); v.push_back(PT(5,5)); v.push_back(PT(0,5)); // expected: 1 1 1 0 0 cerr << PointInPolygon(v, PT(2,2)) << " " << PointInPolygon(v, PT(2,0)) << " " << PointInPolygon(v, PT(0,2)) << " " << PointInPolygon(v, PT(5,2)) << " " << PointInPolygon(v, PT(2,5)) << endl; // expected: 0 1 1 1 1 cerr << PointOnPolygon(v, PT(2,2)) << " " << PointOnPolygon(v, PT(2,0)) << " " << PointOnPolygon(v, PT(0,2)) << " " << PointOnPolygon(v, PT(5,2)) << " " << PointOnPolygon(v, PT(2,5)) << endl; // expected: (1,6)/(5,4) (4,5)//(4,5) (5,4)//(4,5) (5,4) vector<PT> u = CircleLineIntersection(PT(0,6), PT(2,6), PT(1,1), 5); for (int i = 0; i < u.size(); i++) cerr << u[i] << " "; cerr << endl; u = CircleLineIntersection(PT(0,9), PT(9,0), PT(1,1), 5); for (int i = 0; i < u.size(); i++) cerr << u[i] << " "; cerr << endl; u = CircleCircleIntersection(PT(1,1), PT(10,10), 5, 5); for (int i = 0; i < u.size(); i++) cerr << u[i] << " "; cerr << endl; u = CircleCircleIntersection(PT(1,1), PT(8,8), 5, 5); for (int i = 0; i < u.size(); i++) cerr << u[i] << " "; cerr << endl; u = CircleCircleIntersection(PT(1,1), PT(4.5,4.5), 10, sqrt(2.0)/2.0); for (int i = 0; i < u.size(); i++) cerr << u[i] << " "; cerr << endl; u = CircleCircleIntersection(PT(1,1), PT(4.5,4.5), 5, sqrt(2.0)/2.0); for (int i = 0; i < u.size(); i++) cerr << u[i] << " "; cerr << endl; // area should be 5.0; centroid should be (1.1666666, 1.166666) PT pa[] = {PT(0,0), PT(5,0), PT(1,1), PT(0,5)}; vector<PT> p(pa, pa+4); PT c = ComputeCentroid(p); cerr << "Area: " << ComputeArea(p) << endl; cerr << "Centroid: " << c << endl; }
int convex_polygon_intersection( int n, // n >= 3 const double *P, int m, // m >= 3 const double *Q, int *ni, // on input, size of Pi, on output, numer of points in Pi double *Pi // output intersection polygon ){ int i, j; if(n < 3){ return -1; } if(NULL == P){ return -2; } if(m < 3){ return -3; } if(NULL == Q){ return -4; } if(NULL == ni){ return -5; } if(NULL == Pi){ return -6; } // Implementation of: // "A new linear algorithm for intersecting convex polygons" // Joseph O'Rourke, Chi-Bin Chien, Thomas Olson, and David Naddor // Computer Graphics and Image Processing 19, pp. 384-391 (1982) const int nPi = *ni; *ni = 0; int ip = 1, iq = 1; int ipp = 0, iqp = 0; // prev of ip and iq char inside = ' '; // record first intersection int first_xsected = 0; int ipf = n, iqf = m; int first_iter = 0; int Pi_full = 0; int iter; // First, a bounding box check { int iP_x_min = 0, iP_x_max = 0, iP_y_min = 0, iP_y_max = 0; int iQ_x_min = 0, iQ_x_max = 0, iQ_y_min = 0, iQ_y_max = 0; for(i = 1; i < n; ++i){ if(P[2*i+0] < P[2*iP_x_min+0]){ iP_x_min = i; } if(P[2*i+1] < P[2*iP_y_min+1]){ iP_y_min = i; } if(P[2*i+0] > P[2*iP_x_max+0]){ iP_x_max = i; } if(P[2*i+1] > P[2*iP_y_max+1]){ iP_y_max = i; } } for(i = 1; i < m; ++i){ if(Q[2*i+0] < Q[2*iQ_x_min+0]){ iQ_x_min = i; } if(Q[2*i+1] < Q[2*iQ_y_min+1]){ iQ_y_min = i; } if(Q[2*i+0] > Q[2*iQ_x_max+0]){ iQ_x_max = i; } if(Q[2*i+1] > Q[2*iQ_y_max+1]){ iQ_y_max = i; } } if( ( Q[2*iQ_x_min+0] > P[2*iP_x_max+0] ) || ( P[2*iP_x_min+0] > Q[2*iQ_x_max+0] ) || ( Q[2*iQ_y_min+1] > P[2*iP_y_max+1] ) || ( P[2*iP_y_min+1] > Q[2*iQ_y_max+1] ) ){ return 0; } } for(iter = 0; iter <= 2*(m+n); ++iter){ //fprintf(stderr, "iter %d, ip = %d, iq = %d, inside = %c\n", iter, ip, iq, inside); double xp[2]; if(SegmentsIntersect(&P[2*ipp],&P[2*ip],&Q[2*iqp],&Q[2*iq],xp, NULL)){ //fprintf(stderr, " xsect! %f,%f %f,%f %f,%f %f,%f\n", P[2*ipp+0],P[2*ipp+1],P[2*ip+0],P[2*ip+1],Q[2*iqp+0],Q[2*iqp+1],Q[2*iq+0],Q[2*iq+1]); if(first_xsected && first_iter != iter-1){ // if the first intersection was NOT found during the previous iteration if(ip == ipf && iq == iqf){ break; } // if this intersection is the same as the first intersection } if(*ni >= nPi){ Pi_full = 1; } if(!Pi_full){ Pi[2*(*ni)+0] = xp[0]; Pi[2*(*ni)+1] = xp[1]; (*ni)++; } //fprintf(stderr, " Adding %f,%f\n", Pi[2*((*ni)-1)+0], Pi[2*((*ni)-1)+1]); if(LeftTurn(&Q[2*iqp],&Q[2*iq],&P[2*ip]) >= 0){ inside = 'P'; }else{ inside = 'Q'; } if(!first_xsected){ first_xsected = 1; ipf = ip; iqf = iq; first_iter = iter; } } xp[0] = P[2*ip+0] + (Q[2*iq+0] - P[2*ipp+0]); xp[1] = P[2*ip+1] + (Q[2*iq+1] - P[2*ipp+1]); if(LeftTurn(&Q[2*iqp],&Q[2*iq],xp)/*Cross(Q[2*iq]-Q[2*iqp],P[2*ip]-P[2*ipp])*/ >= 0){ if(LeftTurn(&Q[2*iqp],&Q[2*iq],&P[2*ip]) >= 0){ // advance Q if(inside == 'Q'){ if(*ni >= nPi){ Pi_full = 1; } if(!Pi_full){ Pi[2*(*ni)+0] = Q[2*iq+0]; Pi[2*(*ni)+1] = Q[2*iq+1]; (*ni)++; } } iqp = iq; iq = (iq+1)%m; }else{ // advance P if(inside == 'P'){ if(*ni >= nPi){ Pi_full = 1; } if(!Pi_full){ Pi[2*(*ni)+0] = P[2*ip+0]; Pi[2*(*ni)+1] = P[2*ip+1]; (*ni)++; } } ipp = ip; ip = (ip+1)%n; } }else{ if(LeftTurn(&P[2*ipp],&P[2*ip],&Q[2*iq]) >= 0){ // advance P if(inside == 'P'){ if(*ni >= nPi){ Pi_full = 1; } if(!Pi_full){ Pi[2*(*ni)+0] = P[2*ip+0]; Pi[2*(*ni)+1] = P[2*ip+1]; (*ni)++; } } ipp = ip; ip = (ip+1)%n; }else{ // advance Q if(inside == 'Q'){ if(*ni >= nPi){ Pi_full = 1; } if(!Pi_full){ Pi[2*(*ni)+0] = Q[2*iq+0]; Pi[2*(*ni)+1] = Q[2*iq+1]; (*ni)++; } } iqp = iq; iq = (iq+1)%m; } } } // At this point, either P in Q, Q in P, or they don't intersect if(*ni == 0){ int flag = 1; for(j = 0; j < n; ++j){ // really we only need to check j == 0, but due to degeneracy, it is safest to check all for(i = 0; i < m; ++i){ if(LeftTurn(&Q[2*i],&Q[2*((i+1)%m)], &P[2*j]) < 0){ flag = 0; j = n+1; break; } } } if(flag){ // P in Q if(*ni+n >= nPi){ Pi_full = 1; } if(!Pi_full){ for(i = 0; i < n; ++i){ Pi[2*(*ni)+0] = P[2*i+0]; Pi[2*(*ni)+1] = P[2*i+1]; (*ni)++; } return 1; } }else{ flag = 1; for(j = 0; j < m; ++j){ // really we only need to check j == 0, but due to degeneracy, it is safest to check all for(i = 0; i < n; ++i){ if(LeftTurn(&P[2*i],&P[2*((i+1)%n)],&Q[2*j]) < 0){ flag = 0; j = m+1; break; } } } if(flag){ // Q in P if(*ni+m >= nPi){ Pi_full = 1; } if(!Pi_full){ for(i = 0; i < m; ++i){ Pi[2*(*ni)+0] = Q[2*i+0]; Pi[2*(*ni)+1] = Q[2*i+1]; (*ni)++; } return 2; } } } } if(Pi_full){ return -10; }else{ return 0; } }