If you compose two Rounded styles and apply them to a ClippedPolygon that has holes, the holes get dropped.
Minimal repro w/ Julia 1.12.4 and DL main
using DeviceLayout
using DeviceLayout.PreferredUnits
using Unitful: ustrip
outer = centered(Rectangle(100.0μm, 100.0μm))
hole = centered(Rectangle(40.0μm, 40.0μm))
cp = difference2d(outer, hole) # ClippedPolygon: one boundary, one hole
total_area(x) = sum(abs(DeviceLayout.Polygons.area(p)) for p in to_polygons(x))
expected = 100.0^2 - 40.0^2
single = total_area(Rounded(5.0μm)(cp)) # one rounding pass
composed = total_area(Rounded(5.0μm)(Rounded(5.0μm)(cp))) # two rounding passes (nested)
println("expected (100² - 40²) = $(expected) µm²")
println("single Rounded(5µm) = $(round(ustrip(μm^2, single), digits=1)) µm² (correct: hole subtracted)")
println("composed Rounded(5µm)∘(5µm) = $(round(ustrip(μm^2, composed), digits=1)) µm² (BUG: ≈100² ⇒ hole dropped)")
Tentative diagnosis (needs double-checking)
Composing two Rounded styles (a nested StyledEntity) routes through
to_polygons(::CurvilinearRegion, ::Rounded) # src/curvilinear.jl:199
which rounds the exterior and each hole independently and then re-runs difference2d:
difference2d(to_polygons(exterior, sty), [to_polygons(h, sty) for h in holes])
The rounded hole loops come back with the wrong winding, so difference2d no longer
subtracts them.
If you compose two
Roundedstyles and apply them to aClippedPolygonthat has holes, the holes get dropped.Minimal repro w/ Julia 1.12.4 and DL
mainTentative diagnosis (needs double-checking)
Composing two
Roundedstyles (a nestedStyledEntity) routes throughwhich rounds the exterior and each hole independently and then re-runs
difference2d:The rounded hole loops come back with the wrong winding, so
difference2dno longersubtracts them.