Skip to content

Drawing

Surface is the base class for Cell, Scene, and CellGroup. It provides 12 builder methods that all work identically across these three surfaces. This is where creativity happens.

See also

For creative examples of all builder methods, see Drawing with Entities.


Named Positions

All at parameters accept named positions or (rx, ry) relative coordinates:

Name Relative Description
"center" (0.5, 0.5) Center of surface
"top_left" (0.0, 0.0) Top-left corner
"top_right" (1.0, 0.0) Top-right corner
"bottom_left" (0.0, 1.0) Bottom-left corner
"bottom_right" (1.0, 1.0) Bottom-right corner
"top" (0.5, 0.0) Top center
"bottom" (0.5, 1.0) Bottom center
"left" (0.0, 0.5) Left center
"right" (1.0, 0.5) Right center

Type checking for RelCoordLike

The RelCoordLike type accepts:

  • A RelCoord object (e.g., RelCoord(rx=0.5, ry=0.5))
  • A tuple (rx, ry)
  • Exactly one of the strings in NAMED_POSITIONS (like "center" or "top_left")

Why type checkers may complain

If you assign a string indirectly, for example via a list or variable:

positions = ["center", "top_left", "bottom_right"]
for pos in positions:
    scene.add_dot(at=pos)  # ⚠️ Type checker expects explicit strings

… the checker cannot verify that pos matches a valid named position.

How to fix

Explicitly validate each value against NAMED_POSITIONS:

from pyfreeform.core import NAMED_POSITIONS

positions = ["center", "top_left", "bottom_right"]
for pos in positions:
    if pos in NAMED_POSITIONS:
        scene.add_dot(at=pos)  # ✅ safe

Parametric Positioning: along / t / align

All builder methods (except add_fill, add_border, add_path) support parametric positioning:

  • along: Any Pathable object (Line, Curve, Ellipse, Path, Connection, or custom)
  • t: Parameter 0.0 (start) to 1.0 (end) along the path
  • align: If True, rotate the entity to follow the path's tangent direction
  • along_offset: Perpendicular shift from the path. Negative = above the line, positive = below. Direction-independent.

Killer feature

This is PyFreeform's most powerful concept -- position any element along any path:

line = cell.add_diagonal()
cell.add_dot(along=line, t=cell.brightness)  # Dot slides along diagonal
See Paths and Parametric Positioning for in-depth examples.

Entity-Relative Positioning: within

All builder methods (except add_fill, add_border, add_path) support within=:

rect = cell.add_rect(fill="blue", width=0.5, height=0.5)
dot = cell.add_dot(within=rect, at="center", color="red")

When within= is set, all relative coordinates (at, start/end, radius, rx/ry, width/height) are resolved against the referenced entity's bounding box instead of the cell. This is reactive -- if the reference entity moves, dependent entities follow automatically.

The .at Property

Every entity has a read/write .at property that returns a RelCoord:

dot = cell.add_dot(at=(0.25, 0.75), color="red")
print(dot.at)       # RelCoord(0.25, 0.75)
print(dot.at.rx)    # 0.25
dot.at = (0.5, 0.5) # Reposition to center (plain tuples still accepted)

Returns None if the entity was created with pixel coordinates (via place() or direct constructor). See RelCoord for details.

Relative Sizing Properties

Builder methods store sizing as fractions of the reference frame. These are accessible as read/write properties on each entity:

Entity Property Builder default Description
Dot relative_radius 0.05 Fraction of min(width, height)
Line relative_start, relative_end varies Start/end as RelCoord fractions
Curve relative_start, relative_end varies Start/end as RelCoord fractions
Ellipse relative_rx, relative_ry 0.4 Fraction of surface width/height
Rect relative_width, relative_height 0.6 Fraction of surface width/height
Text relative_font_size 0.25 Fraction of surface height
Polygon relative_vertices varies List of RelCoord vertex positions

These return None when the entity is in absolute mode (constructed directly or after a transform resolves them). Setting them switches the entity back to relative mode for that dimension.

Sizing vs geometry

Relative sizing (radius, rx/ry, width/height, font_size) is unaffected by transforms -- rotation doesn't change how big something is relative to its cell. Relative geometry (vertices, start/end) encodes positions that transforms convert to absolute values. After a transform resolves an entity, builder methods won't overwrite those concrete values.

Surface Anchors, Connections, and Data

All surfaces (Cell, Scene, CellGroup) support anchors, connections, and custom data:

