From 7ce35f2b2c9b61cb6420fdce8ff844e950d90629 Mon Sep 17 00:00:00 2001 From: Vibe Nuage Agent Date: Mon, 5 Oct 2026 15:04:46 +0000 Subject: [PATCH 1/2] docs: per-example markdown docs with generated SVGs and a generate:examples task - Add doc/examples// with a minimal README and generated SVGs for cubic-disarray and noodle-love. - Add examples/update-example-svgs.sh and a deno task generate:examples that regenerates all example SVGs deterministically. - Refactor examples/noodle_love.ts to the Settings/Vormen runner with a fixed seed so its output is reproducible. - Rename doc/cubic-disarray.svg to doc/readme-example.svg to clarify it is the README's example, and document the task in the README. - Add an integration test asserting deterministic regeneration. Co-authored-by: berkes --- README.md | 17 ++- .../integration/generate_examples_test.ts | 39 ++++++ deno.json | 1 + doc/examples/cubic-disarray/README.md | 13 ++ doc/examples/cubic-disarray/svg/01.svg | 1 + doc/examples/cubic-disarray/svg/02.svg | 1 + doc/examples/noodle-love/README.md | 10 ++ doc/examples/noodle-love/svg/01.svg | 39 ++++++ ...{cubic-disarray.svg => readme-example.svg} | 0 examples/noodle_love.ts | 132 +++++++++++------- examples/update-example-svgs.sh | 8 ++ 11 files changed, 206 insertions(+), 55 deletions(-) create mode 100644 __tests__/integration/generate_examples_test.ts create mode 100644 doc/examples/cubic-disarray/README.md create mode 100644 doc/examples/cubic-disarray/svg/01.svg create mode 100644 doc/examples/cubic-disarray/svg/02.svg create mode 100644 doc/examples/noodle-love/README.md create mode 100644 doc/examples/noodle-love/svg/01.svg rename doc/{cubic-disarray.svg => readme-example.svg} (100%) create mode 100755 examples/update-example-svgs.sh diff --git a/README.md b/README.md index 9d09cb9..7bb3f9a 100644 --- a/README.md +++ b/README.md @@ -79,8 +79,8 @@ We can run it to generate an SVG: deno run drawing.ts render --outfile drawing.svg ``` -![cubic-disarray](doc/cubic-disarray.svg) (This is generated with -[examples/cubic-disarray.ts](examples/cubic-disarray.ts]) +![readme-example](doc/readme-example.svg) (This is generated with +[examples/cubic-disarray.ts](examples/cubic-disarray.ts)) Or output to stdout. @@ -94,6 +94,19 @@ Or override settings deno run drawing.ts render --outfile drawing.svg --setting.rotationStrength=10 ``` +## Examples + +The [examples directory](examples/) contains runnable example drawings. Each +example has a page in [doc/examples/](doc/examples/) with a short explanation +and the SVGs it generates. + +All example SVGs are generated and committed, and can be regenerated with a +single command: + +```bash +deno task generate:examples +``` + ## Pick and Choose The full example above is by no means how it must be set up. This is not a diff --git a/__tests__/integration/generate_examples_test.ts b/__tests__/integration/generate_examples_test.ts new file mode 100644 index 0000000..1715281 --- /dev/null +++ b/__tests__/integration/generate_examples_test.ts @@ -0,0 +1,39 @@ +import { assert, assertEquals } from "@std/assert"; + +const expectedSvgs = [ + "doc/examples/cubic-disarray/svg/01.svg", + "doc/examples/cubic-disarray/svg/02.svg", + "doc/examples/noodle-love/svg/01.svg", +]; + +Deno.test("generate:examples produces deterministic SVGs", async () => { + const repoRoot = new URL("../../", import.meta.url).pathname; + + const runTask = async () => { + const command = new Deno.Command(Deno.execPath(), { + args: ["task", "generate:examples"], + cwd: repoRoot, + }); + const { code, stderr } = await command.output(); + assertEquals( + code, + 0, + `generate:examples exited nonzero. Error: ${ + new TextDecoder().decode(stderr) + }`, + ); + }; + + const readSvg = (path: string) => Deno.readTextFile(repoRoot + path); + + await runTask(); + const first = await Promise.all(expectedSvgs.map(readSvg)); + + await runTask(); + const second = await Promise.all(expectedSvgs.map(readSvg)); + + for (const svg of second) { + assert(svg.startsWith("seed fancy-red-chicken \ No newline at end of file diff --git a/doc/examples/cubic-disarray/svg/02.svg b/doc/examples/cubic-disarray/svg/02.svg new file mode 100644 index 0000000..c7b73a7 --- /dev/null +++ b/doc/examples/cubic-disarray/svg/02.svg @@ -0,0 +1 @@ +seed fancy-red-chicken \ No newline at end of file diff --git a/doc/examples/noodle-love/README.md b/doc/examples/noodle-love/README.md new file mode 100644 index 0000000..cd9a4ef --- /dev/null +++ b/doc/examples/noodle-love/README.md @@ -0,0 +1,10 @@ +# Noodle Love + +A grid of round and cross tiles, each placed in a cell and rotated by a random +multiple of 90 degrees, with a heart in the center. It showcases grids with +square cells, the seedable `Random` generator, and reusing external SVG assets +with `readSvg` and defs. + +![Noodle Love](./svg/01.svg) + +Source: [examples/noodle_love.ts](../../../examples/noodle_love.ts) diff --git a/doc/examples/noodle-love/svg/01.svg b/doc/examples/noodle-love/svg/01.svg new file mode 100644 index 0000000..9047a32 --- /dev/null +++ b/doc/examples/noodle-love/svg/01.svg @@ -0,0 +1,39 @@ +Noodle noodle-love + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/cubic-disarray.svg b/doc/readme-example.svg similarity index 100% rename from doc/cubic-disarray.svg rename to doc/readme-example.svg diff --git a/examples/noodle_love.ts b/examples/noodle_love.ts index 8e3be43..3376a8d 100644 --- a/examples/noodle_love.ts +++ b/examples/noodle_love.ts @@ -1,63 +1,89 @@ -import { Drawing, Grid, Random, readSvg } from "@berkes/vormen"; - -const drawing = new Drawing() - .withA4Size() - .withMargin(20) - .withBackgroundColor("#fff"); - -const canvas = drawing.build(); - -const COLUMNS = 20; -const ROWS = 25; -const grid = new Grid() - .withSize(drawing.getInnerWidth(), drawing.getInnerHeight()) - .withCols(COLUMNS) - .withRows(ROWS) - .withSquareCells(); - -const rand = new Random(); - -const tileNames = ["tile_round", "tile_cross"]; -const tiles = tileNames.map((tileName) => { - const tile = readSvg(`examples/assets/${tileName}.svg`); - tile.id(tileName); - return tile; +/** + * Noodle Love. + * + * A grid of round and cross tiles, rotated randomly, with a heart in the + * center. + * + * Run it and redirect the output to a file: + * deno run examples/noodle_love.ts > noodle-love.svg + */ +import { + Drawing, + Grid, + Random, + readSvg, + Settings, + Vormen, +} from "@berkes/vormen"; + +const settings = new Settings({ + seed: "noodle-love", }); -const centerTile = readSvg("examples/assets/heart.svg"); -centerTile.id("center-tile"); -canvas.defs().add(centerTile); +function draw(settings: Settings): Drawing { + const drawing = new Drawing() + .withA4Size() + .withMargin(20) + .withBackgroundColor("#fff"); -tiles.forEach((tile) => { - canvas.defs().add(tile); -}); + const canvas = drawing.build(); -grid.cells().forEach((cell) => { - const tileName = tileNames[rand.between(0, tileNames.length - 1)]; - const tile = canvas.use(tileName); - tile.x(cell.x).y(cell.y).width(cell.width).height(cell.height); - tile.rotate(rand.between(0, 3) * 90, cell.centerX, cell.centerY); -}); + const COLUMNS = 20; + const ROWS = 25; + + const grid = new Grid() + .withSize(drawing.getInnerWidth(), drawing.getInnerHeight()) + .withCols(COLUMNS) + .withRows(ROWS) + .withSquareCells(); + + const rand = new Random(settings.getString("seed")); + + const tileNames = ["tile_round", "tile_cross"]; + const tiles = tileNames.map((tileName) => { + const tile = readSvg(`examples/assets/${tileName}.svg`); + tile.id(tileName); + return tile; + }); + + const centerTile = readSvg("examples/assets/heart.svg"); + centerTile.id("center-tile"); + canvas.defs().add(centerTile); + + tiles.forEach((tile) => { + canvas.defs().add(tile); + }); + + grid.cells().forEach((cell) => { + const tileName = tileNames[rand.between(0, tileNames.length - 1)]; + const tile = canvas.use(tileName); + tile.x(cell.x).y(cell.y).width(cell.width).height(cell.height); + tile.rotate(rand.between(0, 3) * 90, cell.centerX, cell.centerY); + }); + + // Our center tile is 3 x 2 cells we add our center over the grid at this position + const centerCell = grid.pickCell( + Math.floor(COLUMNS / 2) - 2, + Math.floor(ROWS / 2) - 1, + )!; + const centerTileX = centerCell.x; + const centerTileY = centerCell.y; + const centerTileWidth = centerCell.width * 3; + const centerTileHeight = centerCell.height * 2; + + canvas.rect(centerTileWidth, centerTileHeight).x(centerTileX).y(centerTileY) + .fill("#ffffff"); -// Our center tile is 3 x 2 cells we add our center over the grid at this position -const centerCell = grid.pickCell( - Math.floor(COLUMNS / 2) - 2, - Math.floor(ROWS / 2) - 1, -)!; -const centerTileX = centerCell.x; -const centerTileY = centerCell.y; -const centerTileWidth = centerCell.width * 3; -const centerTileHeight = centerCell.height * 2; - -canvas.rect(centerTileWidth, centerTileHeight).x(centerTileX).y(centerTileY) - .fill("#ffffff"); -canvas.use("center-tile").x(centerTileX).y(centerTileY).width(centerTileWidth) - .height( + canvas.use("center-tile").x(centerTileX).y(centerTileY).width( + centerTileWidth, + ).height( centerTileHeight, ); -const text = canvas.text(`Noodle ${rand.seed}`); + const text = canvas.text(`Noodle ${rand.seed}`); + text.x(0).y(drawing.getInnerHeight()); -text.x(0).y(drawing.getInnerHeight()); + return drawing; +} -console.log(drawing.svg()); +Vormen(draw, settings); diff --git a/examples/update-example-svgs.sh b/examples/update-example-svgs.sh new file mode 100755 index 0000000..9b66be6 --- /dev/null +++ b/examples/update-example-svgs.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")/.." + +deno run --allow-read --allow-write=doc/examples examples/cubic-disarray.ts render -o doc/examples/cubic-disarray/svg/01.svg +deno run --allow-read --allow-write=doc/examples examples/cubic-disarray.ts render -o doc/examples/cubic-disarray/svg/02.svg --setting.rotationStrength=5 +deno run --allow-read --allow-write=doc/examples examples/noodle_love.ts render -o doc/examples/noodle-love/svg/01.svg From 71d6a8ba98e79716f1080c8074064643df6c6ad3 Mon Sep 17 00:00:00 2001 From: Vibe Nuage Agent Date: Mon, 5 Oct 2026 15:30:26 +0000 Subject: [PATCH 2/2] Exclude example SVGs from deno fmt Co-authored-by: berkes --- deno.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deno.json b/deno.json index 50fed6f..e008e56 100644 --- a/deno.json +++ b/deno.json @@ -42,7 +42,7 @@ } }, "fmt": { - "exclude": ["doc/glossary.md"] + "exclude": ["doc/glossary.md", "doc/examples/**/*.svg"] }, "exclude": ["target/", "dist/", "saves/"] }