Skip to content

Commit 3faad83

Browse files
authored
Merge pull request #32 from aws-cqc/gp/fix-terminate
Fix initial termination starting point, behavior on curves
2 parents 8ee4d63 + 2448c0f commit 3faad83

File tree

7 files changed

+130
-20
lines changed

7 files changed

+130
-20
lines changed

Diff for: CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ The format of this changelog is based on
99
### Fixed
1010

1111
- `launch!` without rounding now has the correct gap behind the pad
12+
- `terminate!` with `initial=true` appends the termination before the `Path` start as documented (previously incorrectly kept `p0(path)` constant, shifting the rest of the `Path` forward)
13+
- `terminate!` with rounding on a curve is still drawn as straight but keeps the full underlying segment (previously consumed some turn angle to replace with straight segment including rounding length)
1214

1315
## 1.0.0 (2025-02-27)
1416

Diff for: src/paths/paths.jl

+28-10
Original file line numberDiff line numberDiff line change
@@ -1175,14 +1175,18 @@ If the preceding style is a CPW, this is a "short termination" if `iszero(gap)`
11751175
11761176
Rounding of corners may be specified with radius given by `rounding`. Rounding keeps the
11771177
trace length constant by removing some length from the preceding segment and adding a
1178-
rounded section of equivalent maximum length. (This produces a small quirk if you terminate
1179-
a curved segment with rounding—the termination is "straight" starting at `rounding`
1180-
away from the end of the curve, meaning that the `α0(pa)`)
1178+
rounded section of equivalent maximum length.
1179+
1180+
Terminations can be applied on curves without changing the underlying curve. If you add a
1181+
segment after a termination, it will start a straight distance `gap` away from where the original
1182+
curve ended. However, rounded terminations are always drawn as though straight from the point where
1183+
rounding starts, slightly before the end of the curve. This allows the rounded corners to be represented
1184+
as exact circular arcs.
11811185
11821186
If the preceding style is a trace, the termination only rounds the corners at the end of the
11831187
segment or does nothing if `iszero(rounding)`.
11841188
1185-
If `initial`, the termination is applied at the beginning of the `Path`.
1189+
If `initial`, the termination is appended before the beginning of the `Path`.
11861190
"""
11871191
function terminate!(
11881192
pa::Path{T};
@@ -1192,6 +1196,7 @@ function terminate!(
11921196
) where {T}
11931197
termlen = gap + rounding
11941198
iszero(termlen) && return
1199+
termsty = Termination(pa, rounding; initial=initial, cpwopen=!iszero(gap))
11951200
# Nonzero rounding: splice and delete to make room for rounded part
11961201
if !iszero(rounding)
11971202
orig_sty = initial ? undecorated(style0(pa)) : laststyle(pa)
@@ -1221,17 +1226,30 @@ function terminate!(
12211226
)
12221227
)
12231228
splice!(pa, split_idx, split(split_node, split_len))
1224-
deleteat!(pa, initial ? firstindex(pa) : lastindex(pa))
1229+
termsty = if initial
1230+
Termination(Path(pa[2:end]), rounding; initial=initial, cpwopen=!iszero(gap))
1231+
else
1232+
Termination(Path(pa[1:(end - 1)]), rounding; initial=initial, cpwopen=!iszero(gap))
1233+
end
12251234
end
12261235

1227-
termsty = Termination(pa, rounding; initial=initial, cpwopen=!iszero(gap))
1228-
12291236
if initial
12301237
α = α0(pa)
1231-
p = p0(pa) - termlen * Point(cos(α), sin(α))
1232-
pushfirst!(pa, Straight{T}(termlen, p, α), termsty)
1238+
p = p0(pa) - gap * Point(cos(α), sin(α))
1239+
pa.p0 = p
1240+
pushfirst!(pa, Straight{T}(gap, p, α), termsty)
1241+
if !iszero(rounding)
1242+
# merge first two segments and apply termsty
1243+
simplify!(pa, 1:2)
1244+
setstyle!(pa[1], termsty)
1245+
end
12331246
else
1234-
straight!(pa, termlen, termsty)
1247+
straight!(pa, gap, termsty)
1248+
if !iszero(rounding)
1249+
# merge last two segments and apply termsty
1250+
simplify!(pa, (length(pa) - 1):length(pa))
1251+
setstyle!(pa[end], termsty)
1252+
end
12351253
end
12361254
end
12371255

Diff for: src/render/termination.jl

+40-3
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,16 @@ function _poly(f::Paths.Straight{T}, s::Paths.CPWOpenTermination) where {T}
8080
end
8181
end
8282

83-
function to_polygons(f::Paths.Straight{T}, s::Paths.CPWOpenTermination; kwargs...) where {T}
83+
function to_polygons(f::Paths.Segment{T}, s::Paths.CPWOpenTermination; kwargs...) where {T}
84+
return to_polygons(_poly(f, s); kwargs...)
85+
end
86+
87+
# Disambiguate from compound fallback
88+
function to_polygons(
89+
f::Paths.CompoundSegment{T},
90+
s::Paths.CPWOpenTermination;
91+
kwargs...
92+
) where {T}
8493
return to_polygons(_poly(f, s); kwargs...)
8594
end
8695

@@ -124,8 +133,13 @@ function _poly(f::Paths.Straight{T}, s::Paths.CPWShortTermination) where {T}
124133
return [round1(poly1), round2(poly2)]
125134
end
126135

136+
function to_polygons(f::Paths.Segment{T}, s::Paths.CPWShortTermination; kwargs...) where {T}
137+
return to_polygons.(_poly(f, s); kwargs...)
138+
end
139+
140+
# Disambiguate from compound fallback
127141
function to_polygons(
128-
f::Paths.Straight{T},
142+
f::Paths.CompoundSegment{T},
129143
s::Paths.CPWShortTermination;
130144
kwargs...
131145
) where {T}
@@ -144,6 +158,29 @@ function _poly(f::Paths.Straight{T}, s::Paths.TraceTermination) where {T}
144158
)
145159
end
146160

147-
function to_polygons(f::Paths.Straight{T}, s::Paths.TraceTermination; kwargs...) where {T}
161+
function to_polygons(f::Paths.Segment{T}, s::Paths.TraceTermination; kwargs...) where {T}
162+
return to_polygons(_poly(f, s); kwargs...)
163+
end
164+
165+
# Disambiguate from compound fallback
166+
function to_polygons(
167+
f::Paths.CompoundSegment{T},
168+
s::Paths.TraceTermination;
169+
kwargs...
170+
) where {T}
148171
return to_polygons(_poly(f, s); kwargs...)
149172
end
173+
174+
## Generic segments—just draw as though straight
175+
function _poly(
176+
f::Paths.Segment{T},
177+
s::Union{Paths.TraceTermination, Paths.CPWOpenTermination, Paths.CPWShortTermination}
178+
) where {T}
179+
straight = if s.initial
180+
p = p1(f) - pathlength(f) * Point(cos(α1(f)), sin(α1(f)))
181+
Paths.Straight{T}(pathlength(f), p, α1(f))
182+
else
183+
Paths.Straight{T}(pathlength(f), p0(f), α0(f))
184+
end
185+
return _poly(straight, s)
186+
end

Diff for: src/solidmodels/render.jl

+9-4
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,13 @@ end
327327
# Note: this is called during SolidModel rendering after flattening, so we don't worry about decorations
328328
# Similarly generic tapers have been resolved
329329

330-
# Fallback: use to_polygons
331-
function to_primitives(::SolidModel, f::Paths.Segment, s::Paths.Style; kwargs...)
330+
# Fallback: use pathtopolys to get CurvilinearPolygons
331+
function to_primitives(
332+
::SolidModel,
333+
f::Paths.Segment{T},
334+
s::Paths.Style;
335+
kwargs...
336+
) where {T}
332337
return pathtopolys(f, s; kwargs...)
333338
end
334339

@@ -342,10 +347,10 @@ function to_primitives(
342347
return vcat(to_primitives.(sm, f.segments, s.styles; kwargs...)...)
343348
end
344349

345-
# Terminations are only used with Straight and generate one or two [Rounded] Polygons
350+
# Terminations generate up to two [Rounded] Polygons
346351
function to_primitives(
347352
sm::SolidModel,
348-
seg::Paths.Straight{T},
353+
seg::Paths.Segment{T},
349354
sty::Union{Paths.TraceTermination, Paths.CPWOpenTermination, Paths.CPWShortTermination};
350355
kwargs...
351356
) where {T}

Diff for: test/test_render.jl

+40-3
Original file line numberDiff line numberDiff line change
@@ -694,12 +694,12 @@ end
694694

695695
# Test Layout.jl#68
696696
els = initial ? reverse(elements(c)) : elements(c)
697+
pts_approx(el) = [round.(pt, digits=9) for pt in ustrip.(nm, points(el))]
697698
# First and second element should be CPW polygons
698-
straight_points = Set(reduce(vcat, points.(els[1:2])))
699+
straight_points = Set(reduce(vcat, pts_approx.(els[1:2])))
699700

700701
# Third element will be the terminating polygon
701-
termination_points = Set(points(els[3]))
702-
702+
termination_points = Set(pts_approx(els[3]))
703703
# Test that there are four points in common with CPW polygons and terminating polygon
704704
@test length(intersect(straight_points, termination_points)) == 4
705705

@@ -787,6 +787,43 @@ end
787787
terminate!(pa, gap=0μm, rounding=1μm)
788788
c = Cell("test", nm)
789789
render!(c, pa, GDSMeta())
790+
791+
# Termination with rounding on curve
792+
# open
793+
pa = Path(nm)
794+
turn!(pa, 90°, 100μm, Paths.CPW(10μm, 6μm))
795+
terminate!(pa, rounding=3μm)
796+
terminate!(pa, rounding=3μm, initial=true)
797+
@test iszero(α0(pa))
798+
@test p0(pa) == Point(-6, 0)μm
799+
@test α1(pa) 90°
800+
@test p1(pa) Point(100, 106)μm
801+
c = Cell("test", nm)
802+
render!(c, pa, GDSMeta())
803+
@test bounds(c).ll.y < -11μm # Drawn as though straight, extends at slight angle
804+
# short
805+
pa = Path(nm)
806+
turn!(pa, pi / 2, 100μm, Paths.CPW(10μm, 6μm))
807+
terminate!(pa, gap=0μm, rounding=3μm)
808+
terminate!(pa, gap=0μm, rounding=3μm, initial=true)
809+
@test iszero(α0(pa))
810+
@test p0(pa) == Point(0, 0)μm
811+
@test α1(pa) 90°
812+
@test p1(pa) Point(100, 100)μm
813+
c = Cell("test", nm)
814+
render!(c, pa, GDSMeta())
815+
816+
# Same with trace
817+
pa = Path(nm)
818+
turn!(pa, pi / 2, 100μm, Paths.Trace(10μm))
819+
terminate!(pa, rounding=5μm)
820+
terminate!(pa, rounding=5μm, initial=true)
821+
@test iszero(α0(pa))
822+
@test p0(pa) == Point(0, 0)μm
823+
@test α1(pa) 90°
824+
@test p1(pa) Point(100, 100)μm
825+
c = Cell("test", nm)
826+
render!(c, pa, GDSMeta())
790827
end
791828

792829
@testset "Overlays" begin

Diff for: test/test_solidmodel.jl

+10
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,16 @@ import DeviceLayout.SolidModels.STP_UNIT
189189
isapprox.([x0, y0, x1, y1], ustrip.(STP_UNIT, [x0d, y0d, x1d, y1d]), atol=1e-6)
190190
)
191191

192+
# Termination on curve is still drawn with circular arcs
193+
cs = CoordinateSystem("test", nm)
194+
pa = Path(0nm, 0nm)
195+
turn!(pa, 90°, 10μm, Paths.SimpleCPW(5μm, 2μm))
196+
terminate!(pa; rounding=2.5μm)
197+
place!(cs, pa, SemanticMeta(:test))
198+
sm = SolidModel("test"; overwrite=true)
199+
render!(sm, cs)
200+
@test length(SolidModels.gmsh.model.occ.getEntities(0)) < 20 # would be >100 points if discretized
201+
192202
# Compound segment/style
193203
cs = CoordinateSystem("test", nm)
194204
pa = Path(0nm, 0nm)

Diff for: test/tests.jl

+1
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,7 @@ end
633633
@test Paths.gap(sty) === 6000.0nm
634634
@test Paths.trace(sty) === 10000.0nm
635635
@test pa[1].sty isa Paths.CPWOpenTermination
636+
@test bounds(pa).ll -Point(pa[1].sty.gap, Paths.extent(pa[1].sty))
636637

637638
pa = Path(μm)
638639
sty = launch!(pa, trace1=4.2μm, gap1=3801nm)

0 commit comments

Comments
 (0)