Skip to content

Connections & Paths

Connections link entities and surfaces together. The Pathable protocol lets you position anything along any curve.

See also

For connection patterns and anchor techniques, see Connections and Anchors. For parametric positioning, see Paths and Parametric Positioning.


Connection

Connection(start: Connectable, end: Connectable, start_anchor: AnchorSpec = 'center', end_anchor: AnchorSpec = 'center', *, path: Path | None = None, curvature: float | None = None, visible: bool = True, width: float = 1, color: PaintLike = 'black', z_index: int = 0, cap: CapName = 'round', start_cap: CapName | None = None, end_cap: CapName | None = None, opacity: float = 1.0, color_brightness: float | None = None, style: PathStyle | None = None, segments: int = 32)

A connection between two connectable objects (entities or surfaces).

Connections link any connectable objects together with a visible line by default. Use visible=False for invisible relationships.

Attributes:

Name Type Description
start Connectable

The starting connectable object

end Connectable

The ending connectable object

start_anchor AnchorSpec

Name of anchor on start object

end_anchor AnchorSpec

Name of anchor on end object

data dict[str, Any]

Custom data dictionary

Example
dot1 = Dot(100, 100)
dot2 = Dot(200, 200)
# Visible straight line (default)
conn = dot1.connect(dot2)
# Styled line
conn = dot1.connect(dot2, width=2, color="red")
# Arc
conn = dot1.connect(dot2, curvature=0.3)
# Custom path
conn = dot1.connect(dot2, path=Path.Wave())
# Cell-to-cell connection
conn = cell_a.connect(cell_b, color="red")
# Entity-to-cell connection
conn = dot.connect(cell, end_anchor="left")

Create a connection between two connectable objects.

Parameters:

Name Type Description Default
start Connectable

The starting entity or surface.

required
end Connectable

The ending entity or surface.

required
start_anchor AnchorSpec

Anchor name on start object.

'center'
end_anchor AnchorSpec

Anchor name on end object.

'center'
path Path | None

Custom path geometry (e.g. Path.Wave()). For simple arcs use curvature instead.

None
curvature float | None

Arc curvature (-1 to 1). Positive bows left, negative bows right. Cannot be used with path.

None
visible bool

Whether the connection renders. Default True.

True
width float

Line width in pixels.

1
color PaintLike

Line color.

'black'
z_index int

Layer order (higher = on top).

0
cap CapName

Cap style for both ends ("round", "butt", "square").

'round'
start_cap CapName | None

Override cap for start end (e.g. "arrow").

None
end_cap CapName | None

Override cap for end end (e.g. "arrow").

None
opacity float

Opacity (0.0 transparent to 1.0 opaque).

1.0
color_brightness float | None

Brightness multiplier 0.0 (black) to 1.0 (unchanged).

None
style PathStyle | None

PathStyle object (overrides individual params).

None
segments int

Number of Bézier segments for path rendering.

32

start property

start: Connectable

The starting connectable object.

end property

end: Connectable

The ending connectable object.

start_anchor property

start_anchor: AnchorSpec

Anchor spec on start object.

end_anchor property

end_anchor: AnchorSpec

Anchor spec on end object.

start_point property

start_point: Coord

Current position of the start anchor (always up-to-date).

end_point property

end_point: Coord

Current position of the end anchor (always up-to-date).

data property

data: dict[str, Any]

Custom data dictionary for this connection.

visible property writable

visible: bool

Whether this connection renders.

color property writable

color: str

Line paint as a string (color or gradient ref).

z_index property writable

z_index: int

Layer ordering (higher values render on top).

width instance-attribute

width = float(width)

opacity instance-attribute

opacity = float(opacity)

cap instance-attribute

cap = cap

start_cap instance-attribute

start_cap = start_cap

end_cap instance-attribute

end_cap = end_cap

curvature property

curvature: float | None

Arc curvature, or None for straight/path.

path property

path

The custom path geometry, or None.

effective_start_cap property

effective_start_cap: str

Resolved cap for the start end.

effective_end_cap property

effective_end_cap: str

Resolved cap for the end end.

point_at

point_at(t: float) -> Coord

Get a point along the connection.

Parameters:

Name Type Description Default
t float

Parameter from 0 (start) to 1 (end).

required

Returns:

Type Description
Coord

Coord at that position along the connection.

angle_at

angle_at(t: float) -> float

Get the tangent angle in degrees at parameter t.

Parameters:

Name Type Description Default
t float

Parameter from 0 (start) to 1 (end).

required

Returns:

Type Description
float

Angle in degrees.

to_svg_path_d

to_svg_path_d() -> str

Return SVG path d attribute for this connection.

disconnect

disconnect() -> None

Remove this connection from both endpoints.

Or via the entity/surface shorthand:

connection = entity1.connect(entity2, style=PathStyle(...), curvature=0.3)

Geometry Options

Mode SVG Output Notes
(default) <line> element Straight connection
curvature=0.3 Single cubic Bezier <path> Arc; curvature controls bow direction and amount
path=Path(pathable) Fitted Bezier <path> Any Pathable -- wave, spiral, custom shape
visible=False Nothing (to_svg() returns "") Pure relationship -- point_at(t) still works

Coordinates are auto-mapped

For curvature=, the arc is built in normalized unit-chord space. For path=, the path geometry is pre-computed. Both are automatically stretched and rotated (affine transform) to connect the actual anchor positions at render time.


The Pathable Protocol

The Pathable protocol defines a single required method:

class Pathable:
    def point_at(self, t: float) -> Coord

Where t ranges from 0.0 (start) to 1.0 (end). This enables the along/t parametric positioning system used in all builder methods.

Built-in Pathables

Entity Description
Line Linear interpolation from start to end
Curve Smooth curve with adjustable bow
Ellipse Parametric ellipse (t=0 right, t=0.25 top, t=0.5 left, t=0.75 bottom)
Path Evaluates stored cubic Bezier segments
Connection Dynamic path between entities

Optional Pathable Methods

Method / Property Used By Description
arc_length() add_text(along=) Total path length for text sizing
angle_at(t) get_angle_at() Tangent angle for alignment
to_svg_path_d() add_text(along=) SVG path for <textPath> warping
closed to_svg_path_d() Whether start and end coincide (closed loop)

Built-in Path Shapes

Ready-to-use pathable classes, accessible as nested classes on Path. All implement point_at(t), angle_at(t), arc_length(), to_svg_path_d(), and the closed property.

Wave

Wave(start: CoordLike = (0, 0), end: CoordLike = (1, 0), amplitude: float = 0.15, frequency: float = 2)

Bases: PathShape

Sinusoidal wave between two points.

Generates a sine oscillation along the Y-axis relative to the baseline from start to end.

When start and end are omitted, defaults to normalized (0, 0) -> (1, 0) space — ideal for connection shapes.

Example

Standalone path::

wave = Wave(start=(50, 100), end=(550, 100), amplitude=40, frequency=4)
scene.add_path(wave, width=2, color="blue")

Connection path::

wave = Wave(amplitude=0.15, frequency=3)
conn = dot_a.connect(dot_b, path=Path(wave), style=style)

In a cell::

wave = Wave(start=cell.top_left, end=cell.bottom_right, amplitude=5)
cell.add_path(wave, width=1, color="red")
point_at
point_at(t: float) -> Coord

Get point at parameter t (0.0 to 1.0).

angle_at
angle_at(t: float) -> float

Tangent angle in degrees at parameter t.

Spiral

Spiral(center: CoordLike = (0, 0), start_radius: float = 0, end_radius: float = 50, turns: float = 3)

Bases: PathShape

Archimedean spiral expanding outward from center.

The spiral makes turns revolutions while the radius grows linearly from start_radius to end_radius.

Example

Standalone path::

spiral = Spiral(center=(200, 200), end_radius=80, turns=4)
scene.add_path(spiral, width=1.5, color="coral")

In a cell::

spiral = Spiral(center=cell.center, end_radius=40, turns=3)
cell.add_dot(along=spiral, t=cell.brightness, color="coral")

Closed path with fill::

spiral = Spiral(center=(200, 200), end_radius=60, turns=5)
scene.add_path(spiral, closed=True, fill="lightblue", color="navy")
point_at
point_at(t: float) -> Coord

Get point at parameter t (0.0 to 1.0).

angle_at
angle_at(t: float) -> float

Tangent angle in degrees at parameter t.

Lissajous

Lissajous(center: CoordLike = (0, 0), a: int = 3, b: int = 2, delta: float = pi / 2, size: float = 50)

Bases: PathShape

Lissajous curve: x = size * sin(a*t + delta), y = size * sin(b*t).

A closed curve (closed = True) when a/b is rational. Common ratios:

  • a=3, b=2 — figure-eight variant
  • a=1, b=2 — parabola-like
  • a=5, b=4 — complex knot

Because this is a closed loop, using it directly as a connection shape will raise ValueError. Use start_t/end_t to take an arc::

arc = Path(Lissajous(), start_t=0, end_t=0.5)
dot_a.connect(dot_b, path=arc)
Example

Standalone closed path::

liss = Lissajous(center=(200, 200), a=3, b=2, size=80)
scene.add_path(liss, closed=True, fill="lightblue", color="navy")

Connection via arc::

liss = Lissajous(a=3, b=2, size=50)
conn = dot_a.connect(dot_b, path=Path(liss, start_t=0, end_t=0.5))

Position dots along curve::

liss = Lissajous(center=cell.center, size=60)
cell.add_dot(along=liss, t=cell.brightness, color="coral")
closed property
closed: bool

Lissajous curves are closed loops.

point_at
point_at(t: float) -> Coord

Get point at parameter t (0.0 to 1.0).

angle_at
angle_at(t: float) -> float

Tangent angle in degrees at parameter t.

Zigzag

Zigzag(start: CoordLike = (0, 0), end: CoordLike = (1, 0), teeth: int = 5, amplitude: float = 0.12)

Bases: PathShape

Triangle wave (zigzag) between two points.

Generates a zigzag pattern along the Y-axis relative to the baseline from start to end.

When start and end are omitted, defaults to normalized (0, 0) -> (1, 0) space — ideal for connection shapes.

Example

Standalone path::

zz = Zigzag(start=(50, 100), end=(550, 100), teeth=8, amplitude=20)
scene.add_path(zz, width=2, color="orange")

Connection path::

zz = Zigzag(teeth=6, amplitude=0.12)
conn = dot_a.connect(dot_b, path=Path(zz), style=style)
point_at
point_at(t: float) -> Coord

Get point at parameter t (0.0 to 1.0).

angle_at
angle_at(t: float) -> float

Tangent angle in degrees at parameter t.

# As a standalone path
spiral = Path.Spiral(center=cell.center, end_radius=40, turns=3)
cell.add_path(spiral, width=1.5, color="coral")

# As a connection path (open shapes work directly)
wave = Path.Wave(amplitude=0.15, frequency=3)
conn = dot_a.connect(dot_b, path=Path(wave), style=style)

# Closed shapes need start_t/end_t to create an arc for connections
liss_arc = Path(Path.Lissajous(size=50), start_t=0, end_t=0.5)
conn = dot_a.connect(dot_b, path=liss_arc)

Custom Pathables

Any object with point_at(t: float) -> Coord works as a path. See the Pathable Protocol for a full walkthrough of creating custom paths.