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:

using Devices, Devices.PreferMicrons

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. These details are automatically accounted for by tweaking segment lengths and other captured segment variables just before rendering a path.

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

Path API

Path construction

# Devices.Paths.PathType.

mutable struct Path{T<:Coordinate} <: AbstractVector{Node{T}}
    p0::Point{T}
    α0::Float64
    nodes::Array{Node{T},1}
end

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

source

# Devices.Paths.PathMethod.

Path(p0::Point=Point(0.0,0.0); α0=0.0)
Path(p0x::Real, p0y::Real; kwargs...)

Path{T<:Length}(p0::Point{T}; α0=0.0)
Path{T<:Length}(p0x::T, p0y::T; kwargs...)
Path(p0x::Length, p0y::Length; kwargs...)

Path(u::LengthUnits; α0=0.0)

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{T}(array::AbstractArray{Node{T}})
pathlength{T<:Segment}(array::AbstractArray{T})
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{T}(s::Segment{T})

Return the first point in a segment (calculated).

source

p0(p::Path)

First point of a path.

source

# Devices.Paths.α0Function.

α0(s::Segment)

Return the first angle in a segment (calculated).

source

α0(p::Path)

First angle of a path.

source

# Devices.Paths.p1Function.

p1{T}(s::Segment{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{T}(p::Path{T})

Returns the last-used discrete style in the path.

source

# Devices.Paths.contstyle1Function.

contstyle1(p::Path)

Returns the last-used discrete 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)

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.adjust!Function.

adjust!(p::Path, n::Integer=1)

Adjust a path's parametric functions starting from index n. Used internally whenever segments are inserted into the path.

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!{T<:Coordinate}(p::Path{T}, α, sty::DiscreteStyle=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

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

simplify(p::Path, inds::UnitRange=1:length(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=1:length(p))

In-place version of simplify.

source

# Devices.Paths.straight!Function.

straight!{T<:Coordinate}(p::Path{T}, l::Coordinate,
    sty::ContinuousStyle=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.turn!Function.

turn!{T<:Coordinate}(p::Path{T}, α, 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!{T<:Coordinate}(p::Path{T}, s::String, r::Coordinate,
    sty::ContinuousStyle=contstyle1(p))

Turn a path p with direction coded by string s:

  • "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

Node API

Node construction

# Devices.Paths.NodeType.

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

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

# Devices.Paths.styleFunction.

style(x::Node)

Return the style associated with node x.

source

# Devices.Paths.setsegment!Function.

setsegment!(x::Node, s::Segment)

Set the segment associated with node x to s.

source

# Devices.Paths.setstyle!Function.

setstyle!(x::Node, s::Style)

Set the style associated with node x to s.

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}
    l::T
    p0::Point{T}
    α0::typeof(0.0°)
end

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

The parametric function describing the line segment is given by t -> p0 + Point(t*cos(α),t*sin(α)) where t is a length from 0 to l.

source

# Devices.Paths.TurnType.

mutable struct Turn{T} <: ContinuousSegment{T}
    α::typeof(1.0°)
    r::T
    p0::Point{T}
    α0::typeof(1.0°)
end

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

The center of the circle is given by:

cen = p0 + Point(r*cos(α0+sign(α)*π/2), r*sin(α0+sign(α)*π/2))

The parametric function over t ∈ [0,1] describing the turn is given by:

t -> cen + Point(r*cos(α0-sign(α)*π/2+α*t), r*sin(α0-sign(α)*π/2+α*t))

source

# Devices.Paths.CornerType.

mutable struct Corner{T} <: DiscreteSegment{T}
    α::Float64
    p0::Point{T}
    α0::Float64
    extent::T
    Corner(a) = new(a, Point(zero(T), zero(T)), 0.0, zero(T))
    Corner(a,b,c,d) = new(a,b,c,d)
end

A corner, or sudden kink in a path. The only parameter is the angle α of the kink. The kink 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}
    segments::Vector{Segment{T}}

    CompoundSegment(segments) = begin
        if any(x->isa(x,Corner), segments)
            error("cannot have corners in a `CompoundSegment`. You may have ",
                "tried to simplify a path containing `Corner` objects.")
        else
            new(deepcopy(Array(segments)))
        end
    end
end

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

Constructors and methods

# 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.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. All styles should implement the following methods:

  • extent
  • width

source

# Devices.Paths.ContinuousStyleType.

abstract type ContinuousStyle <: Style end

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

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.SimpleTraceType.

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

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

source

# Devices.Paths.GeneralTraceType.

struct GeneralTrace{T} <: Trace
    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
    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
    width_start::T
    width_end::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
    trace_start::T
    gap_start::T
    trace_end::T
    gap_end::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
    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
    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 <: ContinuousStyle
    styles::Vector{Style}
    grid::Vector{Float64}
end

Combines styles together, typically for use with a CompoundSegment.

  • styles: Array of styles making up the object. This is shallow-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
    s::Style
    ts::Array{Float64,1}
    dirs::Array{Int,1}
    refs::Array{CellReference,1}
end

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

source