Paths

A Paths.Path is an ordered collection of Paths.Nodes, each of which has a Paths.Segment and a Paths.Style. The nodes are linked to each other, so each node knows what the previous and next nodes are.

Because Path is a subtype of GeometryStructure, paths can be used with the transformation interface as well as the structure interface including bounds and other operations.

Segments

Segments describe the curve a Paths.Node follows. For example, Paths.Straight or Paths.Turn are used frequently. In general, each subtype of Segment can represent a class of parametric functions t->Point(x(t),y(t)).

Note

This package assumes that the parametric functions are implemented such that $\sqrt{((dx/dt)^2 + (dy/dt)^2)} = 1$. In other words, t ranges from zero to the path length of the segment.

Instances of these subtypes of Segment specify a particular path in the plane. Instances of Turn, for example, will capture an initial and final angle, a radius, and an origin. All circular turns may be parameterized with these variables.

Another useful Segment subtype is Paths.BSpline, which interpolates between two or more points with specified start and end tangents.

Styles

Each subtype of Style describes how to render a segment. They define a one-dimensional cross-section that is swept along the Segment and that can vary with arclength along the segment. You can create the most common styles using the constructors Paths.Trace (a trace with some width) and Paths.CPW (a coplanar waveguide style).

One can implement new styles by writing rendering methods (for GDSII, that would be to_polygons) that dispatch on different pairs of segment and style types. In this way, the rendering code can be specialized for the task at hand, improving performance and shrinking generated file sizes (ideally).

Tapers

As a convenience, this package provides functions for the automatic tapering of both Paths.Trace and Paths.CPW via the Paths.Taper constructor. Alternatively, one can specify the tapers concretely by calling their respective constructors.

The following example illustrates the use of automatic tapering. First, we construct a taper with two different traces surrounding it:

using DeviceLayout, FileIO;

p = Path(μm)
straight!(p, 10μm, Paths.Trace(2.0μm))
straight!(p, 10μm, Paths.Taper())
straight!(p, 10μm, Paths.Trace(4.0μm))

The taper is automatically chosen to be a Paths.Trace, with appropriate initial (2.0 μm) and final (4.0 μm) widths. The next segment shows that we can even automatically taper between the current Paths.Trace and a hard-coded taper (of concrete type Paths.TaperTrace), matching to the dimensions at the beginning of the latter taper.

straight!(p, 10μm, Paths.Taper())
straight!(p, 10μm, Paths.TaperTrace(2.0μm, 1.0μm))

As a final example, Paths.Taper can also be used in turn! segments, and as a way to automatically transition from a Paths.Taper to a Paths.CPW, or vice-versa:

turn!(p, -π / 2, 10μm, Paths.Taper())
straight!(p, 10μm, Paths.Trace(2.0μm))
straight!(p, 10μm, Paths.Taper())
straight!(p, 10μm, Paths.CPW(2.0μm, 1.0μm))

c = Cell("tapers", nm)
render!(c, p, GDSMeta(0))

Corners

Sharp turns in a path can be accomplished with Paths.corner!. Sharp turns pose a challenge to the path abstraction in that they have zero length, and when rendered effectively take up some length of the neighboring segments. Originally, the segment lengths were tweaked at render time to achieve the intended output. As other code began taking advantage of the path abstractions, the limitations of this approach became apparent.

Currently, corners are implemented such that the preceding Paths.Node is split using Paths.split near the corner when corner! is used, and a short resulting section near the corner has the style changed to Paths.SimpleNoRender. When this is followed by Paths.straight! to create the next segment, a similar operation is done, to ensure the corner is not twice-rendered. This change was necessary to be able to use Intersect.intersect! on paths with corners.

Attachments

attach! is one of the most useful functions defined in this package.