Method/Property Description
surface.anchor(spec) Anchor position by name, RelCoord, or (rx, ry) tuple. See AnchorSpec.
surface.anchor_names List of available anchor names
surface.connect(other, ..., start_anchor, end_anchor) Create a connection. Anchors accept AnchorSpec.
surface.connections Set of connections where this surface is an endpoint
surface.data Custom data dictionary
surface.contains(point) Whether a Coord is within the surface bounds
surface.relative_to_absolute(pos) Convert relative position (named string, tuple, or RelCoord) to pixel Coord

Surface

Surface(x: float, y: float, width: float, height: float)

Base class for any rectangular region that can contain entities.

Provides builder methods (add_dot, add_line, add_curve, etc.), position resolution (named positions, relative coordinates), and entity management.

Implemented by: Cell, Scene, CellGroup

add_dot

add_dot(*, at: RelCoordLike = 'center', within: Entity | None = None, along: Pathable | None = None, t: float | None = None, along_offset: float | None = None, radius: float = 0.05, color: PaintLike = 'black', z_index: int = 0, opacity: float = 1.0, color_brightness: float | None = None, style: FillStyle | None = None) -> Dot

Add a dot to this surface.

Parameters:

Name Type Description Default
at RelCoordLike

RelCoordLike ("center", "top_left", or (rx, ry) tuple).

'center'
within Entity | None

Size/position relative to another entity's bounds.

None
along Pathable | None

Path to position the dot along.

None
t float | None

Parameter on the path (0.0 to 1.0).

None
along_offset float | None

Perpendicular offset from path as fraction of the smaller surface dimension.

None
radius float

Dot radius as fraction of the smaller surface dimension. 0.05 = 5% of min(width, height).

0.05
color PaintLike

Fill color.

'black'
z_index int

Layer order (higher = on top).

0
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 FillStyle | None

FillStyle object (overrides individual params).

None

Returns:

Type Description
Dot

The created Dot entity.

Example
cell.add_dot()  # Centered, default style
cell.add_dot(color="red", radius=0.1)
cell.add_dot(at="top_left")
cell.add_dot(at=(0.25, 0.75))  # 25% across, 75% down
cell.add_dot(along=line, t=0.5)  # Midpoint of a line

add_line

add_line(*, start: RelCoordLike = 'center', end: RelCoordLike = 'center', within: Entity | None = None, along: Pathable | None = None, t: float | None = None, along_offset: float | None = None, align: bool = False, 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) -> Line

Add a line to this surface.

When along is provided, the line's midpoint is repositioned onto the path at parameter t. If align=True, the line is also rotated to follow the path's tangent direction.

Parameters:

Name Type Description Default
start RelCoordLike

Starting position

'center'
end RelCoordLike

Ending position

'center'
along Pathable | None

Path to position the line's midpoint along

None
t float | None

Parameter on the path (0.0 to 1.0, default 0.5)

None
along_offset float | None

Perpendicular offset from path as fraction of the smaller surface dimension.

None
align bool

Rotate line to follow path tangent

False
width float

Stroke width in pixels

1
color PaintLike

Stroke color

'black'
z_index int

Layer order

0
cap CapName

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

'round'
start_cap CapName | None

Override cap for start end only

None
end_cap CapName | None

Override cap for end end only

None
color_brightness float | None

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

None
style PathStyle | None

PathStyle object (overrides individual params)

None

Returns:

Type Description
Line

The created Line entity.

Example
cell.add_line(start="top_left", end="bottom_right")
cell.add_line(start="left", end="right", end_cap="arrow")
# Position along a curve
curve = cell.add_curve()
cell.add_line(start=(0,0), end=(20,0), along=curve, t=0.5, align=True)

add_diagonal

add_diagonal(*, start: Literal['top_left', 'top_right', 'bottom_left', 'bottom_right'] = 'bottom_left', end: Literal['top_left', 'top_right', 'bottom_left', 'bottom_right'] = 'top_right', within: Entity | None = None, along: Pathable | None = None, t: float | None = None, along_offset: float | None = None, align: bool = False, 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) -> Line

Add a diagonal line across this surface.

Convenience method that delegates to add_line() with corner positions.

Parameters:

Name Type Description Default
start Literal['top_left', 'top_right', 'bottom_left', 'bottom_right']

Starting corner (default: "bottom_left")

'bottom_left'
end Literal['top_left', 'top_right', 'bottom_left', 'bottom_right']

Ending corner (default: "top_right")

'top_right'
along Pathable | None

Path to position the line's midpoint along

None
t float | None

Parameter on the path (0.0 to 1.0, default 0.5)

None
along_offset float | None

Perpendicular offset from path as fraction of the smaller surface dimension.

None
align bool

Rotate line to follow path tangent

False
width float

Stroke width

1
color PaintLike

Stroke color

'black'
z_index int

Layer order

0
cap CapName

Cap style for both ends

'round'
start_cap CapName | None

Override cap for start end only

None
end_cap CapName | None

Override cap for end end only

None
color_brightness float | None

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

None
style PathStyle | None

PathStyle object

None

Returns:

Type Description
Line

The created Line entity.

Example
line = cell.add_diagonal()  # Bottom-left to top-right (SW to NE)
line = cell.add_diagonal(start="top_left", end="bottom_right")  # NW to SE
cell.add_dot(along=line, t=cell.brightness)  # Dot slides along

add_curve

add_curve(*, start: RelCoordLike = 'bottom_left', end: RelCoordLike = 'top_right', curvature: float = 0.5, within: Entity | None = None, along: Pathable | None = None, t: float | None = None, along_offset: float | None = None, align: bool = False, 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) -> Curve

Add a smooth curve between two points.

The curvature parameter controls how much the curve bows away from a straight line.

When along is provided, the curve's midpoint is repositioned onto the path at parameter t. If align=True, the curve is also rotated to follow the path's tangent direction.

Parameters:

Name Type Description Default
start RelCoordLike

Starting position

'bottom_left'
end RelCoordLike

Ending position

'top_right'
curvature float

How much the curve bows (-1 to 1, 0 = straight) Positive = bows left, Negative = bows right

0.5
along Pathable | None

Path to position the curve's midpoint along

None
t float | None

Parameter on the path (0.0 to 1.0, default 0.5)

None
along_offset float | None

Perpendicular offset from path as fraction of the smaller surface dimension.

None
align bool

Rotate curve to follow path tangent

False
width float

Stroke width in pixels

1
color PaintLike

Stroke color

'black'
z_index int

Layer order

0
cap CapName

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

'round'
start_cap CapName | None

Override cap for start end only

None
end_cap CapName | None

Override cap for end end only

None
color_brightness float | None

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

None
style PathStyle | None

PathStyle object (overrides width/color/z_index/cap)

None

Returns:

Type Description
Curve

The created Curve entity.

Example
curve = cell.add_curve(curvature=0.5)  # Gentle bow
curve = cell.add_curve(curvature=-0.8, end_cap="arrow")
cell.add_dot(along=curve, t=cell.brightness)  # Dot slides along!

add_path

add_path(pathable: Pathable, *, relative: bool = False, segments: int = 64, closed: bool = False, start_t: float = 0.0, end_t: float = 1.0, width: float = 1, color: PaintLike = 'black', fill: PaintLike | None = None, z_index: int = 0, cap: CapName = 'round', start_cap: CapName | None = None, end_cap: CapName | None = None, opacity: float = 1.0, fill_opacity: float | None = None, stroke_opacity: float | None = None, style: PathStyle | None = None) -> Path

Add a smooth path rendered from any Pathable.

Takes any object with a point_at(t) method (Wave, Spiral, Lissajous, or your own custom class) and renders it as a smooth SVG path using cubic Bézier approximation.

Supports both open and closed paths. Closed paths can be filled. Use start_t and end_t to render a sub-section (arc) of any pathable.

Parameters:

Name Type Description Default
pathable Pathable

Any object implementing point_at(t).

required
relative bool

If True, treat the pathable's coordinates as surface-relative fractions (0.0–1.0) and scale them to pixel space automatically. Lets you write Wave(start=(0.1, 0.5), end=(0.9, 0.5), amplitude=0.15) instead of Wave(start=(w*0.1, h*0.5), ...).

False
segments int

Number of cubic Bézier segments (higher = smoother).

64
closed bool

Close the path smoothly back to start.

False
start_t float

Start parameter on the pathable (0.0-1.0).

0.0
end_t float

End parameter on the pathable (0.0-1.0).

1.0
width float

Stroke width in pixels.

1
color PaintLike

Stroke color.

'black'
fill PaintLike | None

Fill color for closed paths (ignored if not closed).

None
z_index int

Layer order.

0
cap CapName

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

'round'
start_cap CapName | None

Override cap for start only.

None
end_cap CapName | None

Override cap for end only.

None
opacity float

Overall opacity.

1.0
fill_opacity float | None

Override fill opacity.

None
stroke_opacity float | None

