Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ The format of this changelog is based on

## Unreleased

- Normalize rotation angles to [0, 360) when writing GDS files
- Normalized rotation angles to [0, 360) when writing GDS files
- Added layerwise Booleans `union2d_layerwise`, `difference2d_layerwise`, `intersect2d_layerwise`, and `xor2d_layerwise`
- Added `Polygons.area`
- Fixed overly-strict argument types for polygon clipping methods

## 1.12.0 (2026-04-13)

Expand Down
2 changes: 1 addition & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ makedocs(
format=Documenter.HTML(
prettyurls=true,
assets=["assets/favicon.ico"],
size_threshold=300_000,
size_threshold=400_000,
collapselevel=1
),
sitename="DeviceLayout.jl",
Expand Down
6 changes: 6 additions & 0 deletions docs/src/reference/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
Polygon(::AbstractVector{Point{T}}) where {T}
Polygon(::Point, ::Point, ::Point, ::Point...)
Rectangle
Polygons.area
bounds
circle_polygon
gridpoints_in_polygon
Expand All @@ -174,10 +175,15 @@
```@docs
Polygons.ClippedPolygon
difference2d
difference2d_layerwise
intersect2d
intersect2d_layerwise
union2d
union2d_layerwise
xor2d
xor2d_layerwise
clip
clip_tiled
Polygons.StyleDict
```

Expand Down
16 changes: 14 additions & 2 deletions src/DeviceLayout.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import Unitful: Length, LengthUnits, DimensionlessQuantity, NoUnits, DimensionEr
import Unitful: ustrip, unit, inch
Unitful.@derived_dimension InverseLength inv(Unitful.𝐋)

import SpatialIndexing

function render! end
export render!

Expand Down Expand Up @@ -195,6 +197,7 @@ coordinatetype(::AbstractArray{S}) where {T, S <: AbstractGeometry{T}} = T
coordinatetype(iterable) = promote_type(coordinatetype.(iterable)...)
coordinatetype(::Point{T}) where {T} = T
coordinatetype(::Type{Point{T}}) where {T} = T
coordinatetype(::Pair{<:AbstractGeometry{T}}) where {T} = T

# Entity interface
include("entities.jl")
Expand Down Expand Up @@ -378,10 +381,13 @@ import .Polygons:
circle,
circle_polygon,
clip,
clip_tiled,
cliptree,
difference2d,
difference2d_layerwise,
gridpoints_in_polygon,
intersect2d,
intersect2d_layerwise,
offset,
perimeter,
points,
Expand All @@ -390,7 +396,9 @@ import .Polygons:
sweep_poly,
unfold,
union2d,
xor2d
union2d_layerwise,
xor2d,
xor2d_layerwise
export Polygons,
Polygon,
Ellipse,
Expand All @@ -406,8 +414,10 @@ export Polygons,
clip,
cliptree,
difference2d,
difference2d_layerwise,
gridpoints_in_polygon,
intersect2d,
intersect2d_layerwise,
offset,
perimeter,
points,
Expand All @@ -416,7 +426,9 @@ export Polygons,
sweep_poly,
unfold,
union2d,
xor2d
union2d_layerwise,
xor2d,
xor2d_layerwise

include("align.jl")
using .Align
Expand Down
50 changes: 50 additions & 0 deletions src/entities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,53 @@ lowerleft(aent::ArrayEntity) = lowerleft(aent.a)
upperright(aent::ArrayEntity) = upperright(aent.a)
halo(aent::ArrayEntity, outer_delta, inner_delta=nothing) =
ArrayEntity(halo(aent.a, outer_delta, inner_delta))

######## Entity selection
function SpatialIndexing.mbr(ent::AbstractGeometry{T}) where {T}
r = bounds(ent)
return SpatialIndexing.Rect(
(ustrip(unit(onemicron(T)), float.(r.ll))...,),
(ustrip(unit(onemicron(T)), float.(r.ur))...,)
)
end

"""
mbr_spatial_index(geoms)

An `RTree` of minimum bounding rectangles for `geoms`, with indices in `geoms` as values.

See also [`findbox`](@ref).
"""
function mbr_spatial_index(geoms)
tree = SpatialIndexing.RTree{Float64, 2}(Int)
function convertel(enum_ent)
idx, ent = enum_ent
return SpatialIndexing.SpatialElem(SpatialIndexing.mbr(ent), nothing, idx)
end
SpatialIndexing.load!(tree, enumerate(geoms), convertel=convertel)
return tree
end

"""
findbox(box, geoms; intersects=false)
findbox(box, tree::SpatialIndexing.RTree; intersects=false)

Return `indices` such that `geoms[indices]` gives all elements of `geoms` with minimum bounding rectangle contained in `bounds(box)`.

A spatial index created with [`mbr_spatial_index(geoms)`](@ref) can be supplied explicitly to avoid re-indexing for multiple `findbox` operations.

By default, `findbox` will find only entities with bounds contained in `bounds(box)`. If `intersects` is `true`,
it also includes entities with bounds intersecting `bounds(box)` (including those only touching edge-to-edge).
"""
function findbox(box, geoms; intersects=false)
tree = mbr_spatial_index(geoms)
return findbox(box, tree; intersects)
end

function findbox(box, tree::SpatialIndexing.RTree; intersects=false)
intersects && return map(
x -> x.val,
SpatialIndexing.intersects_with(tree, SpatialIndexing.mbr(box))
)
return map(x -> x.val, SpatialIndexing.contained_in(tree, SpatialIndexing.mbr(box)))
end
Loading
Loading