When you call attach!, you are defining a coordinate system local to somewhere along the target Path, saying that a StructureReference should be placed at the origin of that coordinate system (or slightly away from it if you want the cell to be one one side of the path or the other). The local coordinate system will rotate as the path changes orientations. The origin of the StructureReference corresponds how the referenced cell should be displaced with respect to the origin of the local coordinate system. This differs from the usual meaning of the origin of a StructureReference, which is how the referenced cell should be displaced with respect to the origin of a containing Cell.

The same StructureReference can be attached to multiple points along multiple paths. If the reference is modified (e.g. rotation, origin, magnification) before rendering to a Cell, the changes should be reflected at all attachment points. The attachment of the cell reference is not a perfect abstraction: a CellReference must ultimately live inside a Cell, but an unrendered Path does not necessarily live inside any cell. If the path is modified further before rendering, the attachment points will follow the path modifications, moving the origins of the local coordinate systems. The origin fields of the cell references do not change as the path is modified.

Attachments are implemented by introducing a Paths.DecoratedStyle, which is kind of a meta-Style: it remembers where to attach StructureReference, but how the path itself is actually drawn is deferred to a different Style object that it retains a reference to. One can repeat a DecoratedStyle with one attachment to achieve a periodic placement of StructureReference (like an ArrayReference, but along the path). Or, one long segment with a DecoratedStyle could have several attachments to achieve a similar effect.

When a Path is rendered, it is turned into Polygons living in some Cell. The attachments remain CellReferences, now living inside of a Cell and not tied to an abstract path. The notion of local coordinate systems along the path no longer makes sense because the abstract path has been made concrete, and the polygons are living in the coordinate system of the containing cell. Each attachment to the former path now must have its origin referenced to the origin of the containing cell, not to local path coordinate systems. Additionally, the references may need to rotate according to how the path was locally oriented. As a result, even if the same CellReference was attached multiple times to a path, now we need distinct CellReference objects for each attachment, as well as for each time a corresponding DecoratedStyle is rendered.

Suppose we want the ability to transform between coordinate systems, especially between the coordinate system of a referenced cell and the coordinate system of a parent cell. At first glance it would seem like we could simply define a transform function, taking the parent cell and the cell reference we are interested in. But how would we actually identify the particular cell reference we want? Looking in the tree of references for an attached CellReference will not work: distinct CellReferences needed to be made after the path was rendered, and so the particular CellReference object initially attached is not actually in the Cell containing the rendered path.

To overcome this problem, we make searching for the appropriate CellReference easier. Suppose a path with attachments has been rendered to a Cell, which is bound to symbol aaa. A CellReference referring to a cell named "bbb" was attached twice. To recall the second attachment: aaa["bbb",2] (the index defaults to 1 if unspecified). We can go deeper if we want to refer to references inside that attachment: aaa["bbb",2]["ccc"]. In this manner, it is easy to find the right CellReference to use with transformation(::DeviceLayout.GeometryStructure, ::StructureReference).

Intersections

How to do the right thing when paths intersect is often tedious. Intersect.intersect! provides a useful function to modify existing paths automatically to account for intersections according to intersection styles (Intersect.IntersectStyle). Since this is done prior to rendering, further modification can be done easily. Both self-intersections and pairwise intersections can be handled for any reasonable number of paths.

For now, one intersection style is implemented, but the heavy-lifting to add more has been done already. Here's an example (consult API docs below for further information):

pa1 = Path(μm)
turn!(pa1, -360°, 100μm, Paths.CPW(10μm, 6μm))
pa2 = Path(Point(0, 100)μm, α0=-90°)
straight!(pa2, 400μm, Paths.CPW(10μm, 6μm))
turn!(pa2, 270°, 200μm)
straight!(pa2, 400μm)

intersect!(
    Intersect.AirBridge(
        scaffold_meta=GDSMeta(3, 0),
        air_bridge_meta=GDSMeta(4, 0),
        crossing_gap=2μm,
        foot_gap=2μm,
        foot_length=2μm,
        extent_gap=2μm,
        scaffold_gap=2μm
    ),
    pa1,
    pa2
)

c = Cell("test", nm)
render!(c, pa1, GDSMeta(0))
render!(c, pa2, GDSMeta(1))
save(
    "intersect_circle.svg",
    flatten(c);
    layercolors=merge(DeviceLayout.Graphics.layercolors, Dict(1 => (0, 0, 0, 1)))
);

Here's another example:

pa = Path(μm, α0=90°)
straight!(pa, 130μm, Paths.Trace(2μm))
corner!(pa, 90°, Paths.SimpleTraceCorner())
let L = 5μm
    for i = 1:50
        straight!(pa, L)
        corner!(pa, 90°, Paths.SimpleTraceCorner())
        L += 5μm
    end
end
straight!(pa, 5μm)

intersect!(
    Intersect.AirBridge(
        scaffold_meta=GDSMeta(3, 0),
        air_bridge_meta=GDSMeta(4, 0),
        crossing_gap=2μm,
        foot_gap=2μm,
        foot_length=2μm,
        extent_gap=2μm,
        scaffold_gap=2μm
    ),
    pa
)

c = Cell("test", nm)

render!(c, pa, GDSMeta(1))
save(
    "intersect_spiral.svg",
    flatten(c);
    layercolors=merge(DeviceLayout.Graphics.layercolors, Dict(1 => (0, 0, 0, 1)))
);

Path API

Path construction

DeviceLayout.Paths.PathType
mutable struct Path{T<:Coordinate} <: GeometryStructure{T}

Type for abstracting an arbitrary styled path in the plane. Iterating returns Paths.Node objects.

Convenience constructors for Path{T} object:

Path{T}(p0::Point=zero(Point{T}), α0::typeof(1.0°)=0.0°, metadata::Meta=UNDEF_META)
Path{T}(name::String, p0::Point=zero(Point{T}), α0::Float64=0.0, metadata::Meta=UNDEF_META)
Path(p0::Point=zero(Point{typeof(1.0UPREFERRED)}); α0=0.0, name=uniquename("path"), metadata=UNDEF_META)
Path(p0x::Coordinate, p0y::Coordinate; α0=0.0, name=uniquename("path"), metadata=UNDEF_META)
Path(u::CoordinateUnits; α0=0.0, name=uniquename("path"), metadata=UNDEF_META)
Path(v::Vector{Node{T}}; name=uniquename("path"), metadata=UNDEF_META) where {T}
source

Path interrogation

DeviceLayout.Paths.pathlengthFunction
pathlength(p::Path)
pathlength(array::AbstractArray{Node{T}}) where {T}
pathlength(array::AbstractArray{T}) where {T<:Segment}
pathlength(node::Node)

Physical length of a path. Note that length will return the number of segments in a path, not the physical length of the path.

source
DeviceLayout.Paths.p0Function
p0(s::Segment{T}) where {T}

Return the first point in a segment (calculated).

source
p0(p::Path)

First point of a path, returns p.p0.

source
p0(r::Route)

First point of a route, returns r.p0.

source
DeviceLayout.Paths.α0Function
α0(s::Segment)

Return the first angle in a segment (calculated).

source
α0(p::Path)

First angle of a path, returns p.α0.

source
α0(r::Route)

First angle of a route, returns r.α0.

source
DeviceLayout.Paths.p1Function
p1(s::Segment{T}) where {T}

Return the last point in a segment (calculated).

source
p1(p::Path)

Last point of a path.

source
p1(r::Route)

Last point of a route, returns r.p1.

source
DeviceLayout.Paths.style1Function
style1(p::Path)

Undecorated style of the last user-provided (non-virtual) segment of a path.

Throws an error if the path is empty.

source
DeviceLayout.Paths.contstyle1Function
contstyle1(p::Path)

Return the undecorated last user-provided (non-virtual) continuous style in the path.

Throws an error if the path is empty.

source

Path manipulation

DeviceLayout.Paths.setp0!Function
setp0!(s::Straight, p::Point)

Set the p0 of a straight segment.

source
setp0!(s::Turn, p::Point)

Set the p0 of a turn.

source
setp0!(b::BSpline, p::Point)

Translate the interpolated segment so its initial point is p.

source
DeviceLayout.Paths.setα0!Function
setα0!(s::Straight, α0′)

Set the angle of a straight segment.

source
setα0!(s::Turn, α0′)

Set the starting angle of a turn.

source
setα0!(b::BSpline, α0′)

Set the starting angle of an interpolated segment.

source
Base.append!Method
append!(p::Path, p′::Path; reconcile=true)

Given paths p and p′, path p′ is appended to path p. The p0 and initial angle of the first segment from path p′ is modified to match the last point and last angle of path p.

source
DeviceLayout.Paths.attach!Method
attach!(p::Path, c::GeometryReference, t::Coordinate;
    i::Integer=length(p), location::Integer=0)
attach!(p::Path, c::GeometryReference, t;
    i::Integer=length(p), location=zeros(Int, length(t)))

Attach c along a path. The second method permits ranges or arrays of t and location to be specified (if the lengths do not match, location is cycled).

By default, the attachment(s) occur at t ∈ [zero(pathlength(s)),pathlength(s)] along the most recent path segment s, but a different path segment index can be specified using i. The reference is oriented with zero rotation if the path is pointing at 0°, otherwise it is rotated with the path.

The origin of the cell reference tells the method where to place the cell with respect to a coordinate system that rotates with the path. Suppose the path is a straight line with angle 0°. Then an origin of Point(0.,10.) will put the cell at 10 above the path, or 10 to the left of the path if it turns left by 90°.

The location option is for convenience. If location == 0, nothing special happens. If location == -1, then the point of attachment for the reference is on the leftmost edge of the waveguide (the rendered polygons; the path itself has no width). Likewise if location == 1, the point of attachment is on the rightmost edge. This option does not automatically rotate the cell reference, apart from what is already done as described in the first paragraph. You can think of this option as setting a special origin for the coordinate system that rotates with the path. For instance, an origin for the cell reference of Point(0.,10.) together with location == -1 will put the cell at 10 above the edge of a rendered (finite width) path with angle 0°.

source
DeviceLayout.Paths.bspline!Function
bspline!(p::Path{T}, nextpoints, α_end, sty::Style=contstyle1(p), endpoints_speed=2500μm)

Add a BSpline interpolation from the current endpoint of p through nextpoints.

The interpolation reaches nextpoints[end] making the angle α_end with the positive x-axis. The endpoints_speed is "how fast" the interpolation leaves and enters its endpoints. Higher speed means that the start and end angles are approximately α1(p) and α_end over a longer distance.

source
DeviceLayout.Paths.corner!Function
corner!(p::Path, α, sty::Style=discretestyle1(p))

Append a sharp turn or "corner" to path p with angle α.

The style chosen for this corner, if not specified, is the last DiscreteStyle used in the path.

source
Base.intersect!Function
intersect!(sty::IntersectStyle, paths::Path...;
    intersections=prepared_intersections(paths...))

Automatically modify paths to handle cases where they intersect.

Paths later in the argument list cross over paths earlier in the argument list. For self-intersection (path with itself), segments later in a path will cross over segments earlier in the same path (perhaps later this will be configurable by an option).

source
DeviceLayout.Paths.launch!Function
launch!(p::Path; kwargs...)

Add a coplanar-waveguide "launcher" structure to p.

If p is empty, start the path with a launcher; otherwise, terminate with a launcher.

This method exists mainly for use in demonstrations. The launcher design is not optimized for microwave properties.

Keywords:

  • extround = 5.0μm: Rounding of the "back" of the pad and external ground plane "behind" the launcher
  • trace0 = 300.0μm: Trace width of the pad
  • trace1 = 10.0μm: Trace width of the launched CPW
  • gap0 = 150.0μm: Gap width of the pad
  • gap1 = 6.0μm: Gap width of the final CPW
  • flatlen = 250.0μm: Length of the pad
  • taperlen = 250.0μm: Length of the taper between pad and launched CPW
source
DeviceLayout.Paths.meander!Function
meander!(p::Path, len, straightlen, r, α)

Alternate between going straight with length straightlen and turning with radius r and angle α. Each turn goes the opposite direction of the previous. The total length is len. Useful for making resonators.

The straight and turn segments are combined into a CompoundSegment and appended to the path p.

source
meander!(p::Path, endpoint::Point, len, nseg::Int, r, sty::Paths.Style = style1(p); offset = 0)

Another meander method, this one extends Path p from its current end-point to meet endpoint, such that the resulting final total path length will be len.

  • nseg: the number of U-turns in the meander will be 2*nseg
source
DeviceLayout.Paths.overlay!Function
overlay!(path::Path, oversty::Style, metadata::DeviceLayout.Meta; i::Int=length(path))

Apply the style oversty in layer metadata on top of the segment at path[i].

By default, the overlay is applied to the most recent segment.

Overlays generally count as "decorations". For example, they appear in refs(path) and not elements(path). They are removed by undecorated(sty), and they are ignored when choosing the default style for continuing a Path with methods like straight!.

Overlay styles should not be generic Paths.Tapers, since they can't see neighboring styles to resolve the taper style.

You can use overlay! after attach!, in which case the overlay is applied to the style underlying the DecoratedStyle that holds the attachments.

source
DeviceLayout.Paths.reconcile!Function
reconcile!(path::Path, endpoint::Point, end_direction, rule::RouteRule, waypoints, waydirs;
    initialize_waydirs = false)

Ensure that path can be routed to endpoint at end_direction using rule, waypoints, waydirs, or throw an error.

Does nothing for a generic RouteRule. Subtypes of RouteRule may implement specialized methods to do their own validation when route! is called.

May insert inferred constraints to waypoints and waydirs to allow the path to be drawn leg-by-leg. For example, reconcile! with rule::StraightAnd90, no waypoints, and α1(path) == end_direction will insert a waypoint halfway between p1(path) and endpoint, allowing two successive StraightAnd90 legs with opposite bends.

source
reconcile!(p::Path, n::Integer=1)

Reconcile all inconsistencies in a path starting from index n. Used internally whenever segments are inserted into the path, but can be safely used by the user as well.

source
DeviceLayout.Paths.simplifyFunction
simplify(p::Path, inds::UnitRange=firstindex(p):lastindex(p))

At inds, segments of a path are turned into a CompoundSegment and styles of a path are turned into a CompoundStyle. The method returns a Paths.Node, (segment, style).

  • Indexing the path becomes more sane when you can combine several path segments into one logical element. A launcher would have several indices in a path unless you could simplify it.
  • You don't need to think hard about boundaries between straights and turns when you want a continuous styling of a very long path.
source
DeviceLayout.Paths.straight!Function
straight!(p::Path, l::Coordinate, sty::Style=contstyle1(p))

Extend a path p straight by length l in the current direction. By default, we take the last continuous style in the path.

source
DeviceLayout.Paths.terminate!Function
terminate!(pa::Path{T}; gap=Paths.terminationlength(pa), rounding=zero(T), initial=false) where {T}

End a Paths.Path with a termination.

If the preceding style is a CPW, this is a "short termination" if iszero(gap) and is an "open termination" with a gap of gap otherwise, defaulting to the gap of the preceding CPW.

Rounding of corners may be specified with radius given by rounding. Rounding keeps the trace length constant by removing some length from the preceding segment and adding a rounded section of equivalent maximum length.

Terminations can be applied on curves without changing the underlying curve. If you add a segment after a termination, it will start a straight distance gap away from where the original curve ended. However, rounded terminations are always drawn as though straight from the point where rounding starts, slightly before the end of the curve. This allows the rounded corners to be represented as exact circular arcs.

If the preceding style is a trace, the termination only rounds the corners at the end of the segment or does nothing if iszero(rounding).

If initial, the termination is appended before the beginning of the Path.

source
DeviceLayout.Paths.turn!Function
turn!(p::Path, α, r::Coordinate, sty::Style=contstyle1(p))

Turn a path p by angle α with a turning radius r in the current direction. Positive angle turns left. By default, we take the last continuous style in the path.

source
turn!(p::Path, str::String, r::Coordinate, sty::Style=contstyle1(p))

Turn a path p with direction coded by string str:

  • "l": turn by 90° (left)
  • "r": turn by -90° (right)
  • "lrlrllrrll": do those turns in that order

By default, we take the last continuous style in the path.

source

Path intersection styles

DeviceLayout.Intersect.AirBridgeType
AirBridge(; crossing_gap, foot_gap, foot_length, extent_gap, scaffold_gap,
    scaffold_meta=SemanticMeta(:scaffold), air_bridge_meta=SemanticMeta(:air_bridge), 
    name="airbridge", unit=[nm or NoUnits])

Style for automatically leaping one path over another with scaffolded air bridges.

Parameters ("lengths" are along path direction, "extents" are transverse from the center)

  • name: Prefix for unique CoordinateSystem name
  • scaffold_meta: Scaffold layer metadata
  • air_bridge_meta: Air bridge layer metadata
  • crossing_gap: Extra length beyond extent of path being crossed (on one side)
  • foot_gap: Extra length beyond original path termination before bridge foot starts
  • foot_length: Length of bridge foot
  • extent_gap: Gap between edge of bridge trace and edge of original path trace
  • scaffold_gap: Gap between edge of original trace and edge of scaffold
  • rounding: Rounding radius for scaffold and air bridge rectangles
  • unit: Coordinate system unit
source

Node API

Node construction

Node methods

Base.splitMethod
split(n::Node, x::Coordinate)
split(n::Node, x::AbstractVector{<:Coordinate}; issorted=false)

Splits a path node at position(s) x along the segment, returning a path. If issorted, don't sort x first (otherwise required for this to work).

A useful idiom, splitting and splicing back into a path: splice!(path, i, split(path[i], x))

source
DeviceLayout.styleFunction
style(x::Node)

Return the style associated with node x.

source
style(styled_ent::StyledEntity)

Return the GeometryEntityStyle of styled_ent.

source
DeviceLayout.Paths.setsegment!Function
setsegment!(n::Node, s::Segment)

Set the segment associated with node n to s. If reconcile, then modify fields as appropriate for internal consistency (possibly including other linked nodes).

source
DeviceLayout.Paths.setstyle!Function
setstyle!(n::Node, s::Style; reconcile=true)

Set the style associated with node n to s. If reconcile, then modify fields as appropriate for internal consistency.

source

Segment API

Abstract types

DeviceLayout.Paths.SegmentType
abstract type Segment{T<:Coordinate} end

Path segment in the plane. All Segment objects should have the implement the following methods:

  • pathlength
  • p0
  • α0
  • setp0!
  • setα0!
  • α1
source

Concrete types

DeviceLayout.Paths.StraightType
mutable struct Straight{T} <: ContinuousSegment{T}

A straight line segment is parameterized by its length. It begins at a point p0 with initial angle α0.

source
DeviceLayout.Paths.TurnType
mutable struct Turn{T} <: ContinuousSegment{T}

A circular turn is parameterized by the turn angle α and turning radius r. It begins at a point p0 with initial angle α0.

source
DeviceLayout.Paths.CornerType
mutable struct Corner{T} <: DiscreteSegment{T}

