[postgis-tickets] r16618 - Fix infinite loop in linearization of a big radius small arc

Sandro Santilli strk at kbt.io
Fri Jun 15 03:16:07 PDT 2018


Author: strk
Date: 2018-06-15 15:16:06 -0700 (Fri, 15 Jun 2018)
New Revision: 16618

Modified:
   trunk/liblwgeom/cunit/cu_lwstroke.c
   trunk/liblwgeom/lwstroke.c
Log:
Fix infinite loop in linearization of a big radius small arc

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

Modified: trunk/liblwgeom/cunit/cu_lwstroke.c
===================================================================
--- trunk/liblwgeom/cunit/cu_lwstroke.c	2018-06-13 14:53:37 UTC (rev 16617)
+++ trunk/liblwgeom/cunit/cu_lwstroke.c	2018-06-15 22:16:06 UTC (rev 16618)
@@ -237,6 +237,39 @@
 
 	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

Modified: trunk/liblwgeom/lwstroke.c
===================================================================
--- trunk/liblwgeom/lwstroke.c	2018-06-13 14:53:37 UTC (rev 16617)
+++ trunk/liblwgeom/lwstroke.c	2018-06-15 22:16:06 UTC (rev 16618)
@@ -155,8 +155,13 @@
 
 	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);
 
+	LWDEBUGF(2, " p2 side is %d", p2_side);
+
 	/* Force counterclockwise scan if SYMMETRIC operation is requsested */
 	if ( p2_side == -1 && flags & LW_LINEARIZE_FLAG_SYMMETRIC )
 	{
@@ -233,7 +238,7 @@
 		 *   angle = acos( 1 - tol/radius )
 		 *
 		 * 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
 		 * sagitta bigger than twice the radius!
 		 *
@@ -245,7 +250,15 @@
 			LWDEBUGF(2, "lwarc_linearize: tolerance %g is too big, "
 			            "using arc-max 2 * radius == %g", tol, maxErr);
 		}
-		halfAngle = acos( 1.0 - maxErr / radius );
+		do {
+			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;
 		LWDEBUGF(2, "lwarc_linearize: maxDiff:%g, radius:%g, halfAngle:%g, increment:%g (%g degrees)", tol, radius, halfAngle, increment, increment*180/M_PI);
 	}}



More information about the postgis-tickets mailing list