#### <sup>[Ollin](../index.md) → [Guide](index.md) → Chapter 6</sup>

---

# 6. Grids and repetition

<img src="Images/06-GridsAndRepetition/RoseWall.jpg" alt="A five-by-five wall of cut-paper medallions on cream, each a colored disc with cream arms folded around it, the color drifting diagonally from coral through amber and green to deep indigo" width="560">

Grids give generative art its order, and repetition gives it rhythm. The tools here place, turn, fold, and clip your shapes. The twenty-five medallions at the top all come from one crooked arm, drawn once and then placed, turned, folded, and trimmed. After them come a whole sketch run inside a cell, grids of hexagons and triangles, uneven panels split by recursion, and a spiral of numbers.

## One loop, not two: `grid`

You have already built grids twice, the long way. [Chapter 2](02-color.md)'s color field and [Chapter 4](04-randomness.md)'s disorder grid both did the same chores. Pick a margin, then divide the leftover width into cells. Run a loop inside a loop, and rebuild each cell's x and y from the indices. Those chores are what `grid` is for:

```swift
import Ollin

final class ColorTiles: Sketch {
    let ramp = Ramp([
        Color(hex: 0x14213D), Color(hex: 0x5E60CE),
        Color(hex: 0xF77F00), Color(hex: 0xFCBF49),
    ])

    override func draw() {
        background(Color(hex: 0xF2EDE4))
        noStroke()
        for cell in grid(columns: 12, rows: 12, padding: 70, gutter: 10).cells {
            let diagonal = Double(cell.column + cell.row) / 22
            fill(ramp.color(at: diagonal))
            drawRect(cell.frame)
        }
    }
}
```

<img src="Images/06-GridsAndRepetition/ColorTiles.jpg" alt="A twelve-by-twelve grid of square tiles fading from midnight blue to amber along the diagonal" width="560">

One loop does it. `grid(columns:rows:padding:gutter:)` lays a grid over the canvas, where `padding` is the outer margin and `gutter` the gap between tiles. Its `cells` is a list you loop, and every cell arrives knowing everything about itself. It carries its `frame`, the rectangle to draw, plus its `center`, its `column` and its `row`. Those indices are the point. The old nested loops existed mostly so you would have a column number and a row number in hand. Here every cell carries its own, so the indexed tricks stay one-loop simple. A checkerboard from `(cell.column + cell.row) % 2` is one, and this diagonal fade is another. The 22 in the code is the far corner, column 11 plus row 11.

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="Images/06-GridsAndRepetition/GridAnatomy-dark.jpg">
  <img src="Images/06-GridsAndRepetition/GridAnatomy.jpg" alt="Grid anatomy: cells with padding and gutter labeled and one cell's frame and center called out; beside them, points as a dot per cell and as a lattice spanning the edges" width="680">
</picture>

The grid offers two things to loop, and you pick by what you are drawing. `cells` are the tiles. `points` are the dots, one per cell center by default. Pass `distribution: .spanning` for a lattice that reaches the edges, the layout you want when the sketch *is* a grid of dots. A dot field is two lines:

```swift
for p in grid(columns: 12, rows: 12, padding: 70, distribution: .spanning).points {
    drawCircle(center: p.position, radius: 6)
}
```

Two more things are useful to know. `grid(...)` covers the whole canvas, but `Grid(in: someRectangle, ...)` lays one inside any rectangle. Since a cell's `frame` is itself a rectangle, grids nest. A grid where each cell holds a smaller grid is one more loop, and a classic look. For margins that differ per edge, `padding:` takes more than a bare number. It also takes `.symmetric(horizontal: 40, vertical: 20)`, or any mix of edges through `Insets`.

> **Swift note.** `grid(...).cells` chains a call and a property, building the grid and then asking for its cells. You can also drop the `.cells` and loop the grid itself: `for cell in grid(columns: 12, rows: 12)`. As far as Swift is concerned a grid *is* its cells. It walks them in the same order, working each one out as the loop asks for it, so it never builds the array. Everything Swift can do to a list it reads, it can do to a grid. So after `let g = grid(columns: 12, rows: 12)`, `g.count` works too. The `cell` in the loop is a small value with named parts you read with a dot, such as `cell.frame` and `cell.column`. The `drawRect` call accepts the frame whole, with no unpacking into x and y. `p.position` is a `Vector2`, a pair of coordinates carried as one value, and `drawCircle(center:radius:)` takes it directly. [Chapter 10](10-vectors.md) teaches vectors properly, and until then you can read `Vector2` as "a point".

