From ffdb667c60979259b77e1ce9ca5ca29d6868d5b6 Mon Sep 17 00:00:00 2001 From: Darafei Praliaskouski Date: Tue, 4 Sep 2018 22:10:58 +0300 Subject: [PATCH 1/3] isInCircleAdapt sketch Java port Do the non-robust calculation and calculation of error margin. If calculation is possibly not robust, fall back on slower math. https://www.cs.cmu.edu/afs/cs/project/quake/public/code/predicates.c --- .../quadedge/TrianglePredicate.java | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) 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..6256d131da 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 isInCircleDDSlow(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); } /** From 591faaf7069465d6ab5f2cc5085b5e13d6635cd7 Mon Sep 17 00:00:00 2001 From: Darafei Praliaskouski Date: Wed, 5 Sep 2018 01:27:35 +0300 Subject: [PATCH 2/3] Update TrianglePredicate.java --- .../jts/triangulate/quadedge/TrianglePredicate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6256d131da..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 @@ -130,10 +130,10 @@ public static boolean isInCircleAdapt( if (det > errbound) { return true; } - if (-det > errbound)) { + if (-det > errbound) { return false; } - return isInCircleDDSlow(a, b, c, p); + 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 From 0adb85b6a00a7461a61ad6ae07c97c8534160b47 Mon Sep 17 00:00:00 2001 From: Darafei Praliaskouski Date: Mon, 25 May 2026 04:22:34 +0400 Subject: [PATCH 3/3] Add tests for adaptive in-circle predicate --- .../jts/triangulate/DelaunayTest.java | 2 +- .../quadedge/TrianglePredicateTest.java | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 modules/core/src/test/java/org/locationtech/jts/triangulate/quadedge/TrianglePredicateTest.java 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)); + } +}