Skip to content

Grid & Cells

A Grid divides the scene into rows and columns of Cell objects. Each cell is a creative unit with image data, position helpers, and the full set of builder methods.

See also

For grid creation patterns, see Scenes and Grids. For cell usage, see Working with Cells.


Grid

Grid(cols: int, rows: int, cell_size: float | None = None, cell_width: float | None = None, cell_height: float | None = None, origin: tuple[float, float] = (0, 0))

A grid of cells that provides structure for placing entities.

Grids divide space into cells, making it easy to create patterns, load image data, and organize entities spatially.

Attributes:

Name Type Description
num_rows int

Number of rows

num_columns int

Number of columns

cell_width float

Width of each cell in pixels

cell_height float

Height of each cell in pixels

pixel_width float

Total width in pixels

pixel_height float

Total height in pixels

Example
grid = Grid(cols=20, rows=20, cell_size=10)
cell = grid[5][10]     # Access cell at row 5, col 10
row = grid[5]          # Get entire row as list of cells
for cell in grid:      # Iterate all cells (row by row)
    ...

Create a grid.

Parameters:

Name Type Description Default
cols int

Number of columns.

required
rows int

Number of rows.

required
cell_size float | None

Size for square cells (sets both width and height).

None
cell_width float | None

Cell width (overrides cell_size).

None
cell_height float | None

Cell height (overrides cell_size).

None
origin tuple[float, float]

Top-left corner of the grid in pixels.

(0, 0)

num_columns property

num_columns: int

Number of columns.

num_rows property

num_rows: int

Number of rows.

cell_width property

cell_width: float

Width of each cell in pixels.

cell_height property

cell_height: float

Height of each cell in pixels.

cell_size property

cell_size: tuple[float, float]

Cell size as (width, height).

pixel_width property

pixel_width: float

Total width in pixels.

pixel_height property

pixel_height: float

Total height in pixels.

origin property

origin: Coord

Top-left corner of the grid.

source_image property

source_image: Image | None

The original source image (if created via from_image), or None.

cells property

cells: list[Cell]

All cells as a flat list (row by row, left to right).

rows property

rows: Iterator[list[Cell]]

Iterate over all rows (as lists of cells, top to bottom).

Example
for row_idx, row in enumerate(grid.rows):
    for cell in row:
        cell.add_dot(color="red" if row_idx % 2 == 0 else "blue")

columns property

columns: Iterator[list[Cell]]

Iterate over all columns (as lists of cells, left to right).

Example
for col_idx, col in enumerate(grid.columns):
    for cell in col:
        cell.add_dot(radius=0.02 * (col_idx + 1))

from_image classmethod

from_image(image: Image, cols: int | None = None, rows: int | None = None, cell_size: float = 10, cell_ratio: float = 1.0, cell_width: float | None = None, cell_height: float | None = None, origin: tuple[float, float] = (0, 0), load_layers: bool = True) -> Grid

Create a grid sized to match an image.

Parameters:

Name Type Description Default
image Image

Source image (will be resized to match grid).

required
cols int | None

Number of columns (calculates rows from aspect ratio). If None and rows is also None, derives both from image dimensions and cell size (fit-grid-to-image mode).

None
rows int | None

Number of rows (calculates cols from aspect ratio).

None
cell_size float

Base size of each cell in pixels.

10
cell_ratio float

Width-to-height ratio (e.g., 2.0 for domino cells).

1.0
cell_width float | None

Explicit cell width (overrides cell_size and cell_ratio).

None
cell_height float | None

Explicit cell height (overrides cell_size).

None
origin tuple[float, float]

Top-left corner of the grid.

(0, 0)
load_layers bool

Whether to load image data into cells.

True

Returns:

Type Description
Grid

A new Grid with image data loaded into cells.

row

row(index: int) -> list[Cell]

Get all cells in a specific row.

Parameters:

Name Type Description Default
index int

Row index (0-based).

required

Returns:

Type Description
list[Cell]

List of cells in that row (left to right).

Example
for cell in grid.row(0):  # Top row
    cell.add_dot(color="red")

column

column(index: int) -> list[Cell]

Get all cells in a specific column.

Parameters:

Name Type Description Default
index int

Column index (0-based).

required

Returns:

Type Description
list[Cell]

List of cells in that column (top to bottom).

Example
for cell in grid.column(0):  # Left column
    cell.add_dot(color="blue")

get

get(row: int, col: int) -> Cell | None

Safely access a cell by (row, col) index.

Like dict.get(), returns None instead of raising on out-of-bounds indices. Use grid[row][col] when you want IndexError on invalid indices.

Parameters:

Name Type Description Default
row int

Row index (0-based).

required
col int

Column index (0-based).

required

Returns:

Type Description
Cell | None

The Cell at that position, or None if out of bounds.

cell_at_pixel

cell_at_pixel(x: float, y: float) -> Cell | None

Get the cell containing a pixel position.

Parameters:

Name Type Description Default
x float

Horizontal pixel position.

required
y float

Vertical pixel position.

required

Returns:

Type Description
Cell | None

The Cell at that position, or None if outside grid.

region

region(row_start: int = 0, row_end: int | None = None, col_start: int = 0, col_end: int | None = None) -> Iterator[Cell]

Iterate over cells in a rectangular region.

Parameters:

Name Type Description Default
row_start int

Starting row (inclusive, default 0).

0
row_end int | None

Ending row (exclusive, default all rows).

None
col_start int

Starting column (inclusive, default 0).

0
col_end int | None

Ending column (exclusive, default all columns).

None

Yields:

Type Description
Cell

Cells in the region (row by row).

Example
# Top-left quarter
for cell in grid.region(row_end=grid.num_rows//2, col_end=grid.num_columns//2):
    cell.add_fill(color="blue")

border

border(thickness: int = 1) -> Iterator[Cell]

Iterate over cells on the grid border.

Parameters:

Name Type Description Default
thickness int

Border thickness in cells (default 1).

1

Yields:

Type Description
Cell

Cells on the border.

Example
for cell in grid.border():
    cell.add_fill(color="gray")

merge

merge(start: tuple[int, int] = (0, 0), end: tuple[int, int] | None = None) -> CellGroup

Merge a rectangular region of cells into a single virtual surface.

The returned CellGroup acts like a single large cell — it has all the same builder methods (add_dot, add_line, add_curve, etc.) and averaged data properties (brightness, color, rgb).

Both corners are inclusivemerge((0, 0), (2, 2)) selects a 3x3 block (rows 0-2, cols 0-2).

Parameters:

Name Type Description Default
start tuple[int, int]

Top-left corner as (row, col), inclusive. Default (0, 0).

(0, 0)
end tuple[int, int] | None

Bottom-right corner as (row, col), inclusive. Default (rows-1, cols-1) (entire grid).

None

Returns:

Type Description
CellGroup

A CellGroup spanning the selected region.

Example
header = grid.merge((0, 0), (1, grid.num_columns - 1))
header.add_fill(color="#333")
header.add_text("Title", font_size=0.5, color="white")

single = grid.merge((3, 3), (3, 3))  # one cell
block = grid.merge((0, 0), (2, 2))    # 3x3 block

merge_row

merge_row(index: int) -> CellGroup

Merge an entire row into a single virtual surface.

Parameters:

Name Type Description Default
index int

Row index (0-based).

required

Returns:

Type Description
CellGroup

A CellGroup spanning the full row.

Example
top = grid.merge_row(0)
top.add_fill(color="navy")
top.add_text("Header", font_size=0.5, color="white")

merge_col

merge_col(index: int) -> CellGroup

Merge an entire column into a single virtual surface.

Parameters:

Name Type Description Default
index int

Column index (0-based).

required

Returns:

Type Description
CellGroup

A CellGroup spanning the full column.

Example
sidebar = grid.merge_col(0)
sidebar.add_fill(color="gray")

every

every(n: int, offset: int = 0) -> Iterator[Cell]

Iterate over every Nth cell.

Cells are numbered left-to-right, top-to-bottom.

Parameters:

Name Type Description Default
n int

Select every Nth cell.

required
offset int

Starting offset (default 0).

0

Yields:

Type Description
Cell

Every Nth cell.

Example
# Checkerboard pattern (every 2nd cell)
for cell in grid.every(2):
    cell.add_fill(color="black")

checkerboard

checkerboard(color: str = 'black') -> Iterator[Cell]

Iterate over checkerboard pattern cells.

Parameters:

Name Type Description Default
color str

"black" for dark squares, "white" for light squares.

'black'

Yields:

Type Description
Cell

Cells in the checkerboard pattern.

Example
for cell in grid.checkerboard("black"):
    cell.add_fill(color="#333")

where

where(predicate: Callable[[Cell], bool]) -> Iterator[Cell]

Iterate over cells matching a condition.

Parameters:

Name Type Description Default
predicate Callable[[Cell], bool]

Function that takes a Cell and returns True/False.

required

Yields:

Type Description
Cell

Cells where predicate returns True.

Example
# Bright cells
for cell in grid.where(lambda c: c.brightness > 0.7):
    cell.add_dot(radius=0.3)

# Top half
for cell in grid.where(lambda c: c.row < grid.num_rows // 2):
    cell.add_fill(color="blue")

diagonal

diagonal(direction: Literal['down', 'up'] = 'down', offset: int = 0) -> Iterator[Cell]

Iterate over cells on a diagonal.

Parameters:

Name Type Description Default
direction Literal['down', 'up']

"down" for top-left to bottom-right, "up" for bottom-left to top-right.

'down'
offset int

Diagonal offset (0 = main diagonal).

0

Yields:

Type Description
Cell

Cells on the diagonal.

Example
for cell in grid.diagonal():
    cell.add_dot(color="red")

load_layer

load_layer(name: str, source: Layer | Image, mode: str = 'value') -> None

Load layer data from an image or layer into cell data.

Parameters:

Name Type Description Default
name str

Key to store data under in cell.data.

required
source Layer | Image

A Layer or Image to sample from.

required
mode str

How to store values: "value" - Store raw numeric value "normalized" - Store value / 255 (0-1 range) "hex" - Store hex color string (requires Image source)

'value'

Cell

Cell(grid: Grid, row: int, col: int, x: float, y: float, width: float, height: float)

Bases: Surface

A cell within a grid - the fundamental unit for placing art elements.

Cells provide: - Typed data access: cell.brightness, cell.color instead of dict lookups - Builder methods: cell.add_dot(), cell.add_line() for easy element creation (inherited from Surface) - Position helpers: Named positions like "center", "top_left", etc. (inherited from Surface) - Neighbor access: cell.right, cell.below for cross-cell operations

Basic usage
for cell in scene.grid:
    # Typed access to image data
    if cell.brightness > 0.5:
        cell.add_dot(color=cell.color)
Builder methods
cell.add_dot(radius=0.4, color="red")
cell.add_line(start="top_left", end="bottom_right")
cell.add_diagonal(start="bottom_left", end="top_right")
cell.add_fill(color="blue")
cell.add_border(color="gray")

Attributes:

Name Type Description
row int

Row index (0-based)

col int

Column index (0-based)

brightness float

Normalized brightness 0.0-1.0 (from loaded image)

color str

Hex color string (from loaded image)

rgb tuple[int, int, int]

RGB tuple (0-255 each)

alpha float

Transparency 0.0-1.0

Create a cell (typically called by Grid, not directly).

Parameters:

Name Type Description Default
grid Grid

The parent grid.

required
row int

Row index.

required
col int

Column index.

required
x float

Top-left corner x in pixels.

required
y float

Top-left corner y in pixels.

required
width float

Cell width in pixels.

required
height float

Cell height in pixels.

required

brightness property

brightness: float

Area-averaged brightness from 0.0 (black) to 1.0 (white).

Derived from the LANCZOS-resampled image (one pixel per cell), so each value represents the weighted average of all source pixels that fall within this cell's region. This smooths harsh transitions — e.g. a cell straddling a black/white border reads ~0.5.

For single-pixel sampling without averaging, use sample_brightness().

Returns 0.5 if no image is loaded.

Example
cell.add_dot(radius=0.02 + 0.08 * cell.brightness)

color property

color: str

Area-averaged hex color from the resampled image (e.g., "#ff5733").

Derived from the LANCZOS-resampled image (one pixel per cell), so it represents the blended color of all source pixels in this cell's region. Borders between contrasting colors will blend.

For single-pixel sampling without averaging, use sample_hex().

Returns "#808080" (gray) if no image is loaded.

Example
cell.add_dot(color=cell.color)

rgb property

rgb: tuple[int, int, int]

Area-averaged RGB color as a tuple of integers (0-255 each).

Derived from the LANCZOS-resampled image (one pixel per cell). Each channel is the weighted average of source pixels in this cell's region.

For single-pixel sampling without averaging, use sample_image().

Returns (128, 128, 128) if no image is loaded.

Example
r, g, b = cell.rgb
is_reddish = r > g and r > b

alpha property

alpha: float

Area-averaged transparency from 0.0 (transparent) to 1.0 (opaque).

Derived from the LANCZOS-resampled image (one pixel per cell), so it represents the blended alpha of all source pixels in this cell's region.

Returns 1.0 if no alpha data is loaded.

data property

data: dict[str, Any]

Raw data dictionary (for custom data or backwards compatibility).

Prefer typed properties (brightness, color, etc.) for standard data.

row property

row: int

Row index (0-based).

col property

col: int

Column index (0-based).

grid property

grid: Grid

The parent grid.

normalized_position property

normalized_position: RelCoord

(col, row) normalized to 0.0-1.0 within the grid.

Useful for position-based gradients and effects.

Returns:

Type Description
RelCoord

RelCoord(rx, ry) where both are in [0.0, 1.0].

above property

above: Cell | None

Cell above this one (north), or None if at edge.

below property

below: Cell | None

Cell below this one (south), or None if at edge.

left property

left: Cell | None

Cell to the left (west), or None if at edge.

right property

right: Cell | None

Cell to the right (east), or None if at edge.

above_left property

above_left: Cell | None

Cell diagonally above-left (northwest), or None if at edge.

above_right property

above_right: Cell | None

Cell diagonally above-right (northeast), or None if at edge.

below_left property

below_left: Cell | None

Cell diagonally below-left (southwest), or None if at edge.

below_right property

below_right: Cell | None

Cell diagonally below-right (southeast), or None if at edge.

neighbors property

neighbors: dict[str, Cell | None]

All neighbors as a dict (cardinal directions only).

neighbors_all property

neighbors_all: dict[str, Cell | None]

All 8 neighbors including diagonals.

sample_image

sample_image(rx: float = 0.5, ry: float = 0.5) -> tuple[int, int, int]

Read a single pixel from the original source image (no averaging).

Unlike rgb (which comes from the resampled, area-averaged image), this reads one pixel at full resolution. This preserves sharp edges — a cell on a black/white border returns pure black or pure white depending on where the sample point falls.

Parameters:

Name Type Description Default
rx float

Horizontal position within cell (0.0 = left edge, 1.0 = right edge).

0.5
ry float

Vertical position within cell (0.0 = top edge, 1.0 = bottom edge).

0.5

Returns:

Type Description
tuple[int, int, int]

RGB tuple (0-255 each).

Raises:

Type Description
ValueError

If the grid was not created from an image.

sample_brightness

sample_brightness(rx: float = 0.5, ry: float = 0.5) -> float

Read brightness of a single pixel from the original source image.

Unlike brightness (which is area-averaged from the resampled image), this reads one pixel at full resolution. Useful for images with sharp transitions where averaging would produce unwanted intermediate values.

Parameters:

Name Type Description Default
rx float

Horizontal position within cell (0.0 = left edge, 1.0 = right edge).

0.5
ry float

Vertical position within cell (0.0 = top edge, 1.0 = bottom edge).

0.5

Returns:

Type Description
float

Brightness value 0.0 (black) to 1.0 (white).

sample_hex

sample_hex(rx: float = 0.5, ry: float = 0.5) -> str

Read hex color of a single pixel from the original source image.

Unlike color (which is area-averaged from the resampled image), this reads one pixel at full resolution.

Parameters:

Name Type Description Default
rx float

Horizontal position within cell (0.0 = left edge, 1.0 = right edge).

0.5
ry float

Vertical position within cell (0.0 = top edge, 1.0 = bottom edge).

0.5

Returns:

Type Description
str

Hex color string (e.g., "#ff5733").

distance_to

distance_to(other: Cell | Entity | Coord | tuple[float, float]) -> float

Euclidean pixel distance from this cell's center to another position.

Accepts: Cell (uses center), Coord, or (x, y) tuple. Entity positions work too — just pass entity.anchor("center") or entity.position.

Parameters:

Name Type Description Default
other Cell | Entity | Coord | tuple[float, float]

A Cell, Coord, or (x, y) tuple.

required

Returns:

Type Description
float

Distance in pixels.

Cell extends Surface -- it inherits all 12 builder methods plus has image data, position helpers, and neighbor access.

Neighbor properties return Cells, not positions

cell.left, cell.right, cell.above, cell.below return Cell | None, not position coordinates. Use cell.center, cell.top_left, etc. for positions.


CellGroup

CellGroup(cells: list[Cell], grid: Grid)

Bases: Surface

A virtual surface spanning multiple grid cells.

CellGroup acts like a single large cell with merged bounds. It inherits all builder methods from Surface (add_dot, add_line, add_curve, etc.) and provides averaged data properties.

Created via grid.merge(), not directly:

```python
group = scene.grid.merge((0, 0), (0, 4))
group.add_fill(color=group.color)
group.add_text("Title", font_size=20)
```

Attributes:

Name Type Description
brightness float

Average brightness across constituent cells

color str

Average color across constituent cells

rgb tuple[int, int, int]

Average RGB across constituent cells

cells list[Cell]

The constituent Cell objects

Create a CellGroup from a list of cells.

Typically called by Grid.merge(), not directly.

Parameters:

Name Type Description Default
cells list[Cell]

The cells to merge into this group.

required
grid Grid

The parent grid.

required

brightness property

brightness: float

Average brightness across all constituent cells.

color property

color: str

Average color across all constituent cells as hex string.

rgb property

rgb: tuple[int, int, int]

Average RGB across all constituent cells.

alpha property

alpha: float

Average alpha across all constituent cells.

cells property

cells: list[Cell]

The constituent cells in this group.

grid property

grid: Grid

The parent grid.

A CellGroup is a virtual surface -- it has all the same add_* builder methods as a Cell, and averaged data properties from its constituent cells.