From 37237dc240087053926fa60b2263b6dda155d48f Mon Sep 17 00:00:00 2001 From: Dan Baston Date: Mon, 13 Jul 2026 10:41:17 -0400 Subject: [PATCH] CircularArcIntersector: Explicitly check for endpoint/control point equality --- src/algorithm/CircularArcIntersector.cpp | 15 +++++++++--- .../tests/general/TestOverlayCurvesAA.xml | 23 ++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/algorithm/CircularArcIntersector.cpp b/src/algorithm/CircularArcIntersector.cpp index 5f82d3230d..79ab344b9c 100644 --- a/src/algorithm/CircularArcIntersector.cpp +++ b/src/algorithm/CircularArcIntersector.cpp @@ -142,9 +142,10 @@ CircularArcIntersector::intersects(const CircularArc& arc, const CoordinateSeque return; } - // Check for exact endpoint-endpoint intersections + // Check for exact endpoint-endpoint or endpoint-control point intersections // If found, replace the computed intersection points with an exact endpoint const CoordinateXY& ap0 = arc.p0(); + const CoordinateXY& ap1 = arc.p1(); const CoordinateXY& ap2 = arc.p2(); const CoordinateXY& bp0 = seq.getAt(segPos0); const CoordinateXY& bp1 = seq.getAt(segPos1); @@ -152,6 +153,9 @@ CircularArcIntersector::intersects(const CircularArc& arc, const CoordinateSeque if (ap0 == bp0 || ap0 == bp1) { closestPoint(isect0, isect1, nPointsIntersectingLine, ap0) = ap0; } + if (ap1 == bp0 || ap1 == bp1) { + closestPoint(isect0, isect1, nPointsIntersectingLine, ap1) = ap1; + } if (ap2 == bp0 || ap2 == bp1) { closestPoint(isect0, isect1, nPointsIntersectingLine, ap2) = ap2; } @@ -251,14 +255,19 @@ CircularArcIntersector::intersects(const CircularArc& arc1, const CircularArc& a // Check to see if computed intersection points are inexact versions of an endpoint intersection const CoordinateXY& ap0 = arc1.p0(); + const CoordinateXY& ap1 = arc1.p1(); const CoordinateXY& ap2 = arc1.p2(); const CoordinateXY& bp0 = arc2.p0(); + const CoordinateXY& bp1 = arc2.p1(); const CoordinateXY& bp2 = arc2.p2(); - if (ap0 == bp0 || ap0 == bp2) { + if (ap0 == bp0 || ap0 == bp1 || ap0 == bp2) { closestPoint(isect0, isect1, 2, ap0) = ap0; } - if (ap2 == bp0 || ap2 == bp2) { + if (ap1 == bp0 || ap1 == bp1 || ap1 == bp2) { + closestPoint(isect0, isect1, 2, ap1) = ap1; + } + if (ap2 == bp0 || ap2 == bp1 || ap2 == bp2) { closestPoint(isect0, isect1, 2, ap2) = ap2; } diff --git a/tests/xmltester/tests/general/TestOverlayCurvesAA.xml b/tests/xmltester/tests/general/TestOverlayCurvesAA.xml index d53c49484b..c9a1d43a82 100644 --- a/tests/xmltester/tests/general/TestOverlayCurvesAA.xml +++ b/tests/xmltester/tests/general/TestOverlayCurvesAA.xml @@ -160,7 +160,7 @@ - CURVEPOLYGON ((0 0, 0 230, 210 230, 210 0, 0 0), COMPOUNDCURVE ((70 120, 90 90), CIRCULARSTRING (90 90, 100 120, 70 120)), CIRCULARSTRING (60 90, 60 60, 90 60, 96.21320343559643 75, 90 90, 75 96.21320343559643, 60 90), CIRCULARSTRING (0 0, 40 20, 60 60, 20 40, 0 0), (90 90, 120 70, 120 80, 90 90)) + CURVEPOLYGON ((0 0, 0 230, 210 230, 210 0, 0 0), COMPOUNDCURVE ((70 120, 90 90), CIRCULARSTRING (90 90, 100 120, 70 120)), CIRCULARSTRING (60 60, 75 53.78679656440357, 90 60, 96.21320343559643 75, 90 90, 75 96.21320343559643, 60 90, 53.78679656440357 75, 60 60), CIRCULARSTRING (0 0, 40 20, 60 60, 20 40, 0 0), (90 90, 120 70, 120 80, 90 90)) @@ -174,13 +174,13 @@ - MULTISURFACE (CURVEPOLYGON (CIRCULARSTRING (60 90, 90 90, 90 60, 60 60, 60 90)), CURVEPOLYGON (CIRCULARSTRING (0 0, 20 40, 60 60, 40 20, 0 0))) + MULTISURFACE (CURVEPOLYGON (CIRCULARSTRING (60 60, 53.78679656440357 75, 60 90, 90 90, 90 60, 75 53.78679656440357, 60 60)), CURVEPOLYGON (CIRCULARSTRING (0 0, 20 40, 60 60, 40 20, 0 0))) - CURVEPOLYGON ((0 0, 0 300, 340 300, 340 0, 0 0), CIRCULARSTRING (60 90, 60 60, 90 60, 90 90, 60 90), CIRCULARSTRING (0 0, 40 20, 60 60, 20 40, 0 0)) + CURVEPOLYGON ((0 0, 0 300, 340 300, 340 0, 0 0), CIRCULARSTRING (60 60, 75 53.78679656440357, 90 60, 90 90, 60 90, 53.78679656440357 75, 60 60), CIRCULARSTRING (0 0, 40 20, 60 60, 20 40, 0 0)) @@ -226,12 +226,29 @@ CURVEPOLYGON (COMPOUNDCURVE((0 80, 0 240, 220 240, 220 80), CIRCULARSTRING(220 80, 110 100, 0 80)), COMPOUNDCURVE((110 100, 120 120), CIRCULARSTRING(120 120, 110 120, 110 100))) + + + CURVEPOLYGON (COMPOUNDCURVE ((20 86.75950277309494, 20 160, 200 160, 200 86.75950277309494), CIRCULARSTRING (200 86.75950277309494, 155.48436755308268 96.672156424695, 110 100, 64.5156324469173 96.672156424695, 20 86.75950277309494)), COMPOUNDCURVE ((110 100, 120 120), CIRCULARSTRING (120 120, 110 120, 110 100))) + + + CURVEPOLYGON (COMPOUNDCURVE ((0 80, 0 240, 220 240, 220 80), CIRCULARSTRING (220 80, 210.05709729666793 83.54869072597688, 200 86.75950277309494), (200 86.75950277309494, 200 0, 20 0), (20 0, 20 86.75950277309494), CIRCULARSTRING (20 86.75950277309494, 9.942902703332095 83.54869072597688, 0 80))) + + + MULTISURFACE (CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (110 100, 110 120, 120 120), (120 120, 110 100))), CURVEPOLYGON (COMPOUNDCURVE ((20 0, 20 86.75950277309494), CIRCULARSTRING (20 86.75950277309494, 64.5156324469173 96.672156424695, 110 100, 155.48436755308268 96.672156424695, 200 86.75950277309494), (200 86.75950277309494, 200 0, 20 0)))) + + + + + + MULTISURFACE (CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (110 100, 110 120, 120 120), (120 120, 110 100))), CURVEPOLYGON (COMPOUNDCURVE ((0 80, 0 240, 220 240, 220 80), CIRCULARSTRING (220 80, 210.05709729666793 83.54869072597688, 200 86.75950277309494), (200 86.75950277309494, 200 160, 20 160, 20 86.75950277309494), CIRCULARSTRING (20 86.75950277309494, 9.942902703332095 83.54869072597688, 0 80))), CURVEPOLYGON (COMPOUNDCURVE ((20 0, 20 86.75950277309494), CIRCULARSTRING (20 86.75950277309494, 64.5156324469173 96.672156424695, 110 100, 155.48436755308268 96.672156424695, 200 86.75950277309494), (200 86.75950277309494, 200 0, 20 0)))) + + mAmA - complex polygons touching and overlapping