## Moving the paper: `translate`, `rotate`, and `scale`

The grid raises a question. How do you draw something *rotated* inside a cell? `drawRect` and friends do not take an angle. The answer is one of the oldest ideas in computer graphics. You do not rotate the shape, you rotate the *paper*.

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="Images/06-GridsAndRepetition/TransformSteps-dark.jpg">
  <img src="Images/06-GridsAndRepetition/TransformSteps.jpg" alt="Four panels drawing the same flag with the same call: untransformed at the origin, then translated, then rotated a twelfth of a turn, then scaled up" width="680">
</picture>

Three calls move the paper. `translate(x, y)` slides the origin, the point that counts as (0, 0), somewhere else. `rotate(angle)` turns the paper around that origin (angles work like [Chapter 3](03-motionandtime.md): `.tau / 4` is a quarter turn). `scale(factor)` stretches it. After any of them, every drawing call is measured on the moved paper, which is what the diagram shows. All four flags are the same `drawRect` at the same numbers, drawn on paper that had been slid, turned, and stretched first.

The moves accumulate as `draw()` runs, and reset at the start of every frame. Within a frame you also need the undo, and that is `withState`:

```swift
withState {
    translate(cell.center)      // origin to this cell's middle
    rotate(.tau / 8)            // paper turns an eighth
    drawRect(-40, -40, 80, 80)  // a tilted square, centered on (0, 0)
}                               // paper snaps back as if nothing happened
```

Everything inside the braces draws on the moved paper. At the closing brace the paper is restored, along with any `fill` or `stroke` you changed inside. This is the cell-drawing recipe you will use for the rest of the guide. Translate to the cell's center, turn or stretch as the sketch demands, then draw *around the origin*. Coordinates like `(-40, -40)` straddle (0, 0). Put the recipe in a grid, add a seeded pick from [Chapter 4](04-randomness.md), and identical parts start composing figures nobody drew:

```swift
import Ollin

final class Pinwheels: Sketch {
    let ink = Color(hex: 0x232020)
    let accent = Color(hex: 0xC1272D)

    override func draw() {
        randomSeed(11)
        background(Color(hex: 0xF2EDE4))
        noStroke()

        for cell in grid(columns: 10, rows: 10, padding: 70).cells {
            let quarter = randomChoice([0, 1, 2, 3])
            withState {
                translate(cell.center)
                rotate(Double(quarter) * .tau / 4)
                fill(random() < 0.12 ? accent : ink)
                let h = cell.frame.width / 2
                drawPolygon([Vector2(-h, -h), Vector2(h, -h), Vector2(-h, h)])
            }
        }
    }
}
```

<img src="Images/06-GridsAndRepetition/Pinwheels.jpg" alt="A ten-by-ten field of black right triangles at random quarter turns, a few in red; pinwheels, hourglasses, and arrows emerge from the repetition" width="560">

It is one right triangle, half a cell. `drawPolygon` draws a closed shape through a list of points, and here the list is the triangle's three corners. There are four possible spins, and the neighbors do the rest. Hourglasses, pinwheels, and arrows assemble themselves wherever the spins happen to agree. Nobody placed those figures. That is the effect this chapter keeps returning to. It only works because every triangle is the same size in the same place in its cell, so any two spins fit together. [Chapter 7](07-tiles.md)'s Truchet tiles rest on the same fact.

> **Swift note.** `withState { ... }` takes a block of code in braces, like `draw()` itself. Running the block with the paper moved and then restoring it is the whole trick. `random() < 0.12 ? accent : ink` is the compact if, which reads as the condition, then the value when true, then the value when false.

## Symmetry by hand: rotating around the center

Rotating the paper around a cell gave a quilt. Rotate around the canvas center instead, and repetition becomes symmetry. Every sketch carries that center as a property called `center`, which is the `Vector2` at `width / 2, height / 2` and saves you writing it out:

```swift
import Ollin

final class Rosette: Sketch {
    let ink = Color(hex: 0x232020)
    let accent = Color(hex: 0xE4572E)

    override func draw() {
        background(Color(hex: 0xF2EDE4))
        translate(center)

        for _ in 0..<12 {
            rotate(.tau / 12)
            drawArm()
        }
    }

    func drawArm() {
        stroke(ink)
        strokeWeight(5)
        drawLine(70, 0, 340, 0)
        drawLine(250, 0, 300, -52)

        noStroke()
        fill(accent)
        drawCircle(340, 0, 26)
        fill(ink)
        drawCircle(300, -52, 13)
        drawCircle(160, 0, 9)
    }
}
```

