Ollin

Cracks

crack growth: cracks that branch at right angles divide the plane into city-block cells, and each crack drags a translucent grain wash across the open space on one side of it (CrackGrowth, noClear)

Run
swift run --package-path Examples Example-Patterns-Cracks
Source
Examples/Patterns/Cracks/Sketch.swift
import Ollin

//  Inspired by "Substrate" by Jared Tarbell (2003), complexification.net.
//  Original interpretation of the technique; no source code ported.

/// Crack growth: straight cracks race across the canvas, each recording its
/// angle into a raster grid. A crack that meets an older line stops, restarts
/// perpendicular to a random point on the existing pattern, and recruits one
/// more crack, so the plane subdivides into city-block cells. Each crack drags
/// a translucent one-sided wash across the open space beside it, so the cells
/// fill with watercolor as the map densifies.
///
/// The canvas never clears (`noClear()`): every frame lays only this frame's
/// marks, and the picture is the accumulation. The same seed always grows the
/// same city; `field.segments` is the whole street plan as plain line
/// segments, ready for the vector exports and the pen plotter.
@main
final class Cracks: Sketch {
    private let inks = [Color(hex: 0x1F5673), Color(hex: 0xC26D3F),
                        Color(hex: 0x8A9B68), Color(hex: 0x71486E),
                        Color(hex: 0xC7A94F), Color(hex: 0x9A4A4A),
                        Color(hex: 0x3E7C66)]
    private var field: CrackGrowth!

    override func setup() {
        noClear()
        seed(7)
        noStroke()
        field = CrackGrowth(width: width, height: height, seed: 7)
    }

    override func draw() {
        if frameCount == 1 { background(Color(hex: 0xF7F3EA)) }

        for mark in field.step(4) {
            // The wash: translucent grains crowding the crack, one ink per
            // crack for its whole life.
            let ink = inks[mark.crack % inks.count]
            for grain in CrackGrowth.grains(from: mark.point, to: mark.washExtent,
                                            gain: mark.gain) {
                fill(ink.withAlpha(grain.alpha))
                drawPoint(at: grain.position)
            }

            // The crack itself: a faint dark point with sub-pixel shiver, so
            // the line builds up grainy rather than ruled.
            fill(Color.black.withAlpha(0.33))
            drawPoint(mark.point.x + random(-0.33, 0.33),
                      mark.point.y + random(-0.33, 0.33))
        }
    }
}

↑↓ move ↩ open