Paths & Parametric Positioning¶
This is PyFreeform's "killer feature" — position any entity at any point along any path using the along / t system.
The Core Idea¶
Every Line, Curve, and Ellipse is a Pathable — it has a point_at(t) method where t goes from 0.0 (start) to 1.0 (end). You can place entities along these paths:
- The dot's position slides along the diagonal based on brightness.
Along Curves¶
Curves make the positioning non-linear and organic:
curve = cell.add_curve(start="bottom_left", end="top_right", curvature=0.6, ...)
for t_val in [0.25, 0.5, 0.75]:
cell.add_dot(along=curve, t=t_val, radius=0.10, color=colors.primary)
Along Ellipses¶
Ellipses are closed paths — t=0 is the rightmost point, going counterclockwise:
| t value | Position |
|---|---|
| 0.0 | Right |
| 0.25 | Top |
| 0.5 | Left |
| 0.75 | Bottom |
ellipse = cell.add_ellipse(at="center", rx=0.4, ry=0.25, fill="none", stroke=colors.line)
cell.add_dot(along=ellipse, t=ny, radius=0.10, color=colors.accent)
Understanding t Values¶
On a single path, t selects the position:
Built-in Path Shapes¶
PyFreeform includes four ready-to-use path shapes, accessible as Path.Wave, Path.Spiral, Path.Lissajous, and Path.Zigzag:
from pyfreeform import Path
wave = Path.Wave(start=(cx - 10, cy), end=(cx + 10, cy), amplitude=8, frequency=3)
cell.add_path(wave, segments=32, width=1.5, color=colors.primary)
All four work with add_path(), along=/t= positioning, and as connection shapes. See the API Reference for full parameter details.
Filled Closed Paths¶
Use closed=True and fill= to create filled shapes from any path. Layering semi-transparent fills produces striking geometric art:
liss = Path.Lissajous(center=(200, 200), a=3, b=2, size=150)
path = Path(liss, closed=True, fill="#4a90d9", color="#6ab0ff",
width=1.2, fill_opacity=0.25, stroke_opacity=0.7, segments=128)
scene.place(path)
Custom Pathables¶
You can also create your own — any object with point_at(t) -> Coord works as a path:
from pyfreeform import Coord
class MyPath:
def point_at(self, t):
x = t * 100
y = 50 + 20 * math.sin(t * math.pi * 4)
return Coord(x, y)
Sub-Paths and Arcs¶
Use start_t and end_t to render only a portion of a path:
cell.add_path(
ellipse,
start_t=0.1, # Start at 10% around
end_t=0.6, # End at 60% around
segments=24,
width=2,
color=colors.primary,
)
Text Along Paths¶
Pass a path to add_text(along=) without t to warp text along the full path:
curve = cell.add_curve(start=(0.05, 0.7), end=(0.95, 0.3), curvature=0.5, ...)
cell.add_text("Text flows along any path", along=curve, font_size=0.05, color=colors.accent)
<textPath>.Alignment¶
Set align=True to rotate entities to follow the path's tangent direction:
cell.add_polygon(
Polygon.triangle(size=0.06),
along=curve, t=0.5, align=True, # (1)!
fill=colors.primary,
)
- The triangle points in the direction the curve is heading at
t=0.5.
What's Next?¶
Explore the shape system and learn to compose reusable groups: