Square Subdivision · How it works & code
Cuts a rectangle along the long edge into two pieces and repeats each of them until they reach depth or decide to stop at random.
Runtime logic
Visiting `/generative/subdiv`, DemoHost lazy-loads `demos/kits/kit-generative.js` (registry `mod: 'kit-generative'`) and calls `mount(host, { variant: 'subdiv' })`. mount resolves the variant to family `subdiv`; `withControls` builds the control panel (6 presets + sliders). `createCanvasStage` creates the `<canvas>` (HiDPI ≤ 2×, ResizeObserver) and the info panel. The family’s `build(S)` initialises state and returns the per-frame callback. Every `requestAnimationFrame`: read controls, advance a speed-scaled clock (dt ≤ 50 ms), run the frame callback; returned readouts go to the info panel. Changing parameters: colour/speed/size apply live; structural ones remount; leaving the page cancels rAF, removes listeners and frees resources.
Environment
Canvas 2D: 2D canvas drawing; requestAnimationFrame: Display-synced animation loop; ResizeObserver: Canvas follows container size; Pointer Events: Unified mouse/touch/pen events; devicePixelRatio: Crisp HiDPI rendering (capped at 2×) · react 18.3.1, react-router-dom 6.30.6, vite 5.4.21
Dependencies
react 18.3.1
Imported APIs
withControls — Wraps the demo in the shared control panel (presets, sliders, reset); hot-updates or remounts on change; paramsFromSpec — Turns the spec (colour/speed/size/count/extras) into control-panel parameter definitions; createCanvasStage — Shared Canvas 2D stage: canvas + info panel, HiDPI, one rAF loop, pointer state, idle auto-pointer; hueOf — Hue of a colour (0–360°); clamp — Clamps a value to a range; TAU — Constant 2π; fmt — Number formatting (fixed decimals); F — Formula block HTML; S.clear — Clears with the background colour (translucent = trails); S.onFrame — Registers a per-frame callback; its return value feeds the info panel; ctx.fillRect() — Fills a rectangle; ctx.strokeRect() — Strokes a rectangle; ctx.beginPath() — Starts a new path; ctx.moveTo() — Moves the pen without drawing; ctx.arcTo() — Adds an arc using tangent lines; ctx.closePath() — Closes the current sub-path; ctx.fill() — Fills the current path; ctx.stroke() — Strokes the current path; ctx.arc() — Adds a circular arc (center, radius, start/end angle); ctx.drawImage() — Draws an image or offscreen canvas; ctx.save() — Saves the drawing state (transform, styles, clip); ctx.rect() — Adds a rectangle to the path; ctx.clip() — Turns the current path into the clipping region; ctx.restore() — Restores the saved drawing state
Source code
frontend/src/demos/kits/kit-generative.js: FAM.subdiv (L403-L417)
Code walkthrough
L403-417: Calls the factory `gFam()` to produce the full definition (spec + doc + build) from the options below. L403-403: Title shown in the info and control panels: “Recursive Subdivision”. L403-403: Range/default of the size slider: Partition width. L403-403: Range/default of the count slider: Recursive Depth. L404-404: Extra demo-specific slider/toggle: Fill Scale. L404-404: Default main colour. L404-404: Help text of the count parameter. L404-404: Help text of the extra parameter. L404-404: Preset buttons: name → parameter overrides (5). L405-405: Body of the “Notes” section. L405-405: Main formula of the “Formula” section. L405-405: Second formula line. L406-417: Per-frame draw function: draws with Canvas 2D `fillRect`, `strokeRect`; math via `Math.floor`, `Math.sin`, `Math.round`; returns readouts `mode`, `cell` to the info panel.
Principles
Point position = 0.5 + 0.3 · sin (0.4t + seed) Leaf count ≤ 2^Depth Recursive subdivision (Mondrian style): Cut a rectangle along the long edge into two pieces and repeat each piece until it reaches depth or randomly decides to stop. The split position moves slowly over time, so the grid stretches; the rectangle in which the pointer is located is bright. Generative Art = a simple mathematical rule + Random number/噪声,让电脑自己「画」出图案;改变参数就是改变规则。