From 4ce3fc1f3df963edf73f3aee9ed5d4bf22002293 Mon Sep 17 00:00:00 2001 From: Greg Peairs Date: Mon, 30 Mar 2026 16:54:25 +0200 Subject: [PATCH 1/2] Use curvilinear polygon coordinate type for rounded result, not promotion with rounding type --- src/curvilinear.jl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/curvilinear.jl b/src/curvilinear.jl index ecd4e6e1b..9c8121c6d 100644 --- a/src/curvilinear.jl +++ b/src/curvilinear.jl @@ -183,12 +183,15 @@ CurvilinearRegion(points::Vector{Point{T}}, curves, curve_start_idx) where {T} = to_polygons(e::CurvilinearRegion{T}; kwargs...) where {T} = to_polygons(difference2d(to_polygons(e.exterior), to_polygons.(e.holes))) -to_polygons(e::CurvilinearRegion, sty::Polygons.Rounded; kwargs...) = to_polygons( - difference2d( - to_polygons(e.exterior, sty; kwargs...), - [to_polygons(h, sty; kwargs...) for h in e.holes] +function to_polygons(e::CurvilinearRegion, sty::Polygons.Rounded; kwargs...) + isempty(e.holes) && return to_polygons(e.exterior, sty; kwargs...) + return to_polygons( + difference2d( + to_polygons(e.exterior, sty; kwargs...), + [to_polygons(h, sty; kwargs...) for h in e.holes] + ) ) -) +end function transform(e::CurvilinearRegion{T}, f::Transformation) where {T} return CurvilinearRegion{T}(transform(e.exterior, f), transform.(e.holes, Ref(f))) @@ -581,7 +584,7 @@ function to_polygons( iszero(rad_raw) && return to_polygons(ent; kwargs...) relative = rad_raw isa Real - V = promote_type(float(S), float(T)) + V = float(S) poly = ent.p n = length(poly) @@ -718,7 +721,7 @@ function to_polygons( end end - return Polygon(final_points) + return Polygon{V}(final_points) end # Intersect a parallel line (p_offset + s * v_line) with a circle of radius D centered at O. From bad8515d089333c29585691bbe5b0b1106c3954e Mon Sep 17 00:00:00 2001 From: Greg Peairs Date: Tue, 31 Mar 2026 12:21:39 +0200 Subject: [PATCH 2/2] Always avoid promotion with rounded coordinate type --- src/curvilinear.jl | 14 +++++++++----- src/polygons.jl | 10 +++++----- src/solidmodels/render.jl | 20 ++++++++++---------- test/test_line_arc_rounding.jl | 10 ++++++++++ test/test_shapes.jl | 7 +++++++ 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/curvilinear.jl b/src/curvilinear.jl index 9c8121c6d..bec65334b 100644 --- a/src/curvilinear.jl +++ b/src/curvilinear.jl @@ -181,10 +181,14 @@ CurvilinearRegion(points::Vector{Point{T}}, segments) where {T} = CurvilinearRegion(points::Vector{Point{T}}, curves, curve_start_idx) where {T} = CurvilinearRegion(CurvilinearPolygon(points, curves, curve_start_idx)) -to_polygons(e::CurvilinearRegion{T}; kwargs...) where {T} = - to_polygons(difference2d(to_polygons(e.exterior), to_polygons.(e.holes))) +function to_polygons(e::CurvilinearRegion; kwargs...) + isempty(e.holes) && return [to_polygons(e.exterior; kwargs...)] + return to_polygons( + difference2d(to_polygons(e.exterior; kwargs...), to_polygons.(e.holes; kwargs...)) + ) +end function to_polygons(e::CurvilinearRegion, sty::Polygons.Rounded; kwargs...) - isempty(e.holes) && return to_polygons(e.exterior, sty; kwargs...) + isempty(e.holes) && return [to_polygons(e.exterior, sty; kwargs...)] return to_polygons( difference2d( to_polygons(e.exterior, sty; kwargs...), @@ -584,7 +588,7 @@ function to_polygons( iszero(rad_raw) && return to_polygons(ent; kwargs...) relative = rad_raw isa Real - V = float(S) + V = float(S) # No promotion with T (bypass Unitful.jl#845), entity coordinate type has priority poly = ent.p n = length(poly) @@ -796,7 +800,7 @@ function rounded_corner_line_arc( min_side_len=radius, min_angle=1e-3 ) where {T, S <: DeviceLayout.Coordinate} - V = promote_type(T, S) + V = float(T) r = convert(V, radius) # Check straight edge length against min_side_len diff --git a/src/polygons.jl b/src/polygons.jl index c2d907c02..d07b55ea7 100644 --- a/src/polygons.jl +++ b/src/polygons.jl @@ -754,13 +754,13 @@ function _round_poly( ) where {T, S <: Coordinate} iszero(radius) && return pol # If radius is dimensional, non-relative rounding. - V = ((S <: Length && T <: Length) || (S <: Real && T <: Real)) ? promote_type(T, S) : T + V = float(T) # Tie break for Real, Real introduces a type instability for non-dimensional. relative = ((T <: Length) && (S <: Real)) || (relative && T <: Real && S <: Real) poly = points(pol) len = length(poly) - new_polygon = Point{float(V)}[] + new_polygon = Point{V}[] for i in eachindex(poly) if !(i in corner_indices) push!(new_polygon, poly[i]) @@ -783,7 +783,7 @@ function _round_poly( ) end end - return Polygon(new_polygon...) + return Polygon(new_polygon) end # Perform rounding by creating Polygons of each contour, dispatching @@ -799,7 +799,7 @@ function _round_poly( radius::S; kwargs... ) where {T, S <: Coordinate} - V = ((S <: Length && T <: Length) || (S <: Real && T <: Real)) ? promote_type(T, S) : T + V = float(T) new_pol = T == V ? deepcopy(pol) : convert(ClippedPolygon{V}, pol) round_node!.(new_pol.tree.children, Ref(radius); kwargs...) return new_pol @@ -829,7 +829,7 @@ function rounded_corner( min_side_len=radius, min_angle=1e-3 ) where {T, S <: Coordinate} - V = promote_type(T, S) + V = float(T) rad = convert(V, radius) v1 = (p1 - p0) / norm(p1 - p0) diff --git a/src/solidmodels/render.jl b/src/solidmodels/render.jl index a369b5a9d..82622384c 100644 --- a/src/solidmodels/render.jl +++ b/src/solidmodels/render.jl @@ -198,14 +198,14 @@ function round_to_curvilinearpolygon( min_side_len=relative ? zero(T) : radius )::CurvilinearPolygon{T} where {T, S <: Coordinate} # If radius is dimensional, non-relative rounding. - V = ((S <: Length && T <: Length) || (S <: Real && T <: Real)) ? promote_type(T, S) : T + V = float(T) # Tie break for Real, Real introduces a type instability for non-dimensional. relative = ((T <: Length) && (S <: Real)) || (relative && T <: Real && S <: Real) poly = points(pol) len = length(poly) - new_points = Point{float(V)}[] - new_curves = Paths.Turn{float(V)}[] + new_points = Point{V}[] + new_curves = Paths.Turn{V}[] new_curve_start_idx = Int[] for i in eachindex(poly) @@ -248,19 +248,19 @@ function round_to_curvilinearpolygon( min_side_len=relative ? zero(T) : radius )::CurvilinearPolygon{T} where {T, S <: Coordinate} # If radius is dimensional, non-relative rounding. - V = ((S <: Length && T <: Length) || (S <: Real && T <: Real)) ? promote_type(T, S) : T + V = float(T) # Tie break for Real, Real introduces a type instability for non-dimensional. relative = ((T <: Length) && (S <: Real)) || (relative && T <: Real && S <: Real) poly = points(pol) len = length(poly) - new_points = Point{float(V)}[] - new_curves = Paths.Turn{float(V)}[] + new_points = Point{V}[] + new_curves = Paths.Turn{V}[] new_curve_start_idx = Int[] # Track trims for existing curves when rounding line-arc corners - trim_start_pts = Dict{Int, Point{float(V)}}() - trim_end_pts = Dict{Int, Point{float(V)}}() + trim_start_pts = Dict{Int, Point{V}}() + trim_end_pts = Dict{Int, Point{V}}() # Determine which line-arc corners to round la_indices = if !isnothing(line_arc_corner_indices) @@ -382,7 +382,7 @@ function rounded_corner_segment( min_side_len=radius, min_angle=1e-3 ) where {T, S <: Coordinate} - V = promote_type(T, S) + V = float(T) rad = convert(V, radius) v1 = (p1 - p0) / norm(p1 - p0) @@ -441,7 +441,7 @@ function rounded_corner_segment_line_arc( min_side_len=radius, min_angle=1e-3 ) where {T, S <: Coordinate} - V = promote_type(T, S) + V = float(T) r = convert(V, radius) atol = DeviceLayout.Polygons._round_atol(T, S) diff --git a/test/test_line_arc_rounding.jl b/test/test_line_arc_rounding.jl index d745d1751..0dafae71d 100644 --- a/test/test_line_arc_rounding.jl +++ b/test/test_line_arc_rounding.jl @@ -514,6 +514,16 @@ # Relative result should differ from absolute (different radius semantics) @test length(points(rel_rounded)) != length(rounded_pts) || !isapprox(points(rel_rounded)[1], rounded_pts[1]; atol=0.1nm) + + # Unitful issue addressed by Unitful.jl PR#845 bypassed + rect = Rectangle(10.0μm2μm, 10.0μm2μm) + cr = CurvilinearRegion(CurvilinearPolygon(points(rect))) + rnd_μm2nm = Rounded(1.0μm2nm) + poly = only(to_polygons(rnd_μm2nm(cr))) # Runs without error + @test coordinatetype(poly) == typeof(1.00μm2μm) + # Test no-holes bypasses difference2d -- point order would be different + @test poly == to_polygons(cr.exterior, rnd_μm2nm) + @test only(to_polygons(cr)) == to_polygons(cr.exterior) end @testitem "Horseshoe landing pad rounding" setup = [CommonTestSetup] begin diff --git a/test/test_shapes.jl b/test/test_shapes.jl index 3bbb3ef5a..6c5ecf60c 100644 --- a/test/test_shapes.jl +++ b/test/test_shapes.jl @@ -390,6 +390,13 @@ @test_nowarn place!(cs, csty, GDSMeta()) c = Cell("main", nm) @test_nowarn render!(c, cs) + + # Use float input polygon coordinatetype -- don't promote with rounded type + r_int = Rectangle(2μm2μm, 2μm2μm) + r_float = Rectangle(2.0μm2μm, 2.0μm2μm) + rnd = Rounded(0.5μm2nm) + @test coordinatetype(to_polygons(rnd(r_int))) == typeof(1.02μm2μm) + @test coordinatetype(to_polygons(rnd(r_float))) == typeof(1.02μm2μm) end @testitem "Curvilinear" setup = [CommonTestSetup] begin