default_input : Input
Route
Route :: # (opaque)
Placement-independent routing for an already positioned, sized graph.
layout produces deterministic axis-aligned polylines, honors sparse
attachment rules, separates parallel edges into stable tracks, gives
self-loops exterior paths, and returns one anchor for every sparse edge
label in label input order. Every label anchor lies on its final edge path;
labels may be centered on an edge or placed near either end, so relationship
names, roles, and multiplicities remain visually attached without putting
text or styling into the geometry API. Group
attachments are exact boundary crossings: routes approach and leave them
perpendicularly instead of following the group outline.
Sparse waypoint rules are exact: a routed edge passes through every listed
point in order. Sparse guide rules are preferences supplied by placement;
the router discards a guided candidate when it would lose clearance,
reverse, self-intersect, or approach a node from the wrong direction. The
router never moves or normalizes caller geometry; bounds.x and bounds.y
report the actual drawing origin.
Nodes use their rectangular size as their boundary by default. A sparse boundary rule makes attachment points follow an ellipse inside the same sized box; routing still treats that box as the node's obstacle. Every ordinary route first travels outward from its selected attachment to the node's inflated routing boundary before it may turn. Edges sharing an exact attachment share that short terminal throat and then receive stable lanes. Refinement never moves these terminal corridors, so a route cannot return beneath an incident node after leaving its port. Exact waypoints remain obligations and can intentionally describe otherwise infeasible geometry; overlapping endpoint boxes may likewise leave no clear route.
Every edge that enters or leaves a group receives one perpendicular portal
on that boundary. Sparse group attachments override those automatic portals.
Automatic lets the router choose a side and a distinct position, On
fixes only the side, and Fixed chooses an exact normalized offset.
Flexible ports sharing a side are ordered from their endpoints and separated
by edge_gap; when a short side cannot fit that gap, spacing compresses
uniformly while preserving order. Nested portals are crossed in ancestry
order and every resulting route is kept clear of obstacles.
A shared-end rule gives two or more edges one intentional junction at a
node they have in common and selects the shared trunk's node attachment.
Member edges must not repeat an attachment rule at that common end. Their
index-aligned edge routes stop at the
junction; shared_routes contains the single trunk to draw from the
junction to the common node. This represents UML generalization and other
converging relationships without drawing coincident independent edges.
default_settings : Settings
Readable clearance and lane separation, with four bounded joint sweeps.
Side : [Top, Right, Bottom, Left]
Endpoint : [From, To]
Attachment : [Automatic, On(Side), Fixed({ side : Side, offset : F64 })]
AttachmentRule : { edge : U64, endpoint : Endpoint, attachment : Attachment }
Outline : [Rectangle, Ellipse]
BoundaryRule : { node : U64, outline : Outline }
Membership : { node : U64, group : U64 }
GroupAttachmentRule : { edge : U64, group : U64, attachment : Attachment }
LabelPlacement : [Center, Near(Endpoint)]
SharedEndRule : { edges : List(U64), endpoint : Endpoint, attachment : Attachment }
WaypointRule : { edge : U64, points : List(Point) }
Input : { graph : { nodes : List({ width : F64, height : F64 }), edges : List({ from : U64, to : U64 }) }, positions : List({ x : F64, y : F64 }), groups : List(Group), memberships : List(Membership), attachments : List(AttachmentRule), group_attachments : List(GroupAttachmentRule), boundaries : List(BoundaryRule), edge_labels : List(EdgeLabel), shared_ends : List(SharedEndRule), waypoints : List(WaypointRule), guides : List(GuideRule) }
Settings : { obstacle_gap : F64, bend_penalty : F64, shared_path_penalty : F64, edge_gap : F64, max_sweeps : U64 }
obstacle_gap is the empty space kept around node and group boxes. edge_gap
separates flexible ports and parallel lanes. bend_penalty favors fewer
turns over a shorter candidate, while shared_path_penalty favors a
distinct corridor. Zero disables the corresponding preference.
max_sweeps bounds deterministic whole-drawing improvement passes; zero
keeps the initial routes and still performs port and lane assignment.
SelectedAttachment : { point : Point, side : Side }
EdgeAttachments : { from : SelectedAttachment, to : SelectedAttachment }
GroupCrossing : { group : U64, point : Point, side : Side, offset : F64 }
SharedRoute : { edges : List(U64), endpoint : Endpoint, junction : Point, trunk : Route, group_crossings : List(GroupCrossing) }
Problem
:= [InvalidNodeWidth(U64), InvalidNodeHeight(U64), PositionCountMismatch, InvalidPosition(U64), InvalidEdgeFrom(U64), InvalidEdgeTo(U64), InvalidAttachmentEdge(U64), InvalidAttachmentOffset(U64), DuplicateAttachment(U64), InvalidGroupRect(U64), InvalidGroupParent(U64), InvalidMembershipNode(U64), InvalidMembershipGroup(U64), DuplicateMembership(U64), InvalidBoundaryNode(U64), DuplicateBoundary(U64), InvalidGroupAttachmentEdge(U64), InvalidGroupAttachmentGroup(U64), InvalidGroupAttachmentOffset(U64), DuplicateGroupAttachment(U64), GroupAttachmentNotBoundary(U64), InvalidLabelEdge(U64), InvalidLabelWidth(U64), InvalidLabelHeight(U64), InvalidWaypointEdge(U64), InvalidWaypoint(U64), DuplicateWaypoints(U64), BlockedWaypoint(U64), InvalidGuideEdge(U64), InvalidGuide(U64), DuplicateGuides(U64), SharedEndNeedsEdges(U64), InvalidSharedEndEdge(U64), DuplicateSharedEndEdge(U64), InvalidSharedEndAttachmentOffset(U64), SharedEndMismatch(U64), SharedEndMemberAttachment(U64), SharedEndOverlap(U64), InvalidObstacleGap, InvalidBendPenalty, InvalidSharedPathPenalty, InvalidEdgeGap]