A corner, or sudden kink in a path. The only parameter is the angle α of the corner. The corner begins at a point p0 with initial angle α0. It will also end at p0, since the corner has zero path length. However, during rendering, neighboring segments will be tweaked slightly so that the rendered path is properly centered about the path function (the rendered corner has a finite width).

source
DeviceLayout.Paths.CompoundSegmentType
struct CompoundSegment{T} <: ContinuousSegment{T}

Consider an array of segments as one contiguous segment. Useful e.g. for applying styles, uninterrupted over segment changes. The array of segments given to the constructor is copied and retained by the compound segment.

Note that Corners introduce a discontinuity in the derivative of the path function, and are not allowed in a CompoundSegment.

source
DeviceLayout.Paths.BSplineType
mutable struct BSpline{T} <: ContinuousSegment{T}

Interpolate between points p with start and end tangents t0, t1.

Computes the interpolated coordinate r(t) as a function of a dimensionless parameter t, using b-spline interpolation knots spaced uniformly in t. That is, r(0) == p[1] and r(1) == p[end], and generally r((i-1)*tinc) == p[i] where tinc is the knot value spacing 1/(length(p)-1).

A BSpline instance itself can be called as a parametric function of a length that ranges from zero to the total path length.

source

Style API

Style construction

DeviceLayout.Paths.TraceType
Trace(width)
Trace(width::Coordinate)
Trace(width_start::Coordinate, width_end::Coordinate)

Constructor for Trace styles. Automatically chooses SimpleTrace, GeneralTrace, and TaperTrace as appropriate.

source
DeviceLayout.Paths.CPWType
CPW(trace::Coordinate, gap::Coordinate)
CPW(trace, gap::Coordinate)
CPW(trace::Coordinate, gap)
CPW(trace, gap)
CPW(trace_start::Coordinate, gap_start::Coordinate, trace_end::Coordinate, gap_end::Coordinate)

Constructors for CPW styles. Automatically chooses between SimpleCPW, GeneralCPW, or TaperCPW styles as appropriate.

source
DeviceLayout.Paths.TaperType
Taper()

Constructor for generic Taper style. Will automatically create a linearly tapered region between an initial CPW or Trace and an end CPW or Trace of different dimensions.

source
DeviceLayout.Paths.StrandsType
Strands(offset::Coordinate, width::Coordinate, spacing::Coordinate, num::Int)
Strands(offset, width::Coordinate, spacing::Coordinate, num::Int)
Strands(offset::Coordinate, width, spacing::Coordinate, num::Int)
Strands(offset::Coordinate, width::Coordinate, spacing, num::Int)
Strands(offset::Coordinate, width, spacing, num::Int)
Strands(offset, width::Coordinate, spacing, num::Int)
Strands(offset, width, spacing::Coordinate, num::Int)
Strands(offset, width, spacing, num::Int)

              example for num = 2
|||     |||                         |||     |||
|||     |||                         |||     |||
|||     |||                         |||     |||
|||     |||                         |||     |||
<-><---><-><-----------|-----------><-><---><->
 w   s   w    offset                 w   s   w

Constructors for Strands styles. Automatically chooses between SimpleStrandsor GeneralStrands styles as appropriate.

source
DeviceLayout.Paths.NoRenderType
struct NoRender <: Style

Style for suppressing rendering. When asked, it will claim to have zero width. Converts to a continuous or discrete style as needed by straight!, turn!, corner!, etc.

source

Style manipulation

DeviceLayout.Paths.pinFunction
pin(s::ContinuousStyle; start=nothing, stop=nothing)

Imagine having a styled segment of length L split into two, the first segment having length l and the second having length L-l. In all but the simplest styles, the styles need to be modified in order to maintain the rendered appearances. A style appropriate for the segment of length l (L-l) is given by pin(s; stop=l) (pin(s; start=l)).

source
DeviceLayout.Paths.translateFunction
translate(s::ContinuousStyle, x)

Creates a style s′ such that all properties f(s′, t) == f(s, t+x). Basically, advance the style forward by path length x.

source
DeviceLayout.Paths.undecoratedFunction
undecorated(s::DecoratedStyle)
undecorated(s::Style)

Return the underlying, undecorated style if decorated; otherwise just return the style.

source

Abstract types

DeviceLayout.Paths.ContinuousStyleType
abstract type ContinuousStyle{CanStretch} <: Style end

Any style that applies to segments which have non-zero path length. For most styles, CanStretch == false. An example of an exception is a linear taper, e.g. Paths.TaperTrace, where you fix the starting and ending trace widths and let the segment length dictate the abruptness of the transition (hence, stretching the style). Concrete types inheriting from ContinuousStyle{true} should have a length field as the last field of their structure.

source

Concrete types

DeviceLayout.Paths.SimpleNoRenderType
struct SimpleNoRender{T} <: ContinuousStyle{false}
SimpleNoRender(width::T; virtual=false)

A style that inhibits path rendering, but pretends to have a finite width for Paths.attach!.

May be "virtual", in which case it is ignored when looking up the last style of a Path with laststyle or contstyle1, or when extending a Path with (for example) straight!.

source
DeviceLayout.Paths.SimpleCPWType
struct SimpleCPW{T <: Coordinate} <: CPW{false}
    trace::T
    gap::T
end

A CPW with fixed trace and gap as a function of path length.

source
DeviceLayout.Paths.GeneralCPWType
struct GeneralCPW{S, T} <: CPW{false}
    trace::S
    gap::T
end

A CPW with variable trace and gap as a function of path length. trace and gap are callable.

source
DeviceLayout.Paths.TaperTraceType
struct TaperTrace{T<:Coordinate} <: Trace{true}
    width_start::T
    width_end::T
    length::T
end

A single trace with a linearly tapered width as a function of path length.

source
DeviceLayout.Paths.TaperCPWType
struct TaperCPW{T<:Coordinate} <: CPW{true}
    trace_start::T
    gap_start::T
    trace_end::T
    gap_end::T
    length::T
end

A CPW with a linearly tapered trace and gap as a function of path length.

source
DeviceLayout.Paths.SimpleStrandsType
struct SimpleStrands{T<:Coordinate} <: Strands{false}
    offset::T
    width::T
    spacing::T
    num::Int
end

              example for num = 2
|||     |||                         |||     |||
|||     |||                         |||     |||
|||     |||                         |||     |||
|||     |||                         |||     |||
<-><---><-><-----------|-----------><-><---><->
 w   s   w    offset                 w   s   w

Strands with fixed center offset, width, and spacing as a function of path length.

source
DeviceLayout.Paths.GeneralStrandsType
struct GeneralStrands{S,T,U} <: Strands{false}
    offset::S
    width::T
    spacing::U
    num::Int
end

              example for num = 2
|||     |||                         |||     |||
|||     |||                         |||     |||
|||     |||                         |||     |||
|||     |||                         |||     |||
<-><---><-><-----------|-----------><-><---><->
 w   s   w    offset                 w   s   w

Strands with variable center offset, width, and spacing as a function of path length. offset, width, and spacing are callable.

source
DeviceLayout.Paths.CompoundStyleType
struct CompoundStyle{T<:FloatCoordinate} <: ContinuousStyle{false}
    styles::Vector{Style}
    grid::Vector{T}
end

Combines styles together, typically for use with a CompoundSegment.

  • styles: Array of styles making up the object. This is deep-copied by the outer constructor.
  • grid: An array of t values needed for rendering the parameteric path.
source
DeviceLayout.Paths.DecoratedStyleType
mutable struct DecoratedStyle{T<:FloatCoordinate} <: ContinuousStyle{false}
    s::Style
    ts::Vector{Float64}
    dirs::Vector{Int}
    refs::Vector{GeometryReference}
end

Style with decorations, like structures periodically repeated along the path, etc.

source