Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Unreleased

- Improves robustness of set operations (`Union`, `Intersection`, `Difference`,
`SymmetricDifference`) by revamping ghost line construction. The previous
spanning tree approach could produce ghost lines that interfered with the
geometry in problematic ways. The new ray casting approach is more robust.

## v0.56.0

2025-11-21
Expand Down
100 changes: 54 additions & 46 deletions geom/alg_set_op_test.go

Large diffs are not rendered by default.

24 changes: 23 additions & 1 deletion geom/dcel.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
package geom

func newDCELFromGeometries(a, b Geometry) *doublyConnectedEdgeList {
a, b, ghosts := prepareGeometriesForDCEL(a, b)
return newDCELFromRenodedGeometries(a, b, ghosts)
}

// prepareGeometriesForDCEL pre-processes the input geometries (A and B) such
// that they can be used to create a DCEL. An additional "ghost"
// MultiLineString is also returned, which provides the appropriate connections
// such that A and B (when combined together) are fully connected.
func prepareGeometriesForDCEL(a, b Geometry) (Geometry, Geometry, MultiLineString) {
// Renode just A and B. Them being noded correctly is a pre-requisite for
// creating the ghosts.
a, b, _ = reNodeGeometries(a, b, MultiLineString{})

ghosts := createGhosts(a, b)
a, b, ghosts = reNodeGeometries(a, b, ghosts)

if ghosts.IsEmpty() {
return a, b, ghosts
}

// Renode again, since, the ghosts may have introduced new intersections
// with A and/or B.
return reNodeGeometries(a, b, ghosts)
}

func newDCELFromRenodedGeometries(a, b Geometry, ghosts MultiLineString) *doublyConnectedEdgeList {
interactions := findInteractionPoints([]Geometry{a, b, ghosts.AsGeometry()})

dcel := newDCEL()
Expand Down
Loading