Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ The format of this changelog is based on
[Keep a Changelog](https://keepachangelog.com/), and this project adheres to
[Semantic Versioning](https://semver.org/).

## Unreleased

- Added `recover_curves` and the curve-preserving Boolean variants `union2d_curved`, `difference2d_curved`, `intersect2d_curved`, and `xor2d_curved`, which recover original curves (arcs, splines) from a clipped result wherever their discretized footprint survived the operation intact, returning a `Vector{CurvilinearRegion}`

## 1.14.0 (2026-05-28)

- Added `ParameterSet`, a nested dictionary wrapper with dot-access for reading and
Expand Down
30 changes: 29 additions & 1 deletion docs/src/concepts/polygons.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Geometric Boolean operations on polygons are called "clipping" operations. For 2

!!! info

Boolean operations in 3D with `SolidModel` are handled by the Open CASCADE Technology kernel, which works directly with rich geometry types rendered from our native `CoordinateSystem`. If you need boolean operations involving curved geometry whose results can't be achieved by clipping-then-rounding, then your 2D geometry should defer the boolean operation until `SolidModel` postrendering so that the result will still be represented with curves.
Boolean operations in 3D with `SolidModel` are handled by the Open CASCADE Technology kernel, which works directly with rich geometry types rendered from our native `CoordinateSystem`. If you need boolean operations involving curved geometry whose results can't be achieved by clipping-then-rounding, you have two options: keep curves in 2D using the curve-preserving Boolean variants (see [Recovering curves through clipping](@ref) below), or defer the boolean operation until `SolidModel` postrendering so that the result will still be represented with curves.

For many use cases, `union2d`, `difference2d`, `intersect2d`, and `xor2d` behave as expected and are easiest to use.
More general operations may be accomplished using the `clip` function.
Expand All @@ -45,6 +45,34 @@ A [`CurvilinearRegion`](@ref) pairs a `CurvilinearPolygon` exterior with zero or

See [API Reference: Curvilinear geometry](@ref api-curvilinear).

### Recovering curves through clipping

Boolean operations (`union2d`, `difference2d`, etc.) discretize curved geometry to polygons
before passing them to the Clipper library. Normally, the original curves (arcs, splines)
are lost in this process. The [`recover_curves`](@ref) function and its convenience
wrappers (`difference2d_curved`, `union2d_curved`, `intersect2d_curved`, `xor2d_curved`)
track each input curve's discretized integer-grid footprint and substitute the original
curve back into the result wherever that footprint survived the boolean operation intact.

The curve-preserving variants return a `Vector{CurvilinearRegion}` rather than a single
`ClippedPolygon`. Each region in the vector corresponds to one outer contour in the clipped
result (the disjoint pieces each become a separate region). For example:

```julia
# Standard clipping discretizes curves to polygons:
result = difference2d(a, b) # ClippedPolygon

# Curve-preserving variant recovers arcs where possible:
regions = difference2d_curved(a, b) # Vector{CurvilinearRegion}, arcs preserved
```

**Current limitations:** A curve is recovered only if its entire discretized run survives
the boolean operation with exact integer equality. If the operation cuts through a curve
(e.g., a straight edge crossing an arc's interior), that curve falls back to a polyline.
Additionally, curves can only be recovered on CurvilinearRegion/CurvilinearPolygon,
Path nodes, and `Rounded`-styled `Polygon`/`Rectangle` entities. `Rounded` applied to
other entities, styled Curvilinear entities, and nested styles do not yet support curve recovery.

## Styles

In addition to other generic [entity styles](./geometry.md#Entity-Styles) like `NoRender`, `AbstractPolygon`s can be paired with the `Rounded` style. `ClippedPolygon`s support `StyleDict`, which allows for different styles to be applied to different contours in its tree.
Expand Down
5 changes: 5 additions & 0 deletions docs/src/reference/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@
CurvilinearRegion
Curvilinear.edge_type_at_vertex
Curvilinear.line_arc_cornerindices
difference2d_curved
intersect2d_curved
recover_curves
union2d_curved
xor2d_curved
```

### [Shapes](@id api-shapes)
Expand Down
10 changes: 9 additions & 1 deletion src/DeviceLayout.jl
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,15 @@ include("render/render.jl")
# After render.jl to ensure access to `to_polygons`
include("curvilinear.jl")
using .Curvilinear
export Curvilinear, CurvilinearPolygon, CurvilinearRegion, pathtopolys
export Curvilinear,
CurvilinearPolygon,
CurvilinearRegion,
pathtopolys,
recover_curves,
difference2d_curved,
union2d_curved,
intersect2d_curved,
xor2d_curved

include("simple_shapes.jl")
import .SimpleShapes:
Expand Down
Loading