/* ** Test left/right side. */ static void test_lw_segment_side(void) { int rv = 0; POINT2D p1, p2, q; /* Vertical line at x=0 */ p1.x = 0.0; p1.y = 0.0; p2.x = 0.0; p2.y = 1.0; /* On the left */ q.x = -2.0; q.y = 1.5; rv = lw_segment_side(&p1, &p2, &q); //printf("left %g\n",rv); CU_ASSERT(rv < 0); /* On the right */ q.x = 2.0; rv = lw_segment_side(&p1, &p2, &q); //printf("right %g\n",rv); CU_ASSERT(rv > 0); /* On the line */ q.x = 0.0; rv = lw_segment_side(&p1, &p2, &q); //printf("on line %g\n",rv); CU_ASSERT_EQUAL(rv, 0); }
/** * Returns LW_TRUE if b is on the arc formed by a1/a2/a3, but not within * that portion already described by a1/a2/a3 */ static int pt_continues_arc(const POINT4D *a1, const POINT4D *a2, const POINT4D *a3, const POINT4D *b) { POINT4D center; POINT4D *centerptr=¢er; double radius = lwcircle_center(a1, a2, a3, ¢er); double b_distance, diff; /* Co-linear a1/a2/a3 */ if ( radius < 0.0 ) return LW_FALSE; b_distance = distance2d_pt_pt((POINT2D*)b, (POINT2D*)centerptr); diff = fabs(radius - b_distance); LWDEBUGF(4, "circle_radius=%g, b_distance=%g, diff=%g, percentage=%g", radius, b_distance, diff, diff/radius); /* Is the point b on the circle? */ if ( diff < EPSILON_SQLMM ) { int a2_side = signum(lw_segment_side((POINT2D*)a1, (POINT2D*)a3, (POINT2D*)a2)); int b_side = signum(lw_segment_side((POINT2D*)a1, (POINT2D*)a3, (POINT2D*)b)); /* Is the point b on the same side of a1/a3 as the mid-point a2 is? */ /* If not, it's in the unbounded part of the circle, so it continues the arc, return true. */ if ( b_side != a2_side ) return LW_TRUE; } return LW_FALSE; }
/** * Returns the length of a circular arc segment */ double lw_arc_length(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3) { POINT2D C; double radius_A, circumference_A; int a2_side, clockwise; double a1, a3; double angle; if ( lw_arc_is_pt(A1, A2, A3) ) return 0.0; radius_A = lw_arc_center(A1, A2, A3, &C); /* Co-linear! Return linear distance! */ if ( radius_A < 0 ) { double dx = A1->x - A3->x; double dy = A1->y - A3->y; return sqrt(dx*dx + dy*dy); } /* Closed circle! Return the circumference! */ circumference_A = M_PI * 2 * radius_A; if ( p2d_same(A1, A3) ) return circumference_A; /* Determine the orientation of the arc */ a2_side = lw_segment_side(A1, A3, A2); /* The side of the A1/A3 line that A2 falls on dictates the sweep direction from A1 to A3. */ if ( a2_side == -1 ) clockwise = LW_TRUE; else clockwise = LW_FALSE; /* Angles of each point that defines the arc section */ a1 = atan2(A1->y - C.y, A1->x - C.x); a3 = atan2(A3->y - C.y, A3->x - C.x); /* What's the sweep from A1 to A3? */ if ( clockwise ) { if ( a1 > a3 ) angle = a1 - a3; else angle = 2*M_PI + a1 - a3; } else { if ( a3 > a1 ) angle = a3 - a1; else angle = 2*M_PI + a3 - a1; } /* Length as proportion of circumference */ return circumference_A * (angle / (2*M_PI)); }
/** * Returns LW_TRUE if b is on the arc formed by a1/a2/a3, but not within * that portion already described by a1/a2/a3 */ static int pt_continues_arc(const POINT4D *a1, const POINT4D *a2, const POINT4D *a3, const POINT4D *b) { POINT2D center; POINT2D *t1 = (POINT2D*)a1; POINT2D *t2 = (POINT2D*)a2; POINT2D *t3 = (POINT2D*)a3; POINT2D *tb = (POINT2D*)b; double radius = lw_arc_center(t1, t2, t3, ¢er); double b_distance, diff; /* Co-linear a1/a2/a3 */ if ( radius < 0.0 ) return LW_FALSE; b_distance = distance2d_pt_pt(tb, ¢er); diff = fabs(radius - b_distance); LWDEBUGF(4, "circle_radius=%g, b_distance=%g, diff=%g, percentage=%g", radius, b_distance, diff, diff/radius); /* Is the point b on the circle? */ if ( diff < EPSILON_SQLMM ) { int a2_side = lw_segment_side(t1, t3, t2); int b_side = lw_segment_side(t1, t3, tb); double angle1 = lw_arc_angle(t1, t2, t3); double angle2 = lw_arc_angle(t2, t3, tb); /* Is the angle similar to the previous one ? */ diff = fabs(angle1 - angle2); LWDEBUGF(4, " angle1: %g, angle2: %g, diff:%g", angle1, angle2, diff); if ( diff > EPSILON_SQLMM ) { return LW_FALSE; } /* Is the point b on the same side of a1/a3 as the mid-point a2 is? */ /* If not, it's in the unbounded part of the circle, so it continues the arc, return true. */ if ( b_side != a2_side ) return LW_TRUE; } return LW_FALSE; }
int lw_arc_side(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, const POINT2D *Q) { POINT2D C; double radius_A; double side_Q, side_A2; double d; side_Q = lw_segment_side(A1, A3, Q); radius_A = lw_arc_center(A1, A2, A3, &C); side_A2 = lw_segment_side(A1, A3, A2); /* Linear case */ if ( radius_A < 0 ) return side_Q; d = distance2d_pt_pt(Q, &C); /* Q is on the arc boundary */ if ( d == radius_A && side_Q == side_A2 ) { return 0; } /* Q on A1-A3 line, so its on opposite side to A2 */ if ( side_Q == 0 ) { return -1 * side_A2; } /* * Q is inside the arc boundary, so it's not on the side we * might think from examining only the end points */ if ( d < radius_A && side_Q == side_A2 ) { side_Q *= -1; } return side_Q; }
int lw_arc_calculate_gbox_cartesian_2d(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, GBOX *gbox) { POINT2D xmin, ymin, xmax, ymax; POINT2D C; int A2_side; double radius_A; LWDEBUG(2, "lw_arc_calculate_gbox_cartesian_2d called."); radius_A = lw_arc_center(A1, A2, A3, &C); /* Negative radius signals straight line, p1/p2/p3 are colinear */ if (radius_A < 0.0) { gbox->xmin = FP_MIN(A1->x, A3->x); gbox->ymin = FP_MIN(A1->y, A3->y); gbox->xmax = FP_MAX(A1->x, A3->x); gbox->ymax = FP_MAX(A1->y, A3->y); return LW_SUCCESS; } /* Matched start/end points imply circle */ if ( A1->x == A3->x && A1->y == A3->y ) { gbox->xmin = C.x - radius_A; gbox->ymin = C.y - radius_A; gbox->xmax = C.x + radius_A; gbox->ymax = C.y + radius_A; return LW_SUCCESS; } /* First approximation, bounds of start/end points */ gbox->xmin = FP_MIN(A1->x, A3->x); gbox->ymin = FP_MIN(A1->y, A3->y); gbox->xmax = FP_MAX(A1->x, A3->x); gbox->ymax = FP_MAX(A1->y, A3->y); /* Create points for the possible extrema */ xmin.x = C.x - radius_A; xmin.y = C.y; ymin.x = C.x; ymin.y = C.y - radius_A; xmax.x = C.x + radius_A; xmax.y = C.y; ymax.x = C.x; ymax.y = C.y + radius_A; /* Divide the circle into two parts, one on each side of a line joining p1 and p3. The circle extrema on the same side of that line as p2 is on, are also the extrema of the bbox. */ A2_side = lw_segment_side(A1, A3, A2); if ( A2_side == lw_segment_side(A1, A3, &xmin) ) gbox->xmin = xmin.x; if ( A2_side == lw_segment_side(A1, A3, &ymin) ) gbox->ymin = ymin.y; if ( A2_side == lw_segment_side(A1, A3, &xmax) ) gbox->xmax = xmax.x; if ( A2_side == lw_segment_side(A1, A3, &ymax) ) gbox->ymax = ymax.y; return LW_SUCCESS; }
static POINTARRAY * lwcircle_segmentize(POINT4D *p1, POINT4D *p2, POINT4D *p3, uint32_t perQuad) { POINT4D center; POINT4D pt; int p2_side = 0; int clockwise = LW_TRUE; double radius; /* Arc radius */ double increment; /* Angle per segment */ double a1, a2, a3, angle; POINTARRAY *pa; int result; int is_circle = LW_FALSE; LWDEBUG(2, "lwcircle_calculate_gbox called."); radius = lwcircle_center(p1, p2, p3, ¢er); p2_side = signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, (POINT2D*)p2)); /* Matched start/end points imply circle */ if ( p1->x == p3->x && p1->y == p3->y ) is_circle = LW_TRUE; /* Negative radius signals straight line, p1/p2/p3 are colinear */ if ( radius < 0.0 || p2_side == 0 ) return NULL; /* The side of the p1/p3 line that p2 falls on dictates the sweep direction from p1 to p3. */ if ( p2_side == -1 ) clockwise = LW_TRUE; else clockwise = LW_FALSE; increment = fabs(M_PI_2 / perQuad); /* Angles of each point that defines the arc section */ a1 = atan2(p1->y - center.y, p1->x - center.x); a2 = atan2(p2->y - center.y, p2->x - center.x); a3 = atan2(p3->y - center.y, p3->x - center.x); /* p2 on left side => clockwise sweep */ if ( clockwise ) { increment *= -1; /* Adjust a3 down so we can decrement from a1 to a3 cleanly */ if ( a3 > a1 ) a3 -= 2.0 * M_PI; if ( a2 > a1 ) a2 -= 2.0 * M_PI; } /* p2 on right side => counter-clockwise sweep */ else { /* Adjust a3 up so we can increment from a1 to a3 cleanly */ if ( a3 < a1 ) a3 += 2.0 * M_PI; if ( a2 < a1 ) a2 += 2.0 * M_PI; } /* Override angles for circle case */ if( is_circle ) { a3 = a1 + 2.0 * M_PI; a2 = a1 + M_PI; increment = fabs(increment); clockwise = LW_FALSE; } /* Initialize point array */ pa = ptarray_construct_empty(1, 1, 32); /* Sweep from a1 to a3 */ for ( angle = a1; clockwise ? angle > a3 : angle < a3; angle += increment ) { pt.x = center.x + radius * cos(angle); pt.y = center.y + radius * sin(angle); pt.z = interpolate_arc(angle, a1, a2, a3, p1->z, p2->z, p3->z); pt.m = interpolate_arc(angle, a1, a2, a3, p1->m, p2->m, p3->m); result = ptarray_append_point(pa, &pt, LW_FALSE); } return pa; }
/* static int lwcircle_calculate_gbox_cartesian(const POINT4D *p1, const POINT4D *p2, const POINT4D *p3, GBOX *gbox) */ BOX3D * lwcircle_compute_box3d(POINT4D *p1, POINT4D *p2, POINT4D *p3) { POINT2D xmin, ymin, xmax, ymax; POINT4D *center = NULL; int p2_side = 0; double radius = 0.0; LWDEBUG(2, "lwcircle_compute_box3d called."); radius = lwcircle_center(p1, p2, p3, ¢er); BOX3D *box = lwalloc(sizeof(BOX3D)); /* Negative radius signals straight line, p1/p2/p3 are colinear */ if (radius < 0.0) { if ( center ) lwfree(center); box->xmin = FP_MIN(p1->x, p3->x); box->ymin = FP_MIN(p1->y, p3->y); box->zmin = FP_MIN(p1->z, p3->z); box->xmax = FP_MAX(p1->x, p3->x); box->ymax = FP_MAX(p1->y, p3->y); box->zmax = FP_MAX(p1->z, p3->z); return box; } /* Matched start/end points imply circle */ if ( p1->x == p3->x && p1->y == p3->y ) { box->xmin = center->x - radius; box->ymin = center->y - radius; box->zmin = FP_MIN(p1->z,p2->z); box->xmax = center->x + radius; box->ymax = center->y + radius; box->zmax = FP_MAX(p1->z,p2->z); lwfree(center); return box; } /* First approximation, bounds of start/end points */ box->xmin = FP_MIN(p1->x, p3->x); box->ymin = FP_MIN(p1->y, p3->y); box->zmin = FP_MIN(p1->z, p3->z); box->xmax = FP_MAX(p1->x, p3->x); box->ymax = FP_MAX(p1->y, p3->y); box->zmax = FP_MAX(p1->z, p3->z); /* Create points for the possible extrema */ xmin.x = center->x - radius; xmin.y = center->y; ymin.x = center->x; ymin.y = center->y - radius; xmax.x = center->x + radius; xmax.y = center->y; ymax.x = center->x; ymax.y = center->y + radius; /* Divide the circle into two parts, one on each side of a line joining p1 and p3. The circle extrema on the same side of that line as p2 is on, are also the extrema of the bbox. */ p2_side = signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, (POINT2D*)p2)); if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &xmin)) ) box->xmin = xmin.x; if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &ymin)) ) box->ymin = ymin.y; if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &xmax)) ) box->xmax = xmax.x; if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &ymax)) ) box->ymax = ymax.y; lwfree(center); return box; }
int ptarray_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number) { int wn = 0; int i; double side; const POINT2D *seg1; const POINT2D *seg2; double ymin, ymax; seg1 = getPoint2d_cp(pa, 0); seg2 = getPoint2d_cp(pa, pa->npoints-1); if ( check_closed && ! p2d_same(seg1, seg2) ) lwerror("ptarray_contains_point called on unclosed ring"); for ( i=1; i < pa->npoints; i++ ) { seg2 = getPoint2d_cp(pa, i); /* Zero length segments are ignored. */ if ( seg1->x == seg2->x && seg1->y == seg2->y ) { seg1 = seg2; continue; } ymin = FP_MIN(seg1->y, seg2->y); ymax = FP_MAX(seg1->y, seg2->y); /* Only test segments in our vertical range */ if ( pt->y > ymax || pt->y < ymin ) { seg1 = seg2; continue; } side = lw_segment_side(seg1, seg2, pt); /* * A point on the boundary of a ring is not contained. * WAS: if (fabs(side) < 1e-12), see #852 */ if ( (side == 0) && lw_pt_in_seg(pt, seg1, seg2) ) { return LW_BOUNDARY; } /* * If the point is to the left of the line, and it's rising, * then the line is to the right of the point and * circling counter-clockwise, so incremement. */ if ( (side < 0) && (seg1->y <= pt->y) && (pt->y < seg2->y) ) { wn++; } /* * If the point is to the right of the line, and it's falling, * then the line is to the right of the point and circling * clockwise, so decrement. */ else if ( (side > 0) && (seg2->y <= pt->y) && (pt->y < seg1->y) ) { wn--; } seg1 = seg2; } /* Sent out the winding number for calls that are building on this as a primitive */ if ( winding_number ) *winding_number = wn; /* Outside */ if (wn == 0) { return LW_OUTSIDE; } /* Inside */ return LW_INSIDE; }
/** ** @brief returns the kind of #CG_SEGMENT_INTERSECTION_TYPE behavior of lineseg 1 (constructed from p1 and p2) and lineseg 2 (constructed from q1 and q2) ** @param p1 start point of first straight linesegment ** @param p2 end point of first straight linesegment ** @param q1 start point of second line segment ** @param q2 end point of second line segment ** @return a #CG_SEGMENT_INTERSECTION_TYPE ** Returns one of ** SEG_ERROR = -1, ** SEG_NO_INTERSECTION = 0, ** SEG_COLINEAR = 1, ** SEG_CROSS_LEFT = 2, ** SEG_CROSS_RIGHT = 3, */ int lw_segment_intersects(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2) { double pq1, pq2, qp1, qp2; /* No envelope interaction => we are done. */ if (!lw_segment_envelope_intersects(p1, p2, q1, p2)) { return SEG_NO_INTERSECTION; } /* Are the start and end points of q on the same side of p? */ pq1=lw_segment_side(p1,p2,q1); pq2=lw_segment_side(p1,p2,q2); if ((pq1>0 && pq2>0) || (pq1<0 && pq2<0)) { return SEG_NO_INTERSECTION; } /* Are the start and end points of p on the same side of q? */ qp1=lw_segment_side(q1,q2,p1); qp2=lw_segment_side(q1,q2,p2); if ( (qp1 > 0.0 && qp2 > 0.0) || (qp1 < 0.0 && qp2 < 0.0) ) { return SEG_NO_INTERSECTION; } /* Nobody is on one side or another? Must be colinear. */ if ( pq1 == 0.0 && pq2 == 0.0 && qp1 == 0.0 && qp2 == 0.0 ) { return SEG_COLINEAR; } /* ** When one end-point touches, the sidedness is determined by the ** location of the other end-point. Only touches by the first point ** will be considered "real" to avoid double counting. */ LWDEBUGF(4, "pq1=%.15g pq2=%.15g", pq1, pq2); LWDEBUGF(4, "qp1=%.15g qp2=%.15g", qp1, qp2); /* Second point of p or q touches, it's not a crossing. */ if ( pq2 == 0.0 || qp2 == 0.0 ) { return SEG_NO_INTERSECTION; } /* First point of p touches, it's a "crossing". */ if ( pq1 == 0.0 ) { if ( FP_GT(pq2,0.0) ) return SEG_CROSS_RIGHT; else return SEG_CROSS_LEFT; } /* First point of q touches, it's a crossing. */ if ( qp1 == 0.0 ) { if ( FP_LT(pq1,pq2) ) return SEG_CROSS_RIGHT; else return SEG_CROSS_LEFT; } /* The segments cross, what direction is the crossing? */ if ( FP_LT(pq1,pq2) ) return SEG_CROSS_RIGHT; else return SEG_CROSS_LEFT; /* This should never happen! */ return SEG_ERROR; }
static int lwcircle_calculate_gbox_cartesian(const POINT4D *p1, const POINT4D *p2, const POINT4D *p3, GBOX *gbox) { POINT2D xmin, ymin, xmax, ymax; POINT4D center; int p2_side; double radius; LWDEBUG(2, "lwcircle_calculate_gbox called."); radius = lwcircle_center(p1, p2, p3, ¢er); /* Negative radius signals straight line, p1/p2/p3 are colinear */ if (radius < 0.0) { gbox->xmin = FP_MIN(p1->x, p3->x); gbox->ymin = FP_MIN(p1->y, p3->y); gbox->zmin = FP_MIN(p1->z, p3->z); gbox->xmax = FP_MAX(p1->x, p3->x); gbox->ymax = FP_MAX(p1->y, p3->y); gbox->zmax = FP_MAX(p1->z, p3->z); return LW_SUCCESS; } /* Matched start/end points imply circle */ if ( p1->x == p3->x && p1->y == p3->y ) { gbox->xmin = center.x - radius; gbox->ymin = center.y - radius; gbox->zmin = FP_MIN(p1->z,p2->z); gbox->mmin = FP_MIN(p1->m,p2->m); gbox->xmax = center.x + radius; gbox->ymax = center.y + radius; gbox->zmax = FP_MAX(p1->z,p2->z); gbox->mmax = FP_MAX(p1->m,p2->m); return LW_SUCCESS; } /* First approximation, bounds of start/end points */ gbox->xmin = FP_MIN(p1->x, p3->x); gbox->ymin = FP_MIN(p1->y, p3->y); gbox->zmin = FP_MIN(p1->z, p3->z); gbox->mmin = FP_MIN(p1->m, p3->m); gbox->xmax = FP_MAX(p1->x, p3->x); gbox->ymax = FP_MAX(p1->y, p3->y); gbox->zmax = FP_MAX(p1->z, p3->z); gbox->mmax = FP_MAX(p1->m, p3->m); /* Create points for the possible extrema */ xmin.x = center.x - radius; xmin.y = center.y; ymin.x = center.x; ymin.y = center.y - radius; xmax.x = center.x + radius; xmax.y = center.y; ymax.x = center.x; ymax.y = center.y + radius; /* Divide the circle into two parts, one on each side of a line joining p1 and p3. The circle extrema on the same side of that line as p2 is on, are also the extrema of the bbox. */ p2_side = signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, (POINT2D*)p2)); if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &xmin)) ) gbox->xmin = xmin.x; if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &ymin)) ) gbox->ymin = ymin.y; if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &xmax)) ) gbox->xmax = xmax.x; if ( p2_side == signum(lw_segment_side((POINT2D*)p1, (POINT2D*)p3, &ymax)) ) gbox->ymax = ymax.y; return LW_SUCCESS; }
/** * Returns true if P is on the same side of the plane partition * defined by A1/A3 as A2 is. Only makes sense if P has already been * determined to be on the circle defined by A1/A2/A3. */ int lw_pt_in_arc(const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3) { return lw_segment_side(A1, A3, A2) == lw_segment_side(A1, A3, P); }
LWGEOM* pta_unstroke(const POINTARRAY *points, int type, int srid) { int i = 0, j, k; POINT4D a1, a2, a3, b; POINT4D first, center; char *edges_in_arcs; int found_arc = LW_FALSE; int current_arc = 1; int num_edges; int edge_type; /* non-zero if edge is part of an arc */ int start, end; LWCOLLECTION *outcol; /* Minimum number of edges, per quadrant, required to define an arc */ const unsigned int min_quad_edges = 2; /* Die on null input */ if ( ! points ) lwerror("pta_unstroke called with null pointarray"); /* Null on empty input? */ if ( points->npoints == 0 ) return NULL; /* We can't desegmentize anything shorter than four points */ if ( points->npoints < 4 ) { /* Return a linestring here*/ lwerror("pta_unstroke needs implementation for npoints < 4"); } /* Allocate our result array of vertices that are part of arcs */ num_edges = points->npoints - 1; edges_in_arcs = lwalloc(num_edges + 1); memset(edges_in_arcs, 0, num_edges + 1); /* We make a candidate arc of the first two edges, */ /* And then see if the next edge follows it */ while( i < num_edges-2 ) { unsigned int arc_edges; double num_quadrants; double angle; found_arc = LW_FALSE; /* Make candidate arc */ getPoint4d_p(points, i , &a1); getPoint4d_p(points, i+1, &a2); getPoint4d_p(points, i+2, &a3); memcpy(&first, &a1, sizeof(POINT4D)); for( j = i+3; j < num_edges+1; j++ ) { LWDEBUGF(4, "i=%d, j=%d", i, j); getPoint4d_p(points, j, &b); /* Does this point fall on our candidate arc? */ if ( pt_continues_arc(&a1, &a2, &a3, &b) ) { /* Yes. Mark this edge and the two preceding it as arc components */ LWDEBUGF(4, "pt_continues_arc #%d", current_arc); found_arc = LW_TRUE; for ( k = j-1; k > j-4; k-- ) edges_in_arcs[k] = current_arc; } else { /* No. So we're done with this candidate arc */ LWDEBUG(4, "pt_continues_arc = false"); current_arc++; break; } memcpy(&a1, &a2, sizeof(POINT4D)); memcpy(&a2, &a3, sizeof(POINT4D)); memcpy(&a3, &b, sizeof(POINT4D)); } /* Jump past all the edges that were added to the arc */ if ( found_arc ) { /* Check if an arc was composed by enough edges to be * really considered an arc * See http://trac.osgeo.org/postgis/ticket/2420 */ arc_edges = j - 1 - i; LWDEBUGF(4, "arc defined by %d edges found", arc_edges); if ( first.x == b.x && first.y == b.y ) { LWDEBUG(4, "arc is a circle"); num_quadrants = 4; } else { lw_arc_center((POINT2D*)&first, (POINT2D*)&b, (POINT2D*)&a1, (POINT2D*)¢er); angle = lw_arc_angle((POINT2D*)&first, (POINT2D*)¢er, (POINT2D*)&b); int p2_side = lw_segment_side((POINT2D*)&first, (POINT2D*)&a1, (POINT2D*)&b); if ( p2_side >= 0 ) angle = -angle; if ( angle < 0 ) angle = 2 * M_PI + angle; num_quadrants = ( 4 * angle ) / ( 2 * M_PI ); LWDEBUGF(4, "arc angle (%g %g, %g %g, %g %g) is %g (side is %d), quandrants:%g", first.x, first.y, center.x, center.y, b.x, b.y, angle, p2_side, num_quadrants); } /* a1 is first point, b is last point */ if ( arc_edges < min_quad_edges * num_quadrants ) { LWDEBUGF(4, "Not enough edges for a %g quadrants arc, %g needed", num_quadrants, min_quad_edges * num_quadrants); for ( k = j-1; k >= i; k-- ) edges_in_arcs[k] = 0; } i = j-1; } else { /* Mark this edge as a linear edge */ edges_in_arcs[i] = 0; i = i+1; } } #if POSTGIS_DEBUG_LEVEL > 3 { char *edgestr = lwalloc(num_edges+1); for ( i = 0; i < num_edges; i++ ) { if ( edges_in_arcs[i] ) edgestr[i] = 48 + edges_in_arcs[i]; else edgestr[i] = '.'; } edgestr[num_edges] = 0; LWDEBUGF(3, "edge pattern %s", edgestr); lwfree(edgestr); } #endif start = 0; edge_type = edges_in_arcs[0]; outcol = lwcollection_construct_empty(COMPOUNDTYPE, srid, ptarray_has_z(points), ptarray_has_m(points)); for( i = 1; i < num_edges; i++ ) { if( edge_type != edges_in_arcs[i] ) { end = i - 1; lwcollection_add_lwgeom(outcol, geom_from_pa(points, srid, edge_type, start, end)); start = i; edge_type = edges_in_arcs[i]; } } lwfree(edges_in_arcs); /* not needed anymore */ /* Roll out last item */ end = num_edges - 1; lwcollection_add_lwgeom(outcol, geom_from_pa(points, srid, edge_type, start, end)); /* Strip down to singleton if only one entry */ if ( outcol->ngeoms == 1 ) { LWGEOM *outgeom = outcol->geoms[0]; outcol->ngeoms = 0; lwcollection_free(outcol); return outgeom; } return lwcollection_as_lwgeom(outcol); }
/** * Segmentize an arc * * Does not add the final vertex * * @param to POINTARRAY to append segmentized vertices to * @param p1 first point defining the arc * @param p2 second point defining the arc * @param p3 third point defining the arc * @param tol tolerance, semantic driven by tolerance_type * @param tolerance_type see LW_LINEARIZE_TOLERANCE_TYPE * @param flags LW_LINEARIZE_FLAGS * * @return number of points appended (0 if collinear), * or -1 on error (lwerror would be called). * */ static int lwarc_linearize(POINTARRAY *to, const POINT4D *p1, const POINT4D *p2, const POINT4D *p3, double tol, LW_LINEARIZE_TOLERANCE_TYPE tolerance_type, int flags) { POINT2D center; POINT2D *t1 = (POINT2D*)p1; POINT2D *t2 = (POINT2D*)p2; POINT2D *t3 = (POINT2D*)p3; POINT4D pt; int p2_side = 0; int clockwise = LW_TRUE; double radius; /* Arc radius */ double increment; /* Angle per segment */ double angle_shift = 0; double a1, a2, a3, angle; POINTARRAY *pa = to; int is_circle = LW_FALSE; int points_added = 0; int reverse = 0; LWDEBUG(2, "lwarc_linearize called."); p2_side = lw_segment_side(t1, t3, t2); /* Force counterclockwise scan if SYMMETRIC operation is requsested */ if ( p2_side == -1 && flags & LW_LINEARIZE_FLAG_SYMMETRIC ) { /* swap p1-p3 */ t1 = (POINT2D*)p3; t3 = (POINT2D*)p1; p1 = (POINT4D*)t1; p3 = (POINT4D*)t3; p2_side = 1; reverse = 1; } radius = lw_arc_center(t1, t2, t3, ¢er); LWDEBUGF(2, " center is POINT(%.15g %.15g) - radius:%g", center.x, center.y, radius); /* Matched start/end points imply circle */ if ( p1->x == p3->x && p1->y == p3->y ) is_circle = LW_TRUE; /* Negative radius signals straight line, p1/p2/p3 are colinear */ if ( (radius < 0.0 || p2_side == 0) && ! is_circle ) return 0; /* The side of the p1/p3 line that p2 falls on dictates the sweep direction from p1 to p3. */ if ( p2_side == -1 ) clockwise = LW_TRUE; else clockwise = LW_FALSE; if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD ) {{ int perQuad = rint(tol); // error out if tol != perQuad ? (not-round) if ( perQuad != tol ) { lwerror("lwarc_linearize: segments per quadrant must be an integer value, got %.15g", tol, perQuad); return -1; } if ( perQuad < 1 ) { lwerror("lwarc_linearize: segments per quadrant must be at least 1, got %d", perQuad); return -1; } increment = fabs(M_PI_2 / perQuad); LWDEBUGF(2, "lwarc_linearize: perQuad:%d, increment:%g (%g degrees)", perQuad, increment, increment*180/M_PI); }} else if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION ) {{ double halfAngle; if ( tol <= 0 ) { lwerror("lwarc_linearize: max deviation must be bigger than 0, got %.15g", tol); return -1; } halfAngle = acos( -tol / radius + 1 ); increment = 2 * halfAngle; LWDEBUGF(2, "lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g (%g degrees)", tol, radius, halfAngle, increment, increment*180/M_PI); }} else if ( tolerance_type == LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE ) { increment = tol; if ( increment <= 0 ) { lwerror("lwarc_linearize: max angle must be bigger than 0, got %.15g", tol); return -1; } } else { lwerror("lwarc_linearize: unsupported tolerance type %d", tolerance_type); return LW_FALSE; } /* Angles of each point that defines the arc section */ a1 = atan2(p1->y - center.y, p1->x - center.x); a2 = atan2(p2->y - center.y, p2->x - center.x); a3 = atan2(p3->y - center.y, p3->x - center.x); LWDEBUGF(2, "lwarc_linearize A1:%g (%g) A2:%g (%g) A3:%g (%g)", a1, a1*180/M_PI, a2, a2*180/M_PI, a3, a3*180/M_PI); if ( flags & LW_LINEARIZE_FLAG_SYMMETRIC ) {{ /* Calculate total arc angle, in radians */ double angle = clockwise ? a1 - a3 : a3 - a1; if ( angle < 0 ) angle += M_PI * 2; LWDEBUGF(2, "lwarc_linearize SYMMETRIC requested - total angle %g deg", angle * 180 / M_PI); if ( flags & LW_LINEARIZE_FLAG_RETAIN_ANGLE ) {{ /* Number of steps */ int steps = trunc(angle / increment); /* Angle reminder */ double angle_reminder = angle - ( increment * steps ); angle_shift = angle_reminder / 2.0; LWDEBUGF(2, "lwarc_linearize RETAIN_ANGLE operation requested - " "total angle %g, steps %d, increment %g, reminder %g", angle * 180 / M_PI, steps, increment * 180 / M_PI, angle_reminder * 180 / M_PI); }} else {{ /* Number of segments in output */ int segs = ceil(angle / increment); /* Tweak increment to be regular for all the arc */ increment = angle/segs; LWDEBUGF(2, "lwarc_linearize SYMMETRIC operation requested - " "total angle %g degrees - LINESTRING(%g %g,%g %g,%g %g) - S:%d - I:%g", angle*180/M_PI, p1->x, p1->y, center.x, center.y, p3->x, p3->y, segs, increment*180/M_PI); }} }} /* p2 on left side => clockwise sweep */ if ( clockwise ) { LWDEBUG(2, " Clockwise sweep"); increment *= -1; angle_shift *= -1; /* Adjust a3 down so we can decrement from a1 to a3 cleanly */ if ( a3 > a1 ) a3 -= 2.0 * M_PI; if ( a2 > a1 ) a2 -= 2.0 * M_PI; } /* p2 on right side => counter-clockwise sweep */ else { LWDEBUG(2, " Counterclockwise sweep"); /* Adjust a3 up so we can increment from a1 to a3 cleanly */ if ( a3 < a1 ) a3 += 2.0 * M_PI; if ( a2 < a1 ) a2 += 2.0 * M_PI; } /* Override angles for circle case */ if( is_circle ) { a3 = a1 + 2.0 * M_PI; a2 = a1 + M_PI; increment = fabs(increment); clockwise = LW_FALSE; } LWDEBUGF(2, "lwarc_linearize angle_shift:%g, increment:%g", angle_shift * 180/M_PI, increment * 180/M_PI); if ( reverse ) {{ const int capacity = 8; /* TODO: compute exactly ? */ pa = ptarray_construct_empty(ptarray_has_z(to), ptarray_has_m(to), capacity); }} /* Sweep from a1 to a3 */ if ( ! reverse ) { ptarray_append_point(pa, p1, LW_FALSE); } ++points_added; if ( angle_shift ) angle_shift -= increment; LWDEBUGF(2, "a1:%g (%g deg), a3:%g (%g deg), inc:%g, shi:%g, cw:%d", a1, a1 * 180 / M_PI, a3, a3 * 180 / M_PI, increment, angle_shift, clockwise); for ( angle = a1 + increment + angle_shift; clockwise ? angle > a3 : angle < a3; angle += increment ) { LWDEBUGF(2, " SA: %g ( %g deg )", angle, angle*180/M_PI); pt.x = center.x + radius * cos(angle); pt.y = center.y + radius * sin(angle); pt.z = interpolate_arc(angle, a1, a2, a3, p1->z, p2->z, p3->z); pt.m = interpolate_arc(angle, a1, a2, a3, p1->m, p2->m, p3->m); ptarray_append_point(pa, &pt, LW_FALSE); ++points_added; angle_shift = 0; } if ( reverse ) {{ int i; ptarray_append_point(to, p3, LW_FALSE); for ( i=pa->npoints; i>0; i-- ) { getPoint4d_p(pa, i-1, &pt); ptarray_append_point(to, &pt, LW_FALSE); } ptarray_free(pa); }} return points_added; }