Skip to content

Commit d089d76

Browse files
authored
Add xor2d (#110)
1 parent 5dea39d commit d089d76

File tree

5 files changed

+45
-4
lines changed

5 files changed

+45
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ The format of this changelog is based on
44
[Keep a Changelog](https://keepachangelog.com/), and this project adheres to
55
[Semantic Versioning](https://semver.org/).
66

7+
## Upcoming
8+
9+
- Added `xor2d` for polygon XOR
10+
711
## 1.6.0 (2025-10-16)
812

913
- Improved metadata handling for `LayoutTarget` and `SolidModelTarget`

docs/src/polygons.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ For example, if you ask for the bounding box of a path node (which could define
1414

1515
## Clipping
1616

17-
Geometric Boolean operations on polygons are called "clipping" operations. For 2D geometry, these—`union2d`, `difference2d`, and `intersection2d`—are the only geometric Booleans available. Other geometry types are first converted to polygons using `to_polygons` to perform clipping.
17+
Geometric Boolean operations on polygons are called "clipping" operations. For 2D geometry, these—`union2d`, `difference2d`, `intersection2d`, and `xor2d`—are the only geometric Booleans available. Other geometry types are first converted to polygons using `to_polygons` to perform clipping.
1818

1919
!!! info
2020

@@ -27,6 +27,7 @@ More general operations may be accomplished using the `clip` function.
2727
union2d
2828
difference2d
2929
intersect2d
30+
xor2d
3031
clip
3132
cliptree
3233
```

src/DeviceLayout.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,8 @@ import .Polygons:
385385
rounded_corner,
386386
sweep_poly,
387387
unfold,
388-
union2d
388+
union2d,
389+
xor2d
389390
export Polygons,
390391
Polygon,
391392
Ellipse,
@@ -409,7 +410,8 @@ export Polygons,
409410
rounded_corner,
410411
sweep_poly,
411412
unfold,
412-
union2d
413+
union2d,
414+
xor2d
413415

414416
include("align.jl")
415417
using .Align

src/polygons.jl

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ export circle,
8080
rounded_corner,
8181
sweep_poly,
8282
unfold,
83-
union2d
83+
union2d,
84+
xor2d
8485

8586
const USCALE = 1.0 * Unitful.fm
8687
const SCALE = 10.0^9
@@ -1156,6 +1157,34 @@ function intersect2d(plus, minus)
11561157
)
11571158
end
11581159

1160+
"""
1161+
xor2d(p1, p2)
1162+
1163+
Return the symmetric difference (XOR) of `p1` and `p2` as a `ClippedPolygon`.
1164+
1165+
The XOR operation returns regions that are in either `p1` or `p2`, but not in both.
1166+
This is useful for finding non-overlapping regions between two sets of polygons.
1167+
1168+
Each of `p1` and `p2` may be a `GeometryEntity` or array of `GeometryEntity`. All entities
1169+
are first converted to polygons using [`to_polygons`](@ref).
1170+
1171+
Each of `p1` and `p2` can also be a `GeometryStructure` or `GeometryReference`, in which case
1172+
`elements(flatten(p))` will be converted to polygons.
1173+
1174+
Each can also be a pair `geom => layer`, where `geom` is a
1175+
`GeometryStructure` or `GeometryReference`, while `layer` is a `DeviceLayout.Meta`, a layer name `Symbol`, and/or a collection
1176+
of either, in which case only the elements in those layers will be used.
1177+
"""
1178+
function xor2d(p1, p2)
1179+
return clip(
1180+
Clipper.ClipTypeXor,
1181+
p1,
1182+
p2,
1183+
pfs=Clipper.PolyFillTypePositive,
1184+
pfc=Clipper.PolyFillTypePositive
1185+
)
1186+
end
1187+
11591188
function add_path!(
11601189
c::Clipper.Clip,
11611190
path::Vector{Point{T}},

test/test_clipping.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@
114114
@test to_polygons(
115115
union2d(Rectangle(2, 2) + Point(1, 1), [Rectangle(2, 2), Rectangle(10, 10)])
116116
)[1] == Polygon(Point{Int}[(10, 10), (0, 10), (0, 0), (10, 0)])
117+
118+
# XOR
119+
r1 = Rectangle(2.0, 2.0)
120+
r2 = Rectangle(2.0, 2.0) + Point(1.0, 1.0)
121+
@test xor2d(r1, r2) == union2d(difference2d(r2, r1), difference2d(r1, r2))
117122
end
118123

119124
@testset "> ClippedPolygon operations w/o units" begin

0 commit comments

Comments
 (0)