Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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
Expand Down
39 changes: 39 additions & 0 deletions __tests__/integration/generate_examples_test.ts
Original file line number Diff line number Diff line change
@@ -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("<svg"), "generated file is not an SVG");
}
assertEquals(first, second, "generated SVGs are not deterministic");
});
3 changes: 2 additions & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"workspace": ["./examples", "./packages/create-vormen"],
"tasks": {
"test": "deno test --permission-set",
"generate:examples": "./examples/update-example-svgs.sh",
"check:all": "deno ci && deno check && deno lint && deno fmt --check && deno test --permission-set",
"version:minor": "deno bump-version minor && git add . && git commit -m'bump version' && git tag \"v$(< deno.json | jq .version -r)\"",
"version:patch": "deno bump-version patch && git add . && git commit -m'bump version' && git tag \"v$(< deno.json | jq .version -r)\""
Expand Down Expand Up @@ -41,7 +42,7 @@
}
},
"fmt": {
"exclude": ["doc/glossary.md"]
"exclude": ["doc/glossary.md", "doc/examples/**/*.svg"]
},
"exclude": ["target/", "dist/", "saves/"]
}
13 changes: 13 additions & 0 deletions doc/examples/cubic-disarray/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Cubic Disarray

Cubic Disarray, after Georg Nees: a grid of white squares whose rotation grows
stronger towards the bottom rows, on an A4 page with a gray background.

The rotation strength is a setting: the higher it is, the more the squares
scatter. Both SVGs below use the same seed and only differ in the
`rotationStrength` setting (`2` and `5`).

![Cubic Disarray with rotationStrength 2](./svg/01.svg)
![Cubic Disarray with rotationStrength 5](./svg/02.svg)

Source: [examples/cubic-disarray.ts](../../../examples/cubic-disarray.ts)
1 change: 1 addition & 0 deletions doc/examples/cubic-disarray/svg/01.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions doc/examples/cubic-disarray/svg/02.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions doc/examples/noodle-love/README.md
Original file line number Diff line number Diff line change
@@ -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)
39 changes: 39 additions & 0 deletions doc/examples/noodle-love/svg/01.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
132 changes: 79 additions & 53 deletions examples/noodle_love.ts
Original file line number Diff line number Diff line change
@@ -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);
8 changes: 8 additions & 0 deletions examples/update-example-svgs.sh
Original file line number Diff line number Diff line change
@@ -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
Loading