Skip to content

VoronoiDiagramBuilder: tolerance-based dedup + safe cell construction (stacked on #1212)#3

Open
grootstebozewolf wants to merge 4 commits into
micycle1:fixupfrom
grootstebozewolf:stacked-on-1212-voronoi-deduplication
Open

VoronoiDiagramBuilder: tolerance-based dedup + safe cell construction (stacked on #1212)#3
grootstebozewolf wants to merge 4 commits into
micycle1:fixupfrom
grootstebozewolf:stacked-on-1212-voronoi-deduplication

Conversation

@grootstebozewolf

@grootstebozewolf grootstebozewolf commented Jul 12, 2026

Copy link
Copy Markdown

Stacked PR on top of micycle1's \fixup\ (which powers locationtech#1212)

Base: \micycle1/fixup\

Summary

This is a stacked PR built directly on top of the great work in micycle1's \fixup\ branch (and locationtech#1212).

PR locationtech#1212 delivers:

  • A fast Shewchuk-style error-bounded \isInCircleRobust\ (double fast-path + DD fallback)
  • \Orientation.index\ for \Vertex.isCCW\
  • Broader robustness tests

This change adds the pieces required to fully resolve the original locationtech#20 report for the public \VoronoiDiagramBuilder\ API:

  • Tolerance-based deduplication using \KdTree\ when \setTolerance(tol > 0)\
  • Public helper \DelaunayTriangulationBuilder.unique(Coordinate[], double)\
  • Defensive \LinearRing\ construction in \getVoronoiCellPolygon\ (guarantees a valid ≥4-point ring and that cell count always matches the number of unique sites)
  • Strict regression test using the exact 7-point WKB from the issue + \setTolerance(0.1), asserting \isValid()\ + exactly 4 cells
  • Full documentation + reference to the Rocq/Flocq formal verification (\doc/issue20-verification.md)

Why this stack on \ ixup?

The predicate improvements are an excellent foundation for the whole triangulation stack.

For the specific scenario reported in locationtech#20 (\VoronoiDiagramBuilder\ + positive tolerance on near-coincident sites), we also need upstream prevention and downstream safety:

  • Users reasonably expect \setTolerance(0.1)\ to collapse near-coincident sites and produce the correct number of cells.
  • Even with stronger predicates, the Voronoi dual walk can still produce degenerate rings for certain configurations.

This work provides exactly that defense-in-depth on top of the predicate improvements.

Relationship to locationtech#1212

  • Complementary, not competing.
  • The \isInCircleRobust\ + orientation changes from the base become the new robust implementation.
  • This PR supplies the higher-level input sanitization and output guarantees that make the reported reproducer (and the documented \setTolerance\ behavior) actually work.

Testing & verification

  • The new \VoronoiDiagramBuilderTest#testRobustnessIssue20NearCoincidentPoints\ (literal WKB + \setTolerance(0.1)) passes and asserts the expected 4 cells.
  • Full Voronoi + Delaunay test suites continue to pass.
  • Formal verification reference for the orientation predicate via NetTopologySuite.Proofs (Rocq + Flocq).

Request to the author / maintainers

@micycle1 — I really like the direction in your \ ixup\ / locationtech#1212. The bounded fast-path incircle predicate is a nice improvement.

I put this together as a stack on top of \ ixup\ because the original locationtech#20 issue (and many real-world Voronoi users) specifically exercise \VoronoiDiagramBuilder.setTolerance(...). Would you be open to reviewing this, or folding the relevant parts (dedup + cell safety) into the base work? I'm very happy to rebase, adjust scope, or change anything to make the overall contribution stronger.

The only goal here is to make JTS more robust for everyone.

Happy to chat about any trade-offs.

Thanks again for pushing on this area!

#MakeJTSStronger

High-level diff

  • \DelaunayTriangulationBuilder\ / \VoronoiDiagramBuilder\ : dedup via KdTree + unique(..., tolerance)
  • \QuadEdgeSubdivision\ : safe \getVoronoiCellPolygon\ (tiny valid ring synthesis + count invariant)
  • \VoronoiDiagramBuilderTest\ : exact #20 repro test with count assertion
  • \doc/\ : verification report + history entry

How to review/test locally:

\
git fetch https://github.com/grootstebozewolf/jts.git stacked-on-1212-voronoi-deduplication

or from this fork

\\

…ent points.

- Use robust Orientation.index in Vertex.isCCW (raw arith in comments)
- Dedup sites with tolerance via KdTree in DelaunayTriangulationBuilder.unique(..., tol) and both builders' create paths
- Remove debug println in getVoronoiCellPolygon; ensure >=4 pts for LinearRing ctor always
- Add regression test using exact issue locationtech#20 WKB + tol=0.1 asserting valid + correct cell count for unique sites

All existing tests pass; repro now succeeds with valid diagram of 4 cells.
…CellPolygon + filter; independent count assert in repro test (hardcoded 4 from known locationtech#20 data). Targeted tests + fresh verif runs pass.
@micycle1

Copy link
Copy Markdown
Owner

I think this would be better as a separate PR to Dr JTS if/once this PR is merged, so changes remain discrete.

VoronoiDiagramBuilder.java, DelaunayTriangulationBuilder.java and
QuadEdgeSubdivision.java used outdated 'Eclipse Public License v1.0'
headers (and epl-v10.html links). Updated to match the canonical
header in build-tools/src/main/resources/jts/header.txt:

- 'Eclipse Public License 2.0'
- 'http://www.eclipse.org/legal/epl-v20.html'

This fixes the Maven checkstyle header check failures.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants