Skip to content

Add arc-arc corner rounding for CurvilinearPolygon - #271

Open
laylagi wants to merge 4 commits into
mainfrom
lgh/arc-arc-rounding
Open

Add arc-arc corner rounding for CurvilinearPolygon#271
laylagi wants to merge 4 commits into
mainfrom
lgh/arc-arc-rounding

Conversation

@laylagi

@laylagi laylagi commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Adds Rounded support for arc-arc corners in CurvilinearPolygon, so vertices where two circular Paths.Turn arcs meet are filleted with a symbolic arc tangent to both adjacent arcs. The implementation shares the existing curvilinear rounding producer used by GDS and SolidModel paths, honors p0/inverse_selection, trims both neighboring arcs, and leaves non-Turn curve corners unrounded.

@laylagi
laylagi requested a review from gpeairs July 24, 2026 20:23
@laylagi

laylagi commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Before and after rounding:

Screenshot 2026-07-24 at 1 03 57 PM

@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.87500% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/curvilinear.jl 96.87% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

@gpeairs gpeairs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I think my main comments are about whether antiparallel headings count as equivalent -- it seems by construction we can expect headings to always be continuous for successfully rounded corners, but correct me if I'm wrong.

(These look so cool!)

Comment thread src/solidmodels/solidmodels.jl Outdated
Comment on lines 85 to 95
import DeviceLayout.Curvilinear:
edge_type_at_vertex,
line_arc_cornerindices,
arc_arc_cornerindices,
islinear,
round_to_curvilinearpolygon,
rounded_corner_segment,
rounded_corner_segment_line_arc,
rounded_corner_segment_arc_arc,
to_curvilinear,
styled_loop

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't look like most of these imports are needed anymore (maybe just to_curvilinear?)

Comment thread test/runtests.jl Outdated
tangent points on the two arcs, those tangent points lie on the arcs' circles, and the
fillet center is at distance R±r from each arc center (external or internal tangency).
"""
function check_arc_arc_fillets(cp, fillet_r; atol_length=1.0nm, atol_angle=1e-6)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is only used in one testitem I would just define it there

Comment thread src/curvilinear.jl
append!(candidate_centers, solve_cc(D_in, D_out))
end

# Keep in-sweep candidates; pick the center nearest the corner.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this sometimes picks a center when it should reject all of them:

arc_in  = reverse(Paths.Turn(300.0°, 1.0μm, α0=180.0°))
arc_out = Paths.Turn(-300°, 1.0μm, α0=170°)
cp = CurvilinearPolygon([p0(arc_in), p1(arc_in), p1(arc_out)], [arc_in, arc_out], [1, 2])
cp_r = Rounded(2μm)(cp)
Image Image

or

arc_in  = reverse(Paths.Turn(-60.0°, 1.0μm; α0=180.0°))
arc_out = Paths.Turn(70.0°, 1.1μm, α0=120°)
cp = CurvilinearPolygon([p0(arc_in), p1(arc_in), p1(arc_out)], [arc_in, arc_out], [1, 2])
cp_r = Rounded(0.2μm)(cp)
Image

The issue seems to be that it finds an on_arc tangent point but the direction is wrong. I think if the final check at line 1663 is

    t_in = pathlength_nearest(arc_in, T_in)
    t_out = pathlength_nearest(arc_out, T_out)
    (
        isapprox(Paths.p0(fillet), T_in; atol=atol) &&
        isapprox(Paths.p1(fillet), T_out; atol=atol) &&
        isapprox_angle(Paths.α0(fillet), Paths.direction(arc_in, t_in)) &&
        isapprox_angle(Paths.α1(fillet), Paths.direction(arc_out, t_out))
    ) || return nothing

it works out OK.

Comment thread test/runtests.jl Outdated
isapprox(d_out, abs(R_out - fillet_r), atol=atol_length)

# G1 tangency: fillet heading at each end matches the arc's heading at the tangent
# point (mod π, since headings may be recorded in either traversal sense).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think mod pi is ever valid, see other comment with examples

Comment thread src/curvilinear.jl
O_out = Paths.curvaturecenter(arc_out)
p_corner = Paths.p1(arc_in) # == Paths.p0(arc_out)

# Already-smooth (G1) guard: matching or anti-parallel headings at the corner need no fillet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a [near-]cusp with anti-parallel headings could be rounded. Line-arc rounding also skips these, so I'm fine with either documenting the limitation or fixing it.

Comment thread src/curvilinear.jl
Comment on lines +1550 to +1551
arc_in::Paths.Turn{T},
arc_out::Paths.Turn{T},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same coordinate type is unnecessarily restrictive, especially since CurvilinearPolygons can mix segment coordinate types (#262). I think separate types with V = float(promote_type(T1, T2)) works as well.

Comment thread src/curvilinear.jl
new_curves = Paths.Turn{V}[]
# Fillets are always Turns, but a surviving original curve pushed through untouched can be
# any Segment (e.g. a BSpline on an un-roundable corner), so hold the general element type.
new_curves = Paths.Segment[]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Paths.Segment{V}[] work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants