Routes
A Paths.Route implicitly defines a Path between two points, following a Paths.RouteRule, with a specified start and end direction. It's a building block for "interactive autorouting". For simple cases, it lets you define a path between two points without having to do any geometric calculations yourself. In more complicated cases, you can provide additional waypoint constraints to guide it.
To draw a Path from a Route, you can use Path(r::Route, sty).
More often, you won't work directly with the Route type but will instead use route! to extend an existing path to an endpoint according to the rules you specify.
Reference
DeviceLayout.Paths.Route — Typestruct Route{T<:RouteRule, S<:Coordinate}
Route(rule, startpoint::Point{S}, endpoint::Point, start_direction, end_direction; waypoints=Point{S}[], waydirs=[])
Route(rule, path0::Path, endpoint::Point, end_direction)A Route implicitly defines a Path between two points with given start and end directions.
Use Path(r::Route, sty::Paths.Style) to create the explicit path.
Contains a RouteRule to determine how the Path should be drawn.
May contain waypoints and waydirs that additionally constrain the route. The RouteRule determines how these are handled, by default routing waypoint-to-waypoint such that the path starts at p0, passes through each point in waypoints in order, and then ends at p1.
If waydirs is not nothing, it should have the same length as waypoints. If waydirs is provided and is not ignored by the RouteRule (check the specific rule documentation), then waypoints[i] will be reached with the path pointing along waydirs[i].
Route rules
DeviceLayout.Paths.RouteRule — Typeabstract type RouteRule endControls how a Route is turned into a Path.
A RouteRule may contain parameters or be used only for dispatch. It should implement one of the following two methods:
_route!(p::Path, p1::Point, α1, rule::MyRouteRule, sty, waypoints, waydirs)
_route_leg!(p::Path, next::Point, rule::MyRouteRule,
sty::Paths.Style=Paths.contstyle1(p))It may also implement
reconcile!(path::Path, endpoint::Point,
end_direction, rule::RouteRule, waypoints, waydirs; initialize_waydirs)If only _route_leg! is implemented, then a Path drawn from r::Route with MyRouteRule will first call reconcile! to validate constraints and insert waypoints if necessary. (The default implementation of reconcile! does nothing.) The Path will then be routed waypoint-to-waypoint such that the path starts at r.p0, passes through each point in r.waypoints in order, and then ends at r.p1, ignoring waydirs. Alternatively, _route! can be implemented to use r.waypoints and/or r.waydirs all at once as desired.
DeviceLayout.Paths.BSplineRouting — TypeBase.@kwdef struct BSplineRouting <: RouteRule
endpoints_speed = 2500μm
auto_speed = false
endpoints_curvature = nothing
auto_curvature = false
endSpecifies rules for routing from one point to another using BSplines.
Ignores waydirs.
DeviceLayout.Paths.StraightAnd90 — TypeBase.@kwdef struct StraightAnd90 <: RouteRule
min_bend_radius = 200μm
max_bend_radius = Inf*μm
end
StraightAnd90(r) = StraightAnd90(min_bend_radius=r, max_bend_radius=r)Specifies rules for routing from one point to another using straight segments and 90° bends.
Can be used with no waydirs if each waypoint is reachable from the previous with a single turn and the endpoint is reachable with a single turn or two turns in opposite directions.
If waydirs are used, then any waypoint may be reachable with two turns in opposite directions if that satisfies the corresponding waydirection.
DeviceLayout.Paths.StraightAnd45 — TypeBase.@kwdef struct StraightAnd45 <: RouteRule
min_bend_radius = 200μm
max_bend_radius = Inf*μm
end
StraightAnd45(r) = StraightAnd45(min_bend_radius=r, max_bend_radius=r)Specifies rules for routing from one point to another using using straight segments and 45° bends.
Can be used with no waydirs if each waypoint is reachable from the previous with a single turn and the endpoint is reachable with one or two turns.
If waydirs are used, then any waypoint may be reachable with two turns if that satisfies the corresponding waydirection.
DeviceLayout.Paths.CompoundRouteRule — TypeCompoundRouteRule <: RouteRule
CompoundRouteRule(rules::Vector{RouteRule}, leg_length::Vector{Int}=ones(length(rules)))Specifies a sequence of rules for routing from one point to another, where rules[i] is used to route through the next leg_length[i] waypoints and/or endpoint.
DeviceLayout.Paths.SingleChannelRouting — Typestruct SingleChannelRouting{T <: Coordinate} <: AbstractChannelRouting
SingleChannelRouting(ch::RouteChannel, transition_rule::RouteRule, margin::T)
SingleChannelRouting(ch::RouteChannel, transition_rules, margins)A RouteRule for guiding routed paths along tracks in a Paths.RouteChannel.
Tracks
"Tracks" are offsets of the channel's path, with equal spacing between each other and the extents of the channel's trace width. Tracks are ordered from left to right when facing along the channel. For example, track 1 is the top track (most positive offset) for a channel directed along the positive x axis, while the highest track index is its bottom track.
The user manually assigns tracks to paths that will be routed with rule::SingleChannelRouting using Paths.set_track!(rule, path, track_idx) for each path, prior to calling route!(path, ...). Because the track offset depends on the total number of tracks, and the number of tracks is determined by the maximum track index of any path added to rule, all paths should be assigned tracks before any route! call.
If used for schematic routing, the track is supplied as a keyword argument, defaulting to a new track added at the bottom of the channel: route!(g::SchematicGraph, rule, ...; track=num_tracks(rule)+1).
Routing
A path routed from p0 to p1 using this rule will enter the channel at the channel's closest point to p0 and exit at the closest point to p1 if margin is zero. For nonzero margin, the entry and exit points are each shifted towards the other along the channel by margin, allowing more space for the transitions into and out of the channel.
The middle "tracked" section is offset from the channel's center line according to the path's track, the maximum track assigned to any path by the rule, and the channel width.
The path is routed from p0 to the tracked section and from the tracked section to p1 using transition_rule.
Transition rules and margins can also be supplied as tuples to the constructor to allow different parameters for entry and exit transitions.
DeviceLayout.Paths.RouteChannel — TypeRouteChannel{T} <: AbstractComponent{T}
RouteChannel(pa::Path)A channel that routes can be guided through along parallel tracks.
Used in route! with Paths.SingleChannelRouting.
The Path used to construct a RouteChannel should use Trace styles only.
A RouteChannel is an AbstractComponent with the same hooks as its Path and an empty geometry.
Route drawing
Calling Path(r::Route, sty::Style) creates a new Path at p0(r), then extends it to p1(r) using route!. The default implementation of route!, for a generic RouteRule, first calls reconcile! to validate and modify waypoints as necessary. It then calls _route! to draw the path, which by default calls _route_leg! to each waypoint in order. (A "leg" is an informal abstraction describing the "unit" that the RouteRule works with, which may be more than one Paths.Segment. For example, each leg in StraightAnd90 routing has a single 90-degree bend with up to one straight segment on either side, or a single straight segment if no bend is necessary.)
DeviceLayout.Paths.Path — MethodPath(r::Route, sty)The explicit Path defined by r, with style sty.
DeviceLayout.Paths.route! — Functionfunction route!(path::Path{S}, p_end::Point, α_end, rule::RouteRule, sty=Paths.contstyle1(path);
waypoints=Point{S}[], waydirs=Vector{typeof(1.0°)}(undef, length(waypoints)))) where {S}Extend path to p_end with arrival angle α_end according to RouteRule. The default implementation is
reconcile!(path, p_end, α_end, rule, waypoints, waydirs)
_route!(path, p_end, α_end, rule, sty, waypoints, waydirs)followed by checking that the endpoint was successfully reached.
route!(g::SchematicGraph, rule::RouteRule,
nodehook1::Pair{ComponentNode,Symbol}, nodehook2::Pair{ComponentNode,Symbol},
sty, meta;
waypoints=[], waydirs=[], global_waypoints=false,
name=uniquename("r_$(component(nodehook1.first).name)_$(component(nodehook2.first).name)"),
kwargs...)
route!(g::SchematicGraph, rule::RouteRule, node1::ComponentNode, nodehook2::Pair{ComponentNode,Symbol}, sty, meta; kwargs...)
route!(g::SchematicGraph, rule::RouteRule, nodehook1::Pair{ComponentNode,Symbol}, node2::ComponentNode, sty, meta; kwargs...)
route!(g::SchematicGraph, rule::RouteRule, node1::ComponentNode, node2::ComponentNode, sty, meta; kwargs...)Creates a RouteComponent with given style sty and metadata meta, and fuses it between the specified nodes and hooks in g.
Returns the resulting ComponentNode in g.
Example usage: route!(g, BSplineRouting(), zline_node=>:feedline, z_launcher_node=>:line, Paths.CPW(10μm, 6μm), GDSMeta(1, 2))
If one or both hook symbols are not specified, then matching_hook or matching_hooks will be used to attempt to automatically find the correct hook or hooks.
The route will have start and endpoints at the origin until a method like plan! is called. waypoints and waydirs are in component-local coordinates (unless global_waypoints is true), and rule determines how they will be used.
Additional keyword arguments will become vertex properties for the RouteComponent's node.
name should be unique.
DeviceLayout.Paths.reconcile! — Methodreconcile!(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.
Route inspection
A Route supports endpoint inspection much like a Path does:
DeviceLayout.Paths.p0 — Methodp0(s::Segment{T}) where {T}Return the first point in a segment (calculated).
p0(r::Route)First point of a route, returns r.p0.
DeviceLayout.Paths.α0 — Methodα0(s::Segment)Return the first angle in a segment (calculated).
α0(r::Route)First angle of a route, returns r.α0.
DeviceLayout.Paths.p1 — Methodp1(s::Segment{T}) where {T}Return the last point in a segment (calculated).
p1(r::Route)Last point of a route, returns r.p1.
DeviceLayout.Paths.α1 — Methodα1(r::Route)Last angle of a route, returns r.α1.
Examples
Channel routing
RouteChannels offer a way to run routes in parallel, with routes joining or leaving a channel at different points. Using Paths.SingleChannelRouting, we can set the "track" (a curve offset from the channel centerline) for each route to follow through the channel, as well as some rules for joining and leaving the channel from route start and end points. Here's a basic example with a straight channel:
using DeviceLayout, .PreferredUnits, FileIO
import DeviceLayout.Graphics: inch
# Define start and end points for various routes
p0s = [
Point(100.0, 200.0)μm, # Enter and exit from top
Point(50.0, 150)μm, # Enter from top, exit from right
Point(-100.0, -100.0)μm, # Enter from lower left, exit from right
Point(600.0, -150)μm, # Enter from bottom, exit from right
Point(100.0, -200.0)μm # Enter and exit from bottom
]
p1s = [
Point(900.0, 200.0)μm,
Point(1100.0, 150.0)μm,
Point(1200.0, 50.0)μm,
Point(1100.0, -150.0)μm,
Point(400.0, -200.0)μm
]
# Create channel
channel_path = Path()
straight!(channel_path, 1mm, Paths.Trace(0.1mm))
channel = Paths.RouteChannel(channel_path)
# Initialize paths
paths = [Path(p) for p in p0s]
# Define route rule
transition_rule = Paths.StraightAnd90(25μm) # Manhattan with 25μm bend radius
margin = 50.0μm # Room for bends between endpoints and channel
rule = Paths.SingleChannelRouting(channel, transition_rule, margin)
# Set tracks
tracks = [1, 2, 3, 4, 4] # Last two share a track
setindex!.(Ref(rule.segment_tracks), tracks, paths)
# Draw routes
for (pa, p1) in zip(paths, p1s)
route!(pa, p1, 0.0°, rule, Paths.Trace(2μm))
end
c = Cell("test")
render!.(c, paths, GDSMeta())
render!(c, channel_path, GDSMeta(1))
save("straight_channel.svg", flatten(c); width=6inch, height=2inch);We can also have curved channels, like the BSpline-based example below. For the transition rule, StraightAnd90 would no longer work for the paths that join the channel at an angle, so we use BSplineRouting instead. We also enable auto_speed and auto_curvature on that rule to help smooth out the B-splines and maintain continuous curvature.
# Create BSpline channel
channel_path = Path()
bspline!(
channel_path,
[Point(0.5, 0.5)mm, Point(1.0mm, 0.0μm)],
0°,
Paths.Trace(0.1mm),
auto_speed=true,
auto_curvature=true
)
channel = Paths.RouteChannel(channel_path)
# Initialize paths
paths = [Path(p) for p in p0s]
# Define route rule
transition_rule = Paths.BSplineRouting(auto_speed=true, auto_curvature=true)
margin = 50.0μm
rule = Paths.SingleChannelRouting(channel, transition_rule, margin)
# Set tracks
tracks = [1, 2, 3, 4, 4] # Last two share a track
setindex!.(Ref(rule.segment_tracks), tracks, paths)
# Draw routes
for (pa, p1) in zip(paths, p1s)
route!(pa, p1, 0.0°, rule, Paths.Trace(2μm))
end
c = Cell("test")
render!.(c, paths, GDSMeta())
render!(c, channel_path, GDSMeta(1))
save("bspline_channel.svg", flatten(c); width=6inch, height=4inch);Channels can also have variable width, like the example below using the TaperTrace style on a compound segment consisting of four turns.
# Create tapered, composite channel
channel_path = Path()
turn!(channel_path, 90°, 0.25mm, Paths.Trace(0.1mm))
turn!(channel_path, -90°, 0.25mm)
turn!(channel_path, -90°, 0.25mm)
turn!(channel_path, 90°, 0.25mm)
simplify!(channel_path)
setstyle!(channel_path[1], Paths.TaperTrace(0.1mm, 0.05mm))
channel = Paths.RouteChannel(channel_path)
# Initialize paths
paths = [Path(p) for p in p0s]
# Define route rule
transition_rule = Paths.BSplineRouting(auto_speed=true, auto_curvature=true)
margin = 50.0μm
rule = Paths.SingleChannelRouting(channel, transition_rule, margin)
# Set tracks
tracks = [1, 2, 3, 4, 4] # Last two share a track
setindex!.(Ref(rule.segment_tracks), tracks, paths)
# Draw routes
for (pa, p1) in zip(paths, p1s)
route!(pa, p1, 0.0°, rule, Paths.Trace(2μm))
end
c = Cell("test")
render!.(c, paths, GDSMeta())
render!(c, channel_path, GDSMeta(1))
save("compound_channel.svg", flatten(c); width=6inch, height=4inch);