Skip to content

Scenes & Grids

The Scene is your canvas, and the Grid gives it structure. Together they form the foundation of every PyFreeform artwork.

Creating Scenes

From an Image

Scene.from_image() loads a photo and divides it into a grid of cells, each sampling the image's colors and brightness.

from pyfreeform import Scene

scene = Scene.from_image("MonaLisa.jpg", grid_size=40, cell_size=10)

grid_size controls resolution — how many columns of cells across. More cells = more detail:

20 cells across

grid_size=20

40 cells across

grid_size=40

60 cells across

grid_size=60

Cell Ratio

cell_ratio changes cell proportions. A ratio of 2.0 makes cells twice as wide as tall:

Square cells

cell_ratio=1.0 (square)

Wide cells

cell_ratio=2.0 (wide)

Tall cells

cell_ratio=0.5 (tall)

From Scratch

Scene.with_grid() creates a grid with no image data — use position and math to drive visuals:

from pyfreeform import Scene, Palette

colors = Palette.midnight()
scene = Scene.with_grid(cols=15, rows=15, cell_size=22, background=colors.background)

for cell in scene.grid:
    nx, ny = cell.normalized_position
    radius = 0.091 + (nx * ny) * 0.364
    cell.add_dot(radius=radius, color=colors.primary, opacity=0.5 + nx * 0.5)

with_grid basic pattern

Dot size grows with position — no image needed.

Grid Selections

The grid offers powerful selection methods for targeting specific cells.

Row & Column

for cell in scene.grid.row(3):        # All cells in row 3
    cell.add_fill(color=colors.primary, opacity=0.4)

for cell in scene.grid.column(6):     # All cells in column 6
    cell.add_fill(color=colors.accent, opacity=0.4)

Row and column highlighting

Row 3 in coral, column 6 in amber, intersection highlighted.

Border

for cell in scene.grid.border(thickness=2):  # (1)!
    cell.add_fill(color=colors.accent, opacity=0.7)
  1. thickness controls how many rows/columns deep the border extends.

Border selection

The outer 2 rows/columns highlighted as a border.

Region

for cell in scene.grid.region(2, 6, 3, 9):  # (1)!
    cell.add_polygon(Polygon.hexagon(size=0.6), fill=colors.primary)
  1. region(row_start, row_end, col_start, col_end) — end is exclusive.

Region selection with hexagons

Hexagons placed only in the selected rectangular region.

Checkerboard & Diagonal

for cell in scene.grid.checkerboard("black"):
    cell.add_polygon(Polygon.diamond(size=0.7), fill=colors.primary)

for cell in scene.grid.checkerboard("white"):
    cell.add_dot(radius=0.15, color=colors.accent)

Checkerboard

Checkerboard: diamonds and dots alternate.

Diagonal

Every 3rd diagonal highlighted with fills.

Merging Cells

Merge a row, column, or rectangular region into a single CellGroup — a virtual surface that spans multiple cells:

title_bar = scene.grid.merge_row(0)
title_bar.add_fill(color=colors.primary, opacity=0.2)
title_bar.add_text("TITLE BAR", at="center", font_size=0.50, color=colors.accent, bold=True)

Merged title bar

Row 0 merged into a CellGroup with text overlay.

A CellGroup has all the same add_* methods as a Cell — it's a full Surface.

Other merge methods

  • grid.merge_col(i) — merge a full column
  • grid.merge(start, end) — merge any rectangular region. Both args are (row, col) tuples, both inclusive.

What's Next?

Now that you can create and navigate grids, learn how to read and use each cell's data:

Working with Cells →