<img src="Images/06-GridsAndRepetition/Rosette.jpg" alt="A twelve-fold rosette: one branching arm with disks at its tips, repeated by rotation into a botanical snowflake" width="560">

This time there is no `withState`, on purpose. Each `rotate(.tau / 12)` *adds* to the last. So the twelve copies of the arm land a twelfth of a turn apart and close the circle. The arm itself is deliberately lopsided, a stem along the x axis, one branch reaching up, disks of different sizes. A symmetric arm makes a boring rosette. The symmetry comes from the repetition, so the part is free to be as crooked as it likes. Change both 12s to 5 or 48, redraw the arm, drop `time` into the rotation. Every mandala, snowflake, and kaleidoscope pattern you have seen is some cousin of this loop.

> **Swift note.** `func drawArm()` declares a helper function on your sketch, a named block you call like any built-in. It is the same move as [Chapter 4](04-randomness.md)'s `roll()`. Pulling the arm out of the loop keeps `draw()` readable and gives you one place to redesign the arm.

## The fold, done for you: `symmetry`

Write that loop yourself once, because it shows you what symmetry is. After that there is a shortcut, and it can do something the loop makes awkward.

```swift
symmetry(8)                  // every draw call now happens eight times
symmetry(8, mirrored: true)  // sixteen times, alternate copies flipped
noSymmetry()                 // back to normal
```

Like `fill` or a transform, `symmetry` is drawing state. So it applies to everything you draw until you turn it off, and `withState { }` scopes it. Once it is on you stop thinking about repetition. You draw one wedge, and every circle, line, and shape in it lands in all the folds at once.

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="Images/06-GridsAndRepetition/Kaleidoscope-dark.jpg">
  <img src="Images/06-GridsAndRepetition/Kaleidoscope.jpg" alt="Three panels: a single small crooked wedge with a red dot at its tip, the same wedge under eightfold symmetry forming a snowflake, and under mirrored eightfold symmetry forming a denser one with paired reflections" width="680">
</picture>

The mirrored form is the one to use. A plain rotation copies your wedge around like a pinwheel, and every copy still leans the same way. Mirroring flips alternate copies, so neighbors face each other and the seams between them close. That is the difference between a pinwheel and a kaleidoscope. Doing it by hand means a negative `scale` on every other copy. That flip also runs each shape's corners the other way round, a mess you now do not have to write.

The folds pivot on the origin as it stands at the call, the top-left corner unless you `translate` first. And because it is drawing state rather than a loop you write, a whole composition folds as easily as a single arm.

## Drawing inside a shape: `withClip`

Repetition fills space. Sometimes you want it to fill *only part* of the space, a part with a shape of its own: a star, a disc, a letter. `Profile.star` hands back the corners of a star around (0, 0), and `Shape` turns a list of points into a closed outline:

```swift
let star = Shape(Profile.star(points: 5, outerRadius: 300, innerRadius: 130)
                     .map { center + $0 })     // five points, around the middle
withClip(star) {
    // everything here lands only inside the star
}
```

> **Swift note.** `.map { center + $0 }` runs the code in the braces once per point and hands back the new list. Inside the braces, `$0` stands for that point. `center + $0` slides each corner to the middle of the canvas, since adding two `Vector2` values adds their x's and their y's. [Chapter 10](10-vectors.md) covers that arithmetic properly.

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="Images/06-GridsAndRepetition/ClipRegions-dark.jpg">
  <img src="Images/06-GridsAndRepetition/ClipRegions.jpg" alt="Three panels of the same diagonal orange stripes: confined to a star, confined to a circle, and confined to both at once so only the overlap of star and circle is striped" width="680">
</picture>

`withClip` takes a `Shape`, a `Rectangle`, or a `Circle`, and confines everything drawn inside the block to that region. You never work out the intersection yourself. The stripes in the figure are the same handful of long diagonal lines in all three panels. They are drawn straight past the edges, and the region decides what survives.

Nesting is the other half. A clip inside a clip keeps only what falls in both. So the third panel stripes only where the star lies inside the circle, with no geometry on your part. Letters make good clips too, since [Chapter 8](08-words.md)'s `textToShapes` hands back shapes, so you can pour a whole pattern into a word.