Override stroke opacity.

None
style PathStyle | None

PathStyle object (overrides width/color/z_index/cap).

None

Returns:

Type Description
Path

The created Path entity.

Example
# Fraction-first (relative=True)
cell.add_path(Wave(start=(0.05, 0.5), end=(0.95, 0.5), amplitude=0.15),
              relative=True, color="blue", width=2)

# Arc of an ellipse (quarter circle):
ellipse = cell.add_ellipse(rx=0.4, ry=0.4)
cell.add_path(ellipse, start_t=0.0, end_t=0.25, color="red")

add_ellipse

add_ellipse(*, at: RelCoordLike = 'center', within: Entity | None = None, along: Pathable | None = None, t: float | None = None, along_offset: float | None = None, align: bool = False, rx: float | None = None, ry: float | None = None, rotation: float = 0, fill: PaintLike | None = 'black', stroke: PaintLike | None = None, stroke_width: float = 1, z_index: int = 0, opacity: float = 1.0, fill_opacity: float | None = None, stroke_opacity: float | None = None, fill_brightness: float | None = None, stroke_brightness: float | None = None, style: ShapeStyle | None = None) -> Ellipse

Add an ellipse to this surface.

Ellipses support parametric positioning just like lines and curves. Use along + t to place the ellipse center on a path. Use align=True to rotate the ellipse to follow the tangent.

Parameters:

Name Type Description Default
at RelCoordLike

RelCoordLike (center of ellipse)

'center'
along Pathable | None

Path to position the ellipse center along

None
t float | None

Parameter on the path (0.0 to 1.0, default 0.5)

None
along_offset float | None

Perpendicular offset from path as fraction of the smaller surface dimension.

None
align bool

Rotate ellipse to follow path tangent

False
rx float | None

Horizontal radius as fraction of surface width (default 0.4)

None
ry float | None

Vertical radius as fraction of surface height (default 0.4)

None
rotation float

Rotation in degrees (counterclockwise)

0
fill PaintLike | None

Fill color (None for transparent)

'black'
stroke PaintLike | None

Stroke color (None for no stroke)

None
stroke_width float

Stroke width in pixels

1
z_index int

Layer order

0
style ShapeStyle | None

ShapeStyle object (overrides fill/stroke/stroke_width/z_index)

None

Returns:

Type Description
Ellipse

The created Ellipse entity.

Example
ellipse = cell.add_ellipse(rx=0.3, ry=0.2)
cell.add_dot(along=ellipse, t=cell.brightness)
# Place ellipse along a curve
curve = cell.add_curve()
cell.add_ellipse(rx=0.1, ry=0.06, along=curve, t=0.5, align=True)

add_polygon

add_polygon(vertices: list[tuple[float, float]], *, within: Entity | None = None, along: Pathable | None = None, t: float | None = None, along_offset: float | None = None, align: bool = False, fill: PaintLike | None = 'black', stroke: PaintLike | None = None, stroke_width: float = 1, z_index: int = 0, opacity: float = 1.0, fill_opacity: float | None = None, stroke_opacity: float | None = None, fill_brightness: float | None = None, stroke_brightness: float | None = None, rotation: float = 0, style: ShapeStyle | None = None) -> Polygon

Add a polygon to this surface.

Vertices are specified in relative coordinates (0-1), where (0,0) is top-left and (1,1) is bottom-right of the surface.

When along is provided, the polygon's centroid is repositioned onto the path at parameter t. If align=True, the polygon is rotated to follow the path's tangent direction.

Parameters:

Name Type Description Default
vertices list[tuple[float, float]]

List of (x, y) tuples in relative coordinates. Use Polygon.hexagon(), Polygon.star() for common shapes.

required
along Pathable | None

Path to position the polygon's centroid along

None
t float | None

Parameter on the path (0.0 to 1.0, default 0.5)

None
along_offset float | None

Perpendicular offset from path as fraction of the smaller surface dimension.

None
align bool

Rotate polygon to follow path tangent

False
fill PaintLike | None

Fill color (None for transparent)

'black'
stroke PaintLike | None

Stroke color (None for no stroke)

None
stroke_width float

Stroke width in pixels

1
z_index int

Layer order

0
rotation float

Rotation in degrees (around polygon center)

0
style ShapeStyle | None

ShapeStyle object (overrides fill/stroke/stroke_width/z_index)

None

Returns:

Type Description
Polygon

The created Polygon entity.

