Skip to content

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.

Segments

Each subtype of Segment can represent a class of parametric functions t->Point(x(t),y(t)). By "class" we mean e.g. Paths.Straight or Paths.Turn, which are used frequently. Instances of these subtypes of Segment have some captured variables to 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.

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.

Styles

Each subtype of Style describes how to render a 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 methods for render! that dispatch on different 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:

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))

Documenter.Documents.RawHTML("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n\n\n\n\n")

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 Paths.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 CellReference 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 CellReference 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 CellReference, which is how the referenced cell should be displaced with respect to the origin of a containing Cell.

The same CellReference can be attached to multiple points along multiple paths. If the cell reference is modified (e.g. rotation, origin, magnification) before rendering, 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 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 CellReferences, 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 CellReferences (like a CellArray, 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 Cells.transform(::Cell, ::Cells.CellRef).

Intersections

How to do the right thing when paths intersect is often tedious. Paths.intersect! provides a useful function to modify existing paths automatically to account for intersections according to intersection styles (Paths.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)

bridgemetas = GDSMeta.(20:30)
intersect!(Paths.Bridges(bridgemetas=bridgemetas, gap=2μm, footlength=2μm), pa1, pa2;
    interactions=[true true; false true])

c = Cell("test", nm)
render!(c, pa1, GDSMeta(0))
render!(c, pa2, GDSMeta(1))

Here's another example:

pa = Path(μm, α0=90°)
straight!(pa, 100μm, Paths.Trace(2μm))
corner!(pa, 90°, Paths.SimpleTraceCorner())
let L = 4μm
    for i in 1:50

    straight!(pa, L)
    corner!(pa, 90°, Paths.SimpleTraceCorner())
    L += 4μm
    end
end
straight!(pa, 4μm)

c = Cell("test", nm)

bridgemetas = GDSMeta.(20:30)
intersect!(Paths.Bridges(bridgemetas=bridgemetas, gap=2μm, footlength=1μm), pa)

render!(c, pa, GDSMeta(1))

Path API

Path construction

# Devices.Paths.PathType.

mutable struct Path{T<:Coordinate} <: AbstractVector{Node{T}}

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

Path(p0::Point=Point(0.0,0.0); α0=0.0)
Path(p0x::Real, p0y::Real; kwargs...)
Path(p0::Point{T}; α0=0.0) where {T<:Length}
Path(p0x::T, p0y::T; kwargs...) where {T<:Length}
Path(p0x::Length, p0y::Length; kwargs...)
Path(u::LengthUnits; α0=0.0)
Path(v::Vector{<:Node})

Convenience constructors for Path{T} object.

source

Path interrogation

# Devices.Paths.directionFunction.

direction(s, t)

Returns the angle at which some function t->Point(x(t),y(t)) is pointing.

source

# Devices.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

# Devices.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

# Devices.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

# Devices.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

# Devices.Paths.α1Function.

α1(s::Segment)

Return the last angle in a segment (calculated).

source

α1(p::Path)

Last angle of a path.

source

# Devices.Paths.style0Function.

style0(p::Path)

Style of the first segment of a path.

source

# Devices.Paths.style1Function.

style1(p::Path)

Style of the last segment of a path.

source

# Devices.Paths.discretestyle1Function.

discretestyle1(p::Path)

Returns the last-used discrete style in the path.

source

# Devices.Paths.contstyle1Function.

contstyle1(p::Path)

Returns the last user-provided continuous style in the path.

source

Path manipulation

# Devices.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

# Devices.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

# 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

# Devices.Paths.attach!Function.

attach!(p::Path, c::CellReference, t::Coordinate;
    i::Integer=length(p), location::Integer=0)
attach!(p::Path, c::CellReference, 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

# Devices.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!(s::Union{AbstractSet,AbstractVector}, itrs...)

Intersect all passed in sets and overwrite s with the result. Maintain order with arrays.

source

intersect!(sty::IntersectStyle{2}, paths::Path...;
    interactions::AbstractMatrix{Bool}=[default], reconcile=true)

Automatically modify paths to handle cases where they intersect.

interactions is a keyword argument that specifies a pair-wise interaction matrix. If an entry (i,j) is true, then intersect_pairwise!(sty, paths[i], paths[j]) will be called. By default the matrix is an upper-triangular matrix filled with true. This is typically what you want: self-intersections of each path are handled, and the intersection of two given paths is only handled once.

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

# Devices.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

# Devices.Paths.reconcile!Function.

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

# Devices.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 tuple, (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

# Devices.Paths.simplify!Function.

simplify!(p::Path, inds::UnitRange=firstindex(p):lastindex(p))

In-place version of simplify.

source

# Devices.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

# Devices.Paths.terminate!Function.

terminate!(pa::Path)

End a path with open termination (do nothing for a trace, leave a gap for a CPW).

source

# Devices.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 π/2 radians (left)
  • "r": turn by -π/2 radians (right)
  • "lrlrllrrll": do those turns in that order

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

source

Path intersection styles

# Devices.Paths.IntersectStyleType.

IntersectStyle{N}

Abstract type specifying "N-body interactions" for path intersection; N::Int typically 2.

source

# Devices.Paths.BridgesType.

Bridges(; gap, footlength, bridgemetas,
    bridgeprefix="bridges", bridgeunit=[nm or NoUnits])

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

The only required parameters to provide are:

  • gap: how much buffer to leave between the intersecting paths
  • footlength: how long in the direction of the path is the bridge landing pad
  • bridgemetas: a vector of Meta objects. The first element is for the bridge foot, subsequent elements are for increasingly higher steps of the discretized bridge. The length of this array therefore sets the vertical resolution.

For each intersection, a bridge is rendered into a new cell. The cell has database units bridgeunit which is nm by default (if gap has length units, otherwise no units). Each cell is named uniquely with a prefix given by bridgeprefix.

source

Node API

Node construction

# Devices.Paths.NodeType.

Node(a::Segment{T}, b::Style) where {T}

Create a node with segment a and style b.

source

Node methods

# Devices.Paths.previousFunction.

previous(x::Node)

Return the node before x in a doubly linked list.

source

# Devices.Paths.nextFunction.

next(x::Node)

Return the node after x in a doubly linked list.

source

# Devices.Paths.segmentFunction.

segment(x::Node)

Return the segment associated with node x.

source

# 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

# Devices.Paths.styleFunction.

style(x::Node)

Return the style associated with node x.

source

# Devices.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

# Devices.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

# Devices.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

# Devices.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

# Devices.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

# Devices.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

# Devices.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

Style API

Style construction

# Devices.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

# Devices.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

# Devices.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

# Devices.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

# Devices.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

# Devices.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

# Devices.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

# Devices.Paths.undecoratedFunction.

undecorated(s::DecoratedStyle)
undecorated(s::Style)

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

source

Abstract types

# Devices.Paths.StyleType.

abstract type Style end

How to render a given path segment.

source

# Devices.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

# Devices.Paths.DiscreteStyleType.

abstract type DiscreteStyle <: Style end

Any style that applies to segments which have zero path length.

source

Concrete types

# Devices.Paths.SimpleNoRenderType.

struct SimpleNoRender{T} <: ContinuousStyle{false}

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

source

# Devices.Paths.SimpleTraceType.

struct SimpleTrace{T<:Coordinate} <: Trace{false}
    width::T
end

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

source

# Devices.Paths.GeneralTraceType.

struct GeneralTrace{T} <: Trace{false}
    width::T
end

A single trace with variable width as a function of path length. width is callable.

source

# Devices.Paths.SimpleCPWType.

struct SimpleCPW{T<:Coordinate} <: CPW
    trace::T
    gap::T
end

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

source

# Devices.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

# Devices.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

# Devices.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

# Devices.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

# Devices.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

# Devices.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

# Devices.Paths.DecoratedStyleType.

mutable struct DecoratedStyle{T<:FloatCoordinate} <: ContinuousStyle{false}
    s::Style
    ts::Vector{Float64}
    dirs::Vector{Int}
    refs::Vector{CellReference}
end

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

source