Add arc-arc corner rounding for CurvilinearPolygon - #271
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
gpeairs
left a comment
There was a problem hiding this comment.
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!)
| 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 |
There was a problem hiding this comment.
Doesn't look like most of these imports are needed anymore (maybe just to_curvilinear?)
| 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) |
There was a problem hiding this comment.
If this is only used in one testitem I would just define it there
| append!(candidate_centers, solve_cc(D_in, D_out)) | ||
| end | ||
|
|
||
| # Keep in-sweep candidates; pick the center nearest the corner. |
There was a problem hiding this comment.
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)
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)
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 nothingit works out OK.
| 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). |
There was a problem hiding this comment.
I don't think mod pi is ever valid, see other comment with examples
| 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. |
There was a problem hiding this comment.
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.
| arc_in::Paths.Turn{T}, | ||
| arc_out::Paths.Turn{T}, |
There was a problem hiding this comment.
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.
| 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[] |

Adds
Roundedsupport for arc-arc corners in CurvilinearPolygon, so vertices where two circularPaths.Turnarcs 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, honorsp0/inverse_selection, trims both neighboring arcs, and leaves non-Turncurve corners unrounded.