Example
cell.add_polygon([(0.5, 0.1), (0.9, 0.9), (0.1, 0.9)], fill="red")
from pyfreeform.entities.polygon import Polygon
cell.add_polygon(Polygon.hexagon(), fill="purple")
# Place polygon along a curve
curve = cell.add_curve()
cell.add_polygon(Polygon.hexagon(), along=curve, t=0.5, align=True)

add_rect

add_rect(*, at: RelCoordLike = 'center', within: Entity | None = None, along: Pathable | None = None, t: float | None = None, along_offset: float | None = None, align: bool = False, width: float | None = None, height: float | None = None, rotation: float = 0, fill: PaintLike | None = 'black', stroke: PaintLike | None = None, stroke_width: float = 1, opacity: float = 1.0, fill_opacity: float | None = None, stroke_opacity: float | None = None, fill_brightness: float | None = None, stroke_brightness: float | None = None, z_index: int = 0, style: ShapeStyle | None = None) -> Rect

Add a rectangle to this surface.

The at parameter specifies where the CENTER of the rectangle will be placed, consistent with add_ellipse().

When along is provided, the rectangle center is repositioned onto the path at parameter t. If align=True, the rectangle is rotated to follow the path's tangent direction.

For full-surface fills use add_fill(). For borders use add_border().

Parameters:

Name Type Description Default
at RelCoordLike

RelCoordLike of rectangle center.

'center'
along Pathable | None

Path to position the rectangle center along.

None
t float | None

Parameter on the path (0.0 to 1.0, default 0.5).

None
along_offset float | None

Perpendicular offset from path as fraction of the smaller surface dimension.

None
align bool

Rotate rectangle to follow path tangent.

False
width float | None

Rectangle width as fraction of surface width (default 0.6).

None
height float | None

Rectangle height as fraction of surface height (default 0.6).

None
rotation float

Rotation in degrees (counterclockwise).

0
fill PaintLike | None

Fill color (None for transparent).

'black'
stroke PaintLike | None

Stroke color (None for no stroke).

None
stroke_width float

Stroke width in pixels.

1
opacity float

Opacity for both fill and stroke (0.0-1.0).

1.0
fill_opacity float | None

Override opacity for fill only.

None
stroke_opacity float | None

Override opacity for stroke only.

None
z_index int

Layer order (higher = on top).

0
style ShapeStyle | None

ShapeStyle object.

None

Returns:

Type Description
Rect

The created Rect entity.

Example
rect = cell.add_rect(fill="coral")
rect = cell.add_rect(width=0.5, height=0.4, rotation=45)
# Place rect along a curve
curve = cell.add_curve()
cell.add_rect(width=0.2, height=0.1, along=curve, t=0.5, align=True)

add_text

add_text(content: str, *, at: RelCoordLike = 'center', within: Entity | None = None, along: Pathable | None = None, t: float | None = None, along_offset: float | None = None, align: bool = False, font_size: float | None = None, color: PaintLike = 'black', font_family: str = 'sans-serif', bold: bool = False, italic: bool = False, text_anchor: str | None = None, baseline: str = 'middle', rotation: float = 0, z_index: int = 0, opacity: float = 1.0, color_brightness: float | None = None, fit: bool = False, start_offset: float = 0.0, end_offset: float = 1.0, style: TextStyle | None = None) -> Text

Add text to this surface.

When along is provided with t, the text is positioned at that point on the path. If align=True, the text is rotated to follow the path's tangent.

When along is provided without t, the text is warped along the path using SVG <textPath>.

Parameters:

Name Type Description Default
content str

The text string to display.

required
at RelCoordLike

RelCoordLike ("center", "top_left", or (rx, ry) tuple).

'center'
along Pathable | None

Path to position or warp text along.

None
t float | None

Parameter on the path (0.0 to 1.0). If omitted with along, text warps along the full path.

None
along_offset float | None

Perpendicular offset from path as fraction of the smaller surface dimension.

None
align bool

Rotate text to follow path tangent (only with t).

False
font_size float | None

Font size as fraction of surface height (e.g. 0.25 = 25% of cell height). When omitted, defaults to 0.25. In textPath mode, auto-sized via Pillow font metrics to match the path length (falls back to a 0.6× heuristic if the font is unavailable locally; capped at 25% of surface height).

None
color PaintLike

Text color.

'black'
font_family str

Font family.

'sans-serif'
bold bool

Bold text.

False
italic bool

Italic text.

False
text_anchor str | None

Horizontal alignment: "start", "middle", "end".

None
baseline str

Vertical alignment: "auto", "middle", "hanging".

