Segmentize an arc.
149 double angle_shift = 0;
150 double a1, a2, a3, angle;
153 int points_added = 0;
156 LWDEBUG(2,
"lwarc_linearize called.");
158 LWDEBUGF(2,
" curve is CIRCULARSTRING(%.15g %.15f, %.15f %.15f, %.15f %15f)",
159 t1->
x, t1->
y, t2->
x, t2->
y, t3->
x, t3->
y);
163 LWDEBUGF(2,
" p2 side is %d", p2_side);
178 LWDEBUGF(2,
" center is POINT(%.15g %.15g) - radius:%g", center.
x, center.
y, radius);
181 if ( p1->
x == p3->
x && p1->
y == p3->
y )
185 if ( (radius < 0.0 || p2_side == 0) && ! is_circle )
197 int perQuad = rint(tol);
199 if ( perQuad != tol )
201 lwerror(
"lwarc_linearize: segments per quadrant must be an integer value, got %.15g", tol, perQuad);
206 lwerror(
"lwarc_linearize: segments per quadrant must be at least 1, got %d", perQuad);
209 increment = fabs(M_PI_2 / perQuad);
210 LWDEBUGF(2,
"lwarc_linearize: perQuad:%d, increment:%g (%g degrees)", perQuad, increment, increment*180/M_PI);
215 double halfAngle, maxErr;
218 lwerror(
"lwarc_linearize: max deviation must be bigger than 0, got %.15g", tol);
247 if ( maxErr > radius * 2 )
250 LWDEBUGF(2,
"lwarc_linearize: tolerance %g is too big, "
251 "using arc-max 2 * radius == %g", tol, maxErr);
254 halfAngle = acos( 1.0 - maxErr / radius );
257 if ( halfAngle != 0 )
break;
258 LWDEBUGF(2,
"lwarc_linearize: tolerance %g is too small for this arc"
259 " to compute approximation angle, doubling it", maxErr);
262 increment = 2 * halfAngle;
263 LWDEBUGF(2,
"lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g (%g degrees)", tol, radius, halfAngle, increment, increment*180/M_PI);
268 if ( increment <= 0 )
270 lwerror(
"lwarc_linearize: max angle must be bigger than 0, got %.15g", tol);
276 lwerror(
"lwarc_linearize: unsupported tolerance type %d", tolerance_type);
281 a1 = atan2(p1->
y - center.
y, p1->
x - center.
x);
282 a2 = atan2(p2->
y - center.
y, p2->
x - center.
x);
283 a3 = atan2(p3->
y - center.
y, p3->
x - center.
x);
285 LWDEBUGF(2,
"lwarc_linearize A1:%g (%g) A2:%g (%g) A3:%g (%g)",
286 a1, a1*180/M_PI, a2, a2*180/M_PI, a3, a3*180/M_PI);
291 double angle = clockwise ? a1 - a3 : a3 - a1;
292 if ( angle < 0 ) angle += M_PI * 2;
293 LWDEBUGF(2,
"lwarc_linearize SYMMETRIC requested - total angle %g deg",
298 int steps = trunc(angle / increment);
300 double angle_reminder = angle - ( increment * steps );
301 angle_shift = angle_reminder / 2.0;
303 LWDEBUGF(2,
"lwarc_linearize RETAIN_ANGLE operation requested - "
304 "total angle %g, steps %d, increment %g, reminder %g",
305 angle * 180 / M_PI, steps, increment * 180 / M_PI,
306 angle_reminder * 180 / M_PI);
311 int segs = ceil(angle / increment);
313 increment = angle/segs;
315 LWDEBUGF(2,
"lwarc_linearize SYMMETRIC operation requested - "
316 "total angle %g degrees - LINESTRING(%g %g,%g %g,%g %g) - S:%d - I:%g",
317 angle*180/M_PI, p1->
x, p1->
y, center.
x, center.
y, p3->
x, p3->
y,
318 segs, increment*180/M_PI);
325 LWDEBUG(2,
" Clockwise sweep");
337 LWDEBUG(2,
" Counterclockwise sweep");
348 increment = fabs(increment);
349 a3 = a1 + 2.0 * M_PI;
354 LWDEBUGF(2,
"lwarc_linearize angle_shift:%g, increment:%g",
355 angle_shift * 180/M_PI, increment * 180/M_PI);
358 const int capacity = 8;
368 if ( angle_shift ) angle_shift -= increment;
369 LWDEBUGF(2,
"a1:%g (%g deg), a3:%g (%g deg), inc:%g, shi:%g, cw:%d",
370 a1, a1 * 180 / M_PI, a3, a3 * 180 / M_PI, increment, angle_shift, clockwise);
371 for ( angle = a1 + increment + angle_shift; clockwise ? angle > a3 : angle < a3; angle += increment )
373 LWDEBUGF(2,
" SA: %g ( %g deg )", angle, angle*180/M_PI);
374 pt.
x = center.
x + radius * cos(angle);
375 pt.
y = center.
y + radius * sin(angle);
394 for ( i=pa->
npoints; i>0; i-- ) {
int ptarray_remove_point(POINTARRAY *pa, uint32_t where)
Remove a point from an existing POINTARRAY.
@ LW_LINEARIZE_TOLERANCE_TYPE_MAX_ANGLE
Tolerance expresses the maximum angle between the radii generating approximation line vertices,...
@ LW_LINEARIZE_TOLERANCE_TYPE_SEGS_PER_QUAD
Tolerance expresses the number of segments to use for each quarter of circle (quadrant).
@ LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION
Tolerance expresses the maximum distance between an arbitrary point on the curve and the closest poin...
@ LW_LINEARIZE_FLAG_SYMMETRIC
Symmetric linearization means that the output vertices would be the same no matter the order of the p...
@ LW_LINEARIZE_FLAG_RETAIN_ANGLE
Retain angle instructs the engine to try its best to retain the requested angle between generating ra...
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
void ptarray_free(POINTARRAY *pa)
int ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int allow_duplicates)
Append a point to the end of an existing POINTARRAY If allow_duplicate is LW_FALSE,...
#define LW_TRUE
Return types for functions with status returns.
double lw_arc_center(const POINT2D *p1, const POINT2D *p2, const POINT2D *p3, POINT2D *result)
Determines the center of the circle defined by the three given points.
int ptarray_has_z(const POINTARRAY *pa)
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
int ptarray_has_m(const POINTARRAY *pa)
#define LWDEBUG(level, msg)
#define LWDEBUGF(level, msg,...)
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
static double interpolate_arc(double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3)