diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/quadedge/TrianglePredicate.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/quadedge/TrianglePredicate.java index 9397408103..890e2801bc 100644 --- a/modules/core/src/main/java/org/locationtech/jts/triangulate/quadedge/TrianglePredicate.java +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/quadedge/TrianglePredicate.java @@ -95,6 +95,46 @@ public static boolean isInCircleNormalized( return disc > 0; } + public static boolean isInCircleAdapt( + Coordinate a, Coordinate b, Coordinate c, + Coordinate p) { + double epsilon = Math.ulp(1.0); + double iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; + double adx = a.x - p.x; + double ady = a.y - p.y; + double bdx = b.x - p.x; + double bdy = b.y - p.y; + double cdx = c.x - p.x; + double cdy = c.y - p.y; + + double bdxcdy = bdx * cdy; + double cdxbdy = cdx * bdy; + double alift = adx * adx + ady * ady; + + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + double blift = bdx * bdx + bdy * bdy; + + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + double clift = cdx * cdx + cdy * cdy; + + double det = alift * (bdxcdy - cdxbdy) + + blift * (cdxady - adxcdy) + + clift * (adxbdy - bdxady); + + double permanent = (Math.abs(bdxcdy) + Math.abs(cdxbdy)) * alift + + (Math.abs(cdxady) + Math.abs(adxcdy)) * blift + + (Math.abs(adxbdy) + Math.abs(bdxady)) * clift; + double errbound = iccerrboundA * permanent; + if (det > errbound) { + return true; + } + if (-det > errbound) { + return false; + } + return isInCircleDDFast(a, b, c, p); + } /** * Computes twice the area of the oriented triangle (a, b, c), i.e., the area is positive if the * triangle is oriented counterclockwise. @@ -123,9 +163,7 @@ public static boolean isInCircleRobust( Coordinate a, Coordinate b, Coordinate c, Coordinate p) { - //checkRobustInCircle(a, b, c, p); -// return isInCircleNonRobust(a, b, c, p); - return isInCircleNormalized(a, b, c, p); + return isInCircleAdapt(a, b, c, p); } /** diff --git a/modules/core/src/test/java/org/locationtech/jts/triangulate/DelaunayTest.java b/modules/core/src/test/java/org/locationtech/jts/triangulate/DelaunayTest.java index be0b90ead6..dd4d75cbb5 100644 --- a/modules/core/src/test/java/org/locationtech/jts/triangulate/DelaunayTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/triangulate/DelaunayTest.java @@ -63,7 +63,7 @@ public void testGrid() public void testCircle() { String wkt = "POLYGON ((42 30, 41.96 29.61, 41.85 29.23, 41.66 28.89, 41.41 28.59, 41.11 28.34, 40.77 28.15, 40.39 28.04, 40 28, 39.61 28.04, 39.23 28.15, 38.89 28.34, 38.59 28.59, 38.34 28.89, 38.15 29.23, 38.04 29.61, 38 30, 38.04 30.39, 38.15 30.77, 38.34 31.11, 38.59 31.41, 38.89 31.66, 39.23 31.85, 39.61 31.96, 40 32, 40.39 31.96, 40.77 31.85, 41.11 31.66, 41.41 31.41, 41.66 31.11, 41.85 30.77, 41.96 30.39, 42 30))"; - String expected = "MULTILINESTRING ((41.66 31.11, 41.85 30.77), (41.41 31.41, 41.66 31.11), (41.11 31.66, 41.41 31.41), (40.77 31.85, 41.11 31.66), (40.39 31.96, 40.77 31.85), (40 32, 40.39 31.96), (39.61 31.96, 40 32), (39.23 31.85, 39.61 31.96), (38.89 31.66, 39.23 31.85), (38.59 31.41, 38.89 31.66), (38.34 31.11, 38.59 31.41), (38.15 30.77, 38.34 31.11), (38.04 30.39, 38.15 30.77), (38 30, 38.04 30.39), (38 30, 38.04 29.61), (38.04 29.61, 38.15 29.23), (38.15 29.23, 38.34 28.89), (38.34 28.89, 38.59 28.59), (38.59 28.59, 38.89 28.34), (38.89 28.34, 39.23 28.15), (39.23 28.15, 39.61 28.04), (39.61 28.04, 40 28), (40 28, 40.39 28.04), (40.39 28.04, 40.77 28.15), (40.77 28.15, 41.11 28.34), (41.11 28.34, 41.41 28.59), (41.41 28.59, 41.66 28.89), (41.66 28.89, 41.85 29.23), (41.85 29.23, 41.96 29.61), (41.96 29.61, 42 30), (41.96 30.39, 42 30), (41.85 30.77, 41.96 30.39), (41.66 31.11, 41.96 30.39), (41.41 31.41, 41.96 30.39), (41.41 28.59, 41.96 30.39), (41.41 28.59, 41.41 31.41), (38.59 28.59, 41.41 28.59), (38.59 28.59, 41.41 31.41), (38.59 28.59, 38.59 31.41), (38.59 31.41, 41.41 31.41), (38.59 31.41, 39.61 31.96), (39.61 31.96, 41.41 31.41), (39.61 31.96, 40.39 31.96), (40.39 31.96, 41.41 31.41), (40.39 31.96, 41.11 31.66), (38.04 30.39, 38.59 28.59), (38.04 30.39, 38.59 31.41), (38.04 30.39, 38.34 31.11), (38.04 29.61, 38.59 28.59), (38.04 29.61, 38.04 30.39), (39.61 28.04, 41.41 28.59), (38.59 28.59, 39.61 28.04), (38.89 28.34, 39.61 28.04), (40.39 28.04, 41.41 28.59), (39.61 28.04, 40.39 28.04), (41.96 29.61, 41.96 30.39), (41.41 28.59, 41.96 29.61), (41.66 28.89, 41.96 29.61), (40.39 28.04, 41.11 28.34), (38.04 29.61, 38.34 28.89), (38.89 31.66, 39.61 31.96))"; + String expected = "MULTILINESTRING ((38 30, 38.04 29.61), (38 30, 38.04 30.39), (38.04 29.61, 38.04 30.39), (38.04 29.61, 38.15 29.23), (38.04 29.61, 38.34 28.89), (38.04 29.61, 38.59 28.59), (38.04 29.61, 38.59 31.41), (38.04 30.39, 38.15 30.77), (38.04 30.39, 38.34 31.11), (38.04 30.39, 38.59 31.41), (38.15 29.23, 38.34 28.89), (38.15 30.77, 38.34 31.11), (38.34 28.89, 38.59 28.59), (38.34 31.11, 38.59 31.41), (38.59 28.59, 38.59 31.41), (38.59 28.59, 38.89 28.34), (38.59 28.59, 39.61 28.04), (38.59 28.59, 40.39 28.04), (38.59 28.59, 41.41 28.59), (38.59 31.41, 38.89 31.66), (38.59 31.41, 39.61 31.96), (38.59 31.41, 40.39 31.96), (38.59 31.41, 41.41 28.59), (38.59 31.41, 41.41 31.41), (38.89 28.34, 39.23 28.15), (38.89 28.34, 39.61 28.04), (38.89 31.66, 39.23 31.85), (38.89 31.66, 39.61 31.96), (39.23 28.15, 39.61 28.04), (39.23 31.85, 39.61 31.96), (39.61 28.04, 40 28), (39.61 28.04, 40.39 28.04), (39.61 31.96, 40 32), (39.61 31.96, 40.39 31.96), (40 28, 40.39 28.04), (40 32, 40.39 31.96), (40.39 28.04, 40.77 28.15), (40.39 28.04, 41.11 28.34), (40.39 28.04, 41.41 28.59), (40.39 31.96, 40.77 31.85), (40.39 31.96, 41.11 31.66), (40.39 31.96, 41.41 31.41), (40.77 28.15, 41.11 28.34), (40.77 31.85, 41.11 31.66), (41.11 28.34, 41.41 28.59), (41.11 31.66, 41.41 31.41), (41.41 28.59, 41.41 31.41), (41.41 28.59, 41.66 28.89), (41.41 28.59, 41.96 29.61), (41.41 31.41, 41.66 31.11), (41.41 31.41, 41.96 29.61), (41.41 31.41, 41.96 30.39), (41.66 28.89, 41.85 29.23), (41.66 28.89, 41.96 29.61), (41.66 31.11, 41.85 30.77), (41.66 31.11, 41.96 30.39), (41.85 29.23, 41.96 29.61), (41.85 30.77, 41.96 30.39), (41.96 29.61, 41.96 30.39), (41.96 29.61, 42 30), (41.96 30.39, 42 30))"; checkDelaunayEdges(wkt, expected); } diff --git a/modules/core/src/test/java/org/locationtech/jts/triangulate/quadedge/TrianglePredicateTest.java b/modules/core/src/test/java/org/locationtech/jts/triangulate/quadedge/TrianglePredicateTest.java new file mode 100644 index 0000000000..0580567cc1 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/triangulate/quadedge/TrianglePredicateTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2026 Darafei Praliaskouski. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +package org.locationtech.jts.triangulate.quadedge; + +import junit.framework.TestCase; +import junit.textui.TestRunner; +import org.locationtech.jts.geom.Coordinate; + +/** + * Tests for triangle predicates. + */ +public class TrianglePredicateTest extends TestCase { + + public static void main(String args[]) { + TestRunner.run(TrianglePredicateTest.class); + } + + public TrianglePredicateTest(String name) { + super(name); + } + + public void testAdaptiveInCircleClearCases() { + Coordinate a = new Coordinate(0, 0); + Coordinate b = new Coordinate(10, 0); + Coordinate c = new Coordinate(0, 10); + + assertTrue(TrianglePredicate.isInCircleRobust(a, b, c, new Coordinate(1, 1))); + assertFalse(TrianglePredicate.isInCircleRobust(a, b, c, new Coordinate(20, 20))); + } + + public void testAdaptiveInCircleFallbackUsesDDComputation() { + Coordinate a = new Coordinate(100000000000000.19, 100000000000000.53); + Coordinate b = new Coordinate(100000000000000.19, 100000000000000.47); + Coordinate c = new Coordinate(100000000000000.22, 100000000000000.38); + Coordinate p = new Coordinate(100000000000000.40, 100000000000000.75); + + assertTrue(TrianglePredicate.isInCircleDDFast(a, b, c, p)); + assertTrue(TrianglePredicate.isInCircleRobust(a, b, c, p)); + } +}