'middle'
rotation float

Rotation in degrees around the text position.

0
z_index int

Layer order (higher = on top).

0
fit bool

If True, shrink font_size so the rendered text fits within the cell width. Never upsizes — font_size is a ceiling.

False
start_offset float

Where text begins on the path, 0.0-1.0 (textPath mode only). Default 0.0 = start of path.

0.0
end_offset float

Where text ends on the path, 0.0-1.0 (textPath mode only). Default 1.0 = end of path.

1.0
style TextStyle | None

TextStyle object (overrides individual params).

None

Returns:

Type Description
Text

The created Text entity.

Example
cell.add_text("A")  # Centered letter
cell.add_text("Label", at="top", font_size=0.15)
# Position text along a curve
curve = cell.add_curve()
cell.add_text("Hi", along=curve, t=0.5, align=True)
# Warp text along a curve (textPath)
cell.add_text("Hello World", along=curve)
# Warp along middle 60% of the path
cell.add_text("Partial", along=curve, start_offset=0.2, end_offset=0.8)

add_fill

add_fill(*, color: PaintLike = 'black', opacity: float = 1.0, z_index: int = 0, color_brightness: float | None = None, style: FillStyle | None = None) -> Rect

Fill this surface with a rectangle.

Parameters:

Name Type Description Default
color PaintLike

Fill color

'black'
opacity float

Fill opacity (0.0 transparent to 1.0 opaque)

1.0
z_index int

Layer order

0
color_brightness float | None

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

None
style FillStyle | None

FillStyle object

None

Returns:

Type Description
Rect

The created Rect entity.

Example
cell.add_fill(color=cell.color)
cell.add_fill(color="blue", opacity=0.5)

add_border

add_border(*, color: PaintLike = '#cccccc', width: float = 0.5, z_index: int = 0, opacity: float = 1.0, color_brightness: float | None = None, style: BorderStyle | None = None) -> Rect

Add a border around this surface.

Parameters:

Name Type Description Default
color PaintLike

Stroke color

'#cccccc'
width float

Stroke width in pixels

0.5
z_index int

Layer order

0
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 BorderStyle | None

BorderStyle object

None

Returns:

Type Description
Rect

The created Rect entity.

Example
cell.add_border(color=palette.grid)

add_point

add_point(*, at: RelCoordLike = 'center', within: Entity | None = None, along: Pathable | None = None, t: float | None = None, along_offset: float | None = None, z_index: int = 0) -> Point

Add an invisible point to this surface.

Points render nothing — they exist purely as positional anchors. Use them as reactive Polygon vertices, connection endpoints, or reference positions for within=.

Parameters:

Name Type Description Default
at RelCoordLike

RelCoordLike ("center", "top_left", or (rx, ry) tuple)

'center'
within Entity | None

Size/position relative to another entity's bounds

None
along Pathable | None

Path to position the point along

None
t float | None

Parameter on the path (0.0 to 1.0, default 0.5)

None
along_offset float | None

Perpendicular offset from path as fraction of the smaller surface dimension.

None
z_index int

Layer order

0

Returns:

Type Description
Point

The created Point entity.

Example
p = cell.add_point(at=(0.25, 0.75))
p = cell.add_point(at="top_left")
# Reactive polygon vertex
a = cell.add_point(at=(0.5, 0.1))
b = cell.add_point(at=(0.9, 0.9))
c = cell.add_point(at=(0.1, 0.9))
tri = Polygon([a, b, c], fill="coral")

add

add(entity: Entity, at: RelCoordLike = 'center') -> Entity

Add an existing entity to this surface with relative positioning.

The entity is moved to the resolved at position. For creating and adding in one step, use add_dot(), add_line(), etc.

Parameters:

Name Type Description Default
entity Entity

The entity to add.

required
at RelCoordLike

RelCoordLike - relative coords or named position.

'center'

Returns:

Type Description
Entity

The added entity (for chaining).

place

place(entity: Entity) -> Entity

Place an entity at its current absolute pixel position (escape hatch).

Unlike add(), this does NOT reposition the entity — it is registered exactly where it already is.

Parameters:

Name Type Description Default
entity Entity

The entity to place.

required

Returns:

Type Description
Entity

The placed entity (for chaining).

remove

remove(entity: Entity) -> bool

Remove an entity from this surface.

clear

clear() -> None

Remove all entities from this surface.

Note

Most of the time you want add(). Use place() only when you've already positioned an entity at exact pixel coordinates and want to register it with a surface without moving it.