## Putting it together: a wall of rosettes

Now you can build the image at the top. The plan uses the grid, the transforms, symmetry, and a clip, in the order you met them. A grid hands you the blocks, and inside each one the transforms place, turn, and shrink the work. Then a clip opens around the arm, and inside it symmetry folds the arm into a medallion cut to a disc. Make a new file, `MySketches/RoseWall.swift`:

```swift
import Ollin

final class RoseWall: Sketch {
    @Param("Columns", 2...8) var columns = 5
    @Param("Seed", 1...9999) var wallSeed = 7
    @Param("Mirrored") var mirrored = true

    let paper = Color(hex: 0xF2EDE4)
    let ink = Color(hex: 0x232020)
    let ramp = Ramp([
        Color(hex: 0xE4572E), Color(hex: 0xF3A712),
        Color(hex: 0x2A9D8F), Color(hex: 0x3D348B),
    ])

    override func draw() {
        seed(wallSeed)
        background(paper)

        let blocks = grid(columns: columns, rows: columns, padding: 60, gutter: 18)
        for cell in blocks.cells {
            let radius = cell.frame.width / 2
            let folds = [5, 6, 8, 12][Int(random(4))]
            let reach = Double(cell.column + cell.row) / Double(2 * columns - 2)

            withState {
                translate(cell.center)
                rotate(random(.tau))
                scale(random(0.88, 1.0))

                noStroke()
                fill(ramp.color(at: reach))
                drawCircle(0, 0, radius)

                withClip(Circle(center: .zero, radius: radius)) {
                    symmetry(folds, mirrored: mirrored)
                    drawArm(radius: radius)
                }
            }
        }
    }

    // One arm, deliberately lopsided, drawn well past the edge of its block so
    // the clip is what gives every medallion its rim.
    func drawArm(radius: Double) {
        stroke(paper)
        strokeWeight(radius * 0.07)
        strokeCap(.round)
        drawLine(radius * 0.10, 0, radius * 1.30, 0)
        drawLine(radius * 0.58, 0, radius * 0.94, -radius * 0.34)

        noStroke()
        fill(paper)
        drawCircle(radius * 1.30, 0, radius * 0.10)
        fill(ink)
        drawCircle(radius * 0.94, -radius * 0.34, radius * 0.05)
        drawCircle(radius * 0.36, 0, radius * 0.045)
    }

    override func mousePressed() {
        wallSeed += 1
    }
}
```

Run it, then click. What each part contributes:

- `blocks.cells` is the one loop. Each cell arrives knowing its own `frame` and `center`, so nothing here rebuilds an x and a y from indices. The sum `cell.column + cell.row` is the diagonal you already used for the color tiles.
- `withState` is what makes the block independent. The position, the turn, the shrink, the fold, and the clip inside it are all undone at the closing brace. So the next cell starts fresh.
- `translate` then `rotate` then `scale` is the usual order, and the order matters. The turn and the shrink both happen around the cell's center because `translate` moved the origin there first.
- The arm reaches to `radius * 1.30`, well outside its own block. That is deliberate. The clip is what cuts it back, which is why every medallion has a crisp rim and no two rims are cut the same way.
- `folds` is one of four counts, picked by `[5, 6, 8, 12][Int(random(4))]`. The inner `Int(random(4))` rolls 0, 1, 2, or 3, since `Int(...)` drops the fraction as in [Chapter 4](04-randomness.md)'s walk. The brackets then take that entry of the list. The clip is `Circle(center: .zero, radius: radius)`, and `.zero` is the `Vector2` (0, 0). That point is the cell's center once `translate` has moved the origin there.
- `symmetry(folds, mirrored:)` is inside the clip, so the fold happens to the arm and not to the disc under it. Turn `Mirrored` off and the medallions become pinwheels: same arms, but every copy leaning the same way, and the seams between them stop closing.
- Calling `seed(wallSeed)` at the top of `draw()` pins the fold counts, the turns, and the shrinks, so the wall holds still. It is the reseed-every-frame fix from [Chapter 4](04-randomness.md). The Seed parameter, or a click, deals a whole new wall.

When a wall is a keeper, export it as a still:

```sh
swift run OllinLive MySketches/RoseWall.swift --export wall.png
```

Before moving on, make it yours:

- Redraw `drawArm`. It is the one place the sketch's character comes from, and a straighter or busier arm changes all twenty-five medallions at once.
- Swap `[5, 6, 8, 12]` for `[3, 4]` and the wall goes from lace to heraldry.
- Give the disc a stroke in `ink` and drop the gutter to `4`, so the medallions crowd their neighbors like tiles rather than floating.
- Drive `reach` from `noise` at the cell center instead of the diagonal, and the color arrives in patches ([Chapter 5](05-noise.md)) rather than a clean gradient.

## A cell that is a whole canvas: `withViewBox`

The wall moved the paper into each cell by hand and clipped what it drew there. A clip keeps drawing inside a region. A view box goes one step further and moves the coordinates too, so the block inside believes the region *is* the canvas. In the listing, `drawPetals()` stands for a drawing of your own, written for the whole canvas, under whatever name you gave it:

```swift
for cell in grid(columns: 3, rows: 2, padding: 40).cells {
    withViewBox(cell.frame) {
        randomSeed(cell.column + cell.row * 3)
        drawPetals()
    }
}
```

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="Images/06-GridsAndRepetition/ViewBoxSheet-dark.jpg">
  <img src="Images/06-GridsAndRepetition/ViewBoxSheet.jpg" alt="Six boxes on one canvas in two rows of three, each holding the same ring of petals under a different seed, each with its own colored wash" width="680">
</picture>

Inside that block `width` and `height` still report the whole canvas, and `center` is still the middle of it. A circle at `(width / 2, height / 2)` lands in the middle of the cell. That is the point. You hand a sketch written for the whole window to a box, and it runs there unchanged.

Two more things are quietly remapped so that stays true. `background` fills the box rather than the whole canvas. The canvas belongs to every box at once, and one of them wiping it would take the others with it. And the mouse arrives in the box's own coordinates, so an interactive sketch works in each box separately.

The virtual canvas has the *sketch's* shape, not the box's, so in a box shaped like the canvas everything lands as drawn. When the shapes differ, the `fit:` argument of `withViewBox` decides, using the same words a picture uses in [Chapter 9](09-pictures.md). `.contain`, the default, leaves the box showing along two edges, `.cover` fills it and crops, and `.stretch` squashes.

Labels belong outside the block, in canvas coordinates, or they get scaled down with everything else, so a caption under each box stays one size.

Change one number, run six of it, and the seeds that do not work show at once.

## Other ways to divide and walk a grid

The wall's grid divided the canvas into equal blocks and read them the way a page is read, left to right, top to bottom. Neither is the only choice, and the wall needs no other. Hexagons and triangles divide the plane evenly too, a layout wants panels of different sizes, and a picture of numbers wants a different reading order.

### Grids that aren't square: `hexGrid` and `triangleGrid`

`grid` divides a rectangle into rectangles, which covers a great deal but not everything. Two more regular divisions come with Ollin, and both read the same way. Ask for the cells, then loop over them once.

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="Images/06-GridsAndRepetition/OtherGrids-dark.jpg">
  <img src="Images/06-GridsAndRepetition/OtherGrids.jpg" alt="Four panels: a honeycomb tinted by ring distance from one cell, a field of alternating up and down triangles, a rectangle split recursively into unequal panels, and a carved maze" width="680">
</picture>

```swift
for cell in hexGrid(columns: 12, rows: 10, gutter: 6).cells {
    drawPolygon(cell.corners)
}
for cell in triangleGrid(columns: 21, rows: 10).cells {
    fill(cell.pointsUp ? .white : .black)
    drawPolygon(cell.vertices)
}
```

`hexGrid` and `triangleGrid` are the other two regular tilings, the only other *regular polygons* that cover a plane with no gaps and no overlaps. Plenty of irregular shapes manage it, which is [Chapter 7](07-tiles.md)'s subject. Hexagons cannot stretch the way a rectangle can. So the block keeps its true proportions and centers itself, rather than distorting to fill your bounds. A hex cell hands you its `corners` for `drawPolygon`, so a whole honeycomb is two lines. A triangle cell says whether it `pointsUp` and hands you its three `vertices`, and the compact if from the pinwheels turns that answer into a color. The hex grid also knows its own geometry. Keep the grid in a `let`, and its `distance(from:to:)` counts rings between two of its cells. That is what colors the first panel, and what a board game needs.

The figure's other two panels are divisions of another kind. The `subdivide` panel splits a rectangle into unequal panels by recursion, which comes next, in [Uneven panels by recursion](#uneven-panels-by-recursion-subdivide). The maze is carved by `maze(columns:rows:)`, and [Chapter 7](07-tiles.md#one-route-between-any-two-perfect-mazes) sets it beside the maze of diagonals and says what makes it perfect.

### Uneven panels by recursion: `subdivide`

`subdivide` splits a rectangle in two, then splits the halves, and keeps going until the pieces hit `minSize` or a coin says stop. It is for layouts: a poster's panels, a comic's frames, a wall of windows, anything that should read as designed rather than tabulated. The split is the binary space partition of computer graphics, put to work on a page. A space is cut in two, and each half is cut again. The `subdivide` panel of the figure in [Grids that aren't square](#grids-that-arent-square-hexgrid-and-trianglegrid) is one such split. Every panel is a cell with a `frame`, so the loop reads like a grid's:

```swift
for cell in subdivide(minSize: 90, chance: 0.75) {
    drawRect(cell.frame)
}
```

`chance` is the coin. At `1` every piece splits until `minSize` or the depth limit stops it, and lower values leave some panels large. [Tiling and layout](../docs/drawing/tiling.md) has the split styles.

### Walking a grid: numbers in a spiral

Walk the same grid as a square spiral instead, from the middle outward, writing the whole numbers one per cell. The grid becomes a picture of the numbers. Mark the cells whose number passes some test. Mark the primes, and the marks fall on diagonal lines. Stanisław Ulam found this in 1963, and the picture has been redrawn ever since.

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="Images/06-GridsAndRepetition/NumbersInASpiral-dark.jpg">
  <img src="Images/06-GridsAndRepetition/NumbersInASpiral.jpg" alt="Two panels: a seven by seven grid with the numbers 1 to 49 written in a spiral and the walk drawn under them, and a square sixty-one cells on a side with only the primes marked as dots, falling along visible diagonals" width="680">
</picture>

```swift
let spiral = ulamSpiral(size: 101)
noStroke(); fill(.white)
for point in spiral.primePoints { drawCircle(center: point, radius: 3) }
```

The lines are not a mystery once you see where they come from. Step diagonally in a square spiral and the number under you grows by a quadratic. That is a rule with a squared term, so each step adds more than the last. So a diagonal *is* a quadratic, and a crowded one is a quadratic that keeps returning primes. Mathematics has known such polynomials since Euler. What the picture does is make them visible.

The primes are only the famous test. To ask your own question, read `numbers`, the number in every cell in the grid's own order. Or hand `points(where:)` any test at all:

```swift
for point in spiral.points(where: { $0 % 7 == 0 }) { drawCircle(center: point, radius: 2) }
```

`start` is the argument to animate, as in `ulamSpiral(size: 101, start: 1 + frameCount)`. Counting from somewhere other than 1 moves every number, so the diagonals break up and re-form. It is the same fact seen from a different place. [Ulam spiral](../docs/generators/ulamspiral.md) is the reference.

## Where this comes from

The paper-moving transform model goes back to the earliest days of computer graphics, and reached creative coding through Processing's `pushMatrix`/`popMatrix`. The pinwheel quilt is older than all of it, pieced by quilters long before anyone had a coordinate system to rotate. The kaleidoscope is younger than it looks: David Brewster patented one in 1817, and it became a craze within a year.

The spiral of numbers is Stanisław Ulam's, drawn on a notepad during a talk in 1963. He published it the year after with Myron Stein and Mark Wells. Martin Gardner's column carried it to everybody else, and it has been redrawn ever since. Full credits are in the project's [attribution notes](../attribution.md).

## Go deeper

- [Geometry](../docs/drawing/geometry.md): the full `Grid` reference (spanning points, nesting, picking one cell by column and row), `Insets`, and `Rectangle`.
- [Ulam spiral](../docs/generators/ulamspiral.md): the walk, reading any test off it, and the prime helpers behind the marks.
- [Drawing](../docs/drawing/drawing.md): the transform stack in detail, and `pushState`/`popState` (the unscoped siblings of `withState`).
- [Kaleidoscope symmetry](../docs/drawing/drawing.md#symmetry): the full reference for `symmetry`/`noSymmetry`, including which drawing paths fold and which do not. The [`Patterns/Kaleidoscope`](../examples/patterns/kaleidoscope.md) example draws a single arm and lets the folds do the rest.
- [Clipping](../docs/drawing/drawing.md#clip): the reference, including how clips interact with layers and what vector export does with them. The [`Shapes/Clipping`](../examples/shapes/clipping.md) example sweeps a lens across a striped star.
- [Tiling and layout](../docs/drawing/tiling.md): every argument for `HexGrid`, `TriangleGrid`, `Subdivision`, and `Maze`. That includes hex orientation and picking, the quadtree split style, all three maze algorithms, and the longest-path helper.
- Appendix B draws this chapter's math, one picture per idea: [Angles and circles](b-justenoughmath.md#n-copies-close-the-circle), [Moving the paper](b-justenoughmath.md#moving-the-paper).
- Worked examples: [`Patterns/Grid`](../examples/patterns/grid.md) (the grid helper's tour), `Patterns/Kaleidoscope`, and `Shapes/Clipping`.
- The Rojo homages in [`Examples/Recreations/VicenteRojo/`](../examples/recreations/vicenterojo/index.md): `Negaciones` is one letter on a grid of panels, each a different negation of it. The panels are painted over one at a time. `MexicoBajoLaLluvia` is a grid that covers the square, every cell painted and one mark set on it, with the diagonal deciding the kind. In `Senales`, one sign stands on the middle line, and every piece of it is mirrored. Each worked shape then gets its texture inside a clip of its own outline. So on the whole canvas, only the marks of the painter's hand are not mirrored.
- The Winiarski homage [`Obszar`](../examples/recreations/ryszardwiniarski/obszar.md): a grid in which the only decision left in each cell is a throw. It is filled cell by cell from whichever corner chance drew. So the grid is the whole of the rule, and the picture is its record.
- The Sharif homage [`DotsLinesForms`](../examples/recreations/hassansharif/dotslinesforms.md): a table whose every cell is its column's line laid over its row's. So the grid is a multiplication table of marks. The table is drawn three times across one sheet, as dots, as lines, and as squares.
- The Schuh homages in [`Examples/Recreations/OwenSchuh/`](../examples/recreations/owenschuh/index.md): `CountingTheRationals` fills a table of fractions along its diagonals rather than row by row. Going by diagonals is how a single walk reaches every cell of a grid with no edge. The sketch weaves the result, so a crossing shows whichever ribbon the walk let pass. `DiagonalArgument` reads the diagonal of a grid of digits and writes a row that no row of the grid can equal.
- The Bonačić homage [`DynamicObject`](../examples/recreations/vladimirbonacic/dynamicobject.md): a 32 by 32 grid sorted into groups by `row ^ column`, each cell's row and column combined bit by bit. Every group has one cell in each row and each column. Lighting a few groups at a time makes rings, pairs of squares, and triangles, always symmetric about the diagonal.
- The Mohamedi homage [`Registers`](../examples/recreations/nasreenmohamedi/registers.md): a grid with one axis taken away. The sheet is nothing but horizontals, and the whole picture is in how their intervals change. Within each register every interval is the last times a ratio, so the lines gather against a heavy line and open away from it. A field of parallels then reads as planes seen edge on.
- The Gego homage [`Tejedura`](../examples/recreations/gego/tejedura.md): a grid nobody drew. The columns are the strips a print was cut into, and the rows are the strips of foil woven through them. No two strips are the same width. Each cell shows whichever strip is on top there, either the foil or the print's own lines cut to that cell.
- The Farmanfarmaian homage [`Geometric`](../examples/recreations/monirfarmanfarmaian/geometric.md): a grid of triangles ruled across the whole sheet and used as the ruler for everything after it. The hexagons, the spokes, and the filled triangles all sit on its points. The hatching sits on the same grid cut finer, so the export can be checked point by point against the grid.
- The Sato homage [`StraightLines`](../examples/recreations/osamusato/straightlines.md): copying, shifting, mirroring, and rotating as the whole method of a picture. Each lid of the eye is one bent line copied five times, each copy shifted up from the last. The blocks around it are one block turned sixteen times, and the whole eye is mirrored both ways.
- Next door: [Chapter 7](07-tiles.md) keeps the grid and changes what goes in the cells, so that neighboring cells have to agree with each other.

---

[Contents](index.md#contents) · Previous: [Chapter 5, Noise](05-noise.md) · Next: [Chapter 7, Tiles that cover the plane](07-tiles.md)
