Commit 6415fb5f authored by Sandro Santilli's avatar Sandro Santilli

Fix infinite loop in linearization of a big radius small arc

Ref #4058 for trunk (2.5.0dev)
Includes unit test

git-svn-id: http://svn.osgeo.org/postgis/trunk@16618 b70326c6-7e19-0410-871a-916f4a2858ee
parent 014264f9
Pipeline #23965879 passed with stage
in 16 minutes and 15 seconds
...@@ -237,6 +237,39 @@ static void test_lwcurve_linearize(void) ...@@ -237,6 +237,39 @@ static void test_lwcurve_linearize(void)
lwgeom_free(in); lwgeom_free(in);
/*
* ROBUSTNESS: big radius, small tolerance
* See https://trac.osgeo.org/postgis/ticket/4058
* NOTE: we are really only interested in not enterying
* an infinite loop here
*/
toltype = LW_LINEARIZE_TOLERANCE_TYPE_MAX_DEVIATION;
in = lwgeom_from_text("CIRCULARSTRING("
"2696000.553 1125699.831999999936670, "
"2695950.552000000141561 1125749.833000000100583, "
"2695865.195999999996275 1125835.189000)");
out = lwcurve_linearize(in, 0.0001, toltype, 0);
str = lwgeom_to_text(out, 2);
ASSERT_STRING_EQUAL(str, "LINESTRING(2696000 1125700,2695866 1125836)");
lwfree(str);
lwgeom_free(out);
out = lwcurve_linearize(in, 0.0001, toltype, LW_LINEARIZE_FLAG_SYMMETRIC);
str = lwgeom_to_text(out, 2);
ASSERT_STRING_EQUAL(str, "LINESTRING(2696000 1125700,2695866 1125836)");
lwfree(str);
lwgeom_free(out);
#ifndef SKIP_TEST_RETAIN_ANGLE
out = lwcurve_linearize(in, 0.0001, toltype,
LW_LINEARIZE_FLAG_SYMMETRIC |
LW_LINEARIZE_FLAG_RETAIN_ANGLE
);
str = lwgeom_to_text(out, 2);
ASSERT_STRING_EQUAL(str, "LINESTRING(2696000 1125700,2695932 1125768,2695866 1125836)");
lwfree(str);
lwgeom_free(out);
#endif /* SKIP_TEST_RETAIN_ANGLE */
lwgeom_free(in);
/*********************************************************** /***********************************************************
* *
* Maximum angle tolerance type * Maximum angle tolerance type
......
...@@ -155,8 +155,13 @@ lwarc_linearize(POINTARRAY *to, ...@@ -155,8 +155,13 @@ lwarc_linearize(POINTARRAY *to,
LWDEBUG(2, "lwarc_linearize called."); LWDEBUG(2, "lwarc_linearize called.");
LWDEBUGF(2, " curve is CIRCULARSTRING(%.15g %.15f, %.15f %.15f, %.15f %15f)",
t1->x, t1->y, t2->x, t2->y, t3->x, t3->y);
p2_side = lw_segment_side(t1, t3, t2); p2_side = lw_segment_side(t1, t3, t2);
LWDEBUGF(2, " p2 side is %d", p2_side);
/* Force counterclockwise scan if SYMMETRIC operation is requsested */ /* Force counterclockwise scan if SYMMETRIC operation is requsested */
if ( p2_side == -1 && flags & LW_LINEARIZE_FLAG_SYMMETRIC ) if ( p2_side == -1 && flags & LW_LINEARIZE_FLAG_SYMMETRIC )
{ {
...@@ -233,7 +238,7 @@ lwarc_linearize(POINTARRAY *to, ...@@ -233,7 +238,7 @@ lwarc_linearize(POINTARRAY *to,
* angle = acos( 1 - tol/radius ) * angle = acos( 1 - tol/radius )
* *
* Constraints: 1.0 - tol/radius must be between -1 and 1 * Constraints: 1.0 - tol/radius must be between -1 and 1
* which means tol/radius must be between 0 and 2 times * which means tol must be between 0 and 2 times
* the radius, which makes sense as you cannot have a * the radius, which makes sense as you cannot have a
* sagitta bigger than twice the radius! * sagitta bigger than twice the radius!
* *
...@@ -245,7 +250,15 @@ lwarc_linearize(POINTARRAY *to, ...@@ -245,7 +250,15 @@ lwarc_linearize(POINTARRAY *to,
LWDEBUGF(2, "lwarc_linearize: tolerance %g is too big, " LWDEBUGF(2, "lwarc_linearize: tolerance %g is too big, "
"using arc-max 2 * radius == %g", tol, maxErr); "using arc-max 2 * radius == %g", tol, maxErr);
} }
do {
halfAngle = acos( 1.0 - maxErr / radius ); halfAngle = acos( 1.0 - maxErr / radius );
/* TODO: avoid a loop here, going rather straight to
* a minimum angle value */
if ( halfAngle != 0 ) break;
LWDEBUGF(2, "lwarc_linearize: tolerance %g is too small for this arc"
" to compute approximation angle, doubling it", maxErr);
maxErr *= 2;
} while(1);
increment = 2 * halfAngle; 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); LWDEBUGF(2, "lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g (%g degrees)", tol, radius, halfAngle, increment, increment*180/M_PI);
}} }}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment