/** * 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; }
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; }
BOX3D * lwcircle_compute_box3d(POINT4D *p1, POINT4D *p2, POINT4D *p3) { double x1, x2, y1, y2, z1, z2; double angle, radius, sweep; /* angles from center */ double a1, a2, a3; /* angles from center once a1 is rotated to zero */ double r2, r3; double xe = 0.0, ye = 0.0; POINT4D *center; int i; BOX3D *box; LWDEBUG(2, "lwcircle_compute_box3d called."); radius = lwcircle_center(p1, p2, p3, ¢er); if (radius < 0.0) { LWDEBUG(3, "lwcircle_compute_box3d: zero radius"); /* * We've got a straight line here. Look to the end points for extents. * It's worth noting that when lwcircle_center returns < 0, center hasn't been allocated. */ x1 = (FP_LT(p1->x, p3->x)) ? p1->x : p3->x; x2 = (FP_GT(p1->x, p3->x)) ? p1->x : p3->x; y1 = (FP_LT(p1->y, p3->y)) ? p1->y : p3->y; y2 = (FP_GT(p1->y, p3->y)) ? p1->y : p3->y; z1 = (FP_LT(p1->z, p2->z)) ? p1->z : p2->z; z1 = (FP_LT(z1, p3->z)) ? z1 : p3->z; z2 = (FP_GT(p1->z, p2->z)) ? p1->z : p2->z; z2 = (FP_GT(z2, p3->z)) ? z2 : p3->z; box = lwalloc(sizeof(BOX3D)); box->xmin = x1; box->xmax = x2; box->ymin = y1; box->ymax = y2; box->zmin = z1; box->zmax = z2; LWDEBUGF(3, "lwcircle_compute_box3d: extents %.16f %.16f %.16f, %.16f %.16f %.16f", x1, y1, z1, x2, y2, z2); return box; } /* top = center->y + radius; left = center->x - radius; LWDEBUGF(3, "lwcircle_compute_box3d: center (%.16f, %.16f)", center->x, center->y); */ x1 = MAXFLOAT; x2 = -1 * MAXFLOAT; y1 = MAXFLOAT; y2 = -1 * MAXFLOAT; 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); /* Rotate a2 and a3 such that a1 = 0 */ r2 = a2 - a1; r3 = a3 - a1; LWDEBUGF(4, "a1 %.16f, a2 %.16f, a3 %.16f", a1, a2, a3); LWDEBUGF(4, "r2 %.16f, r3 %.16f", r2, r3); /* * There are six cases here I'm interested in * Clockwise: * 1. a1-a2 < 180 == r2 < 0 && (r3 > 0 || r3 < r2) * 2. a1-a2 > 180 == r2 > 0 && (r3 > 0 && r3 < r2) * 3. a1-a2 > 180 == r2 > 0 && (r3 > r2 || r3 < 0) * Counter-clockwise: * 4. a1-a2 < 180 == r2 > 0 && (r3 < 0 || r3 > r2) * 5. a1-a2 > 180 == r2 < 0 && (r3 < 0 && r3 > r2) * 6. a1-a2 > 180 == r2 < 0 && (r3 < r2 || r3 > 0) * 3 and 6 are invalid cases where a3 is the midpoint. * BBOX is fundamental, so these cannot error out and will instead * calculate the sweep using a3 as the middle point. */ /* clockwise 1 */ if (FP_LT(r2, 0) && (FP_GT(r3, 0) || FP_LT(r3, r2))) { sweep = (FP_GT(r3, 0)) ? (r3 - 2 * M_PI) : r3; } /* clockwise 2 */ else if (FP_GT(r2, 0) && FP_GT(r3, 0) && FP_LT(r3, r2)) { sweep = (FP_GT(r3, 0)) ? (r3 - 2 * M_PI) : r3; } /* counter-clockwise 4 */ else if (FP_GT(r2, 0) && (FP_LT(r3, 0) || FP_GT(r3, r2))) { sweep = (FP_LT(r3, 0)) ? (r3 + 2 * M_PI) : r3; } /* counter-clockwise 5 */ else if (FP_LT(r2, 0) && FP_LT(r3, 0) && FP_GT(r3, r2)) { sweep = (FP_LT(r3, 0)) ? (r3 + 2 * M_PI) : r3; } /* clockwise invalid 3 */ else if (FP_GT(r2, 0) && (FP_GT(r3, r2) || FP_LT(r3, 0))) { sweep = (FP_GT(r2, 0)) ? (r2 - 2 * M_PI) : r2; } /* clockwise invalid 6 */ else { sweep = (FP_LT(r2, 0)) ? (r2 + 2 * M_PI) : r2; } LWDEBUGF(3, "a1 %.16f, a2 %.16f, a3 %.16f, sweep %.16f", a1, a2, a3, sweep); angle = 0.0; for (i=0; i < 6; i++) { switch (i) { /* right extent */ case 0: angle = 0.0; xe = center->x + radius; ye = center->y; break; /* top extent */ case 1: angle = M_PI_2; xe = center->x; ye = center->y + radius; break; /* left extent */ case 2: angle = M_PI; xe = center->x - radius; ye = center->y; break; /* bottom extent */ case 3: angle = -1 * M_PI_2; xe = center->x; ye = center->y - radius; break; /* first point */ case 4: angle = a1; xe = p1->x; ye = p1->y; break; /* last point */ case 5: angle = a3; xe = p3->x; ye = p3->y; break; } /* determine if the extents are outside the arc */ if (i < 4) { if (FP_GT(sweep, 0.0)) { if (FP_LT(a3, a1)) { if (FP_GT(angle, (a3 + 2 * M_PI)) || FP_LT(angle, a1)) continue; } else { if (FP_GT(angle, a3) || FP_LT(angle, a1)) continue; } } else { if (FP_GT(a3, a1)) { if (FP_LT(angle, (a3 - 2 * M_PI)) || FP_GT(angle, a1)) continue; } else { if (FP_LT(angle, a3) || FP_GT(angle, a1)) continue; } } } LWDEBUGF(3, "lwcircle_compute_box3d: potential extreame %d (%.16f, %.16f)", i, xe, ye); x1 = (FP_LT(x1, xe)) ? x1 : xe; y1 = (FP_LT(y1, ye)) ? y1 : ye; x2 = (FP_GT(x2, xe)) ? x2 : xe; y2 = (FP_GT(y2, ye)) ? y2 : ye; } LWDEBUGF(3, "lwcircle_compute_box3d: extreames found (%.16f %.16f, %.16f %.16f)", x1, y1, x2, y2); /* x1 = center->x + x1 * radius; x2 = center->x + x2 * radius; y1 = center->y + y1 * radius; y2 = center->y + y2 * radius; */ z1 = (FP_LT(p1->z, p2->z)) ? p1->z : p2->z; z1 = (FP_LT(z1, p3->z)) ? z1 : p3->z; z2 = (FP_GT(p1->z, p2->z)) ? p1->z : p2->z; z2 = (FP_GT(z2, p3->z)) ? z2 : p3->z; box = lwalloc(sizeof(BOX3D)); box->xmin = x1; box->xmax = x2; box->ymin = y1; box->ymax = y2; box->zmin = z1; box->zmax = z2; lwfree(center); return box; }
/* 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; }
static int lwcircle_calculate_gbox(POINT4D p1, POINT4D p2, POINT4D p3, GBOX *gbox) { double x1, x2, y1, y2, z1, z2, m1, m2; double angle, radius, sweep; /* angles from center */ double a1, a2, a3; /* angles from center once a1 is rotated to zero */ double r2, r3; double xe = 0.0, ye = 0.0; POINT4D *center; int i; LWDEBUG(2, "lwcircle_calculate_gbox called."); radius = lwcircle_center(&p1, &p2, &p3, ¢er); if (radius < 0.0) return G_FAILURE; x1 = MAXFLOAT; x2 = -1 * MAXFLOAT; y1 = MAXFLOAT; y2 = -1 * MAXFLOAT; 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); /* Rotate a2 and a3 such that a1 = 0 */ r2 = a2 - a1; r3 = a3 - a1; /* * There are six cases here I'm interested in * Clockwise: * 1. a1-a2 < 180 == r2 < 0 && (r3 > 0 || r3 < r2) * 2. a1-a2 > 180 == r2 > 0 && (r3 > 0 && r3 < r2) * 3. a1-a2 > 180 == r2 > 0 && (r3 > r2 || r3 < 0) * Counter-clockwise: * 4. a1-a2 < 180 == r2 > 0 && (r3 < 0 || r3 > r2) * 5. a1-a2 > 180 == r2 < 0 && (r3 < 0 && r3 > r2) * 6. a1-a2 > 180 == r2 < 0 && (r3 < r2 || r3 > 0) * 3 and 6 are invalid cases where a3 is the midpoint. * BBOX is fundamental, so these cannot error out and will instead * calculate the sweep using a3 as the middle point. */ /* clockwise 1 */ if (FP_LT(r2, 0) && (FP_GT(r3, 0) || FP_LT(r3, r2))) { sweep = (FP_GT(r3, 0)) ? (r3 - 2 * M_PI) : r3; } /* clockwise 2 */ else if (FP_GT(r2, 0) && FP_GT(r3, 0) && FP_LT(r3, r2)) { sweep = (FP_GT(r3, 0)) ? (r3 - 2 * M_PI) : r3; } /* counter-clockwise 4 */ else if (FP_GT(r2, 0) && (FP_LT(r3, 0) || FP_GT(r3, r2))) { sweep = (FP_LT(r3, 0)) ? (r3 + 2 * M_PI) : r3; } /* counter-clockwise 5 */ else if (FP_LT(r2, 0) && FP_LT(r3, 0) && FP_GT(r3, r2)) { sweep = (FP_LT(r3, 0)) ? (r3 + 2 * M_PI) : r3; } /* clockwise invalid 3 */ else if (FP_GT(r2, 0) && (FP_GT(r3, r2) || FP_LT(r3, 0))) { sweep = (FP_GT(r2, 0)) ? (r2 - 2 * M_PI) : r2; } /* clockwise invalid 6 */ else { sweep = (FP_LT(r2, 0)) ? (r2 + 2 * M_PI) : r2; } LWDEBUGF(3, "a1 %.16f, a2 %.16f, a3 %.16f, sweep %.16f", a1, a2, a3, sweep); angle = 0.0; for (i=0; i < 6; i++) { switch (i) { /* right extent */ case 0: angle = 0.0; xe = center->x + radius; ye = center->y; break; /* top extent */ case 1: angle = M_PI_2; xe = center->x; ye = center->y + radius; break; /* left extent */ case 2: angle = M_PI; xe = center->x - radius; ye = center->y; break; /* bottom extent */ case 3: angle = -1 * M_PI_2; xe = center->x; ye = center->y - radius; break; /* first point */ case 4: angle = a1; xe = p1.x; ye = p1.y; break; /* last point */ case 5: angle = a3; xe = p3.x; ye = p3.y; break; } /* determine if the extents are outside the arc */ if (i < 4) { if (FP_GT(sweep, 0.0)) { if (FP_LT(a3, a1)) { if (FP_GT(angle, (a3 + 2 * M_PI)) || FP_LT(angle, a1)) continue; } else { if (FP_GT(angle, a3) || FP_LT(angle, a1)) continue; } } else { if (FP_GT(a3, a1)) { if (FP_LT(angle, (a3 - 2 * M_PI)) || FP_GT(angle, a1)) continue; } else { if (FP_LT(angle, a3) || FP_GT(angle, a1)) continue; } } } LWDEBUGF(3, "lwcircle_calculate_gbox: potential extreame %d (%.16f, %.16f)", i, xe, ye); x1 = (FP_LT(x1, xe)) ? x1 : xe; y1 = (FP_LT(y1, ye)) ? y1 : ye; x2 = (FP_GT(x2, xe)) ? x2 : xe; y2 = (FP_GT(y2, ye)) ? y2 : ye; } LWDEBUGF(3, "lwcircle_calculate_gbox: extreames found (%.16f %.16f, %.16f %.16f)", x1, y1, x2, y2); z1 = FP_MIN(p1.z, p2.z); z1 = FP_MIN(z1, p3.z); z2 = FP_MAX(p1.z, p2.z); z2 = FP_MAX(z2, p3.z); m1 = FP_MIN(p1.m, p2.m); m1 = FP_MIN(m1, p3.m); m2 = FP_MAX(p1.m, p2.m); m2 = FP_MAX(m2, p3.m); gbox->xmin = x1; gbox->xmax = x2; gbox->ymin = y1; gbox->ymax = y2; if ( FLAGS_GET_Z(gbox->flags) ) { gbox->zmin = z1; gbox->zmax = z2; } if ( FLAGS_GET_M(gbox->flags) ) { gbox->mmin = m1; gbox->mmax = m2; } return G_SUCCESS; }
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; }