lattice

grid

The generative layer, and the only part of the package with no opinion about pixels. A field is read on a lattice of cells, each level is quantized against an alphabet, and what comes out is characters.

import { calibrate, glyphAt, normalize, ramp, sample } from "@mariovagomarzal/lattice";

const field = (x: number, y: number, t: number) => Math.sin(2 * Math.hypot(x, y) - 1.5 * t);
const lattice = { columns: 60, rows: 20, span: 7 };

const levels = sample(normalize(field, calibrate(field)), lattice, 0);
const glyphs = Array.from(levels, (level) => glyphAt(level, ramp));
:+***:· ·:+*xx*+::·:+*x#####xx*******++:·  ·:+*xxx+:··:*x##x
:***+:· ·:+xxx*:··:+*x#####x**++++++***+:·· ·:*xxx*+:·:+x##x
+***+·  ·:*xx*+:··:+x####x**+::···::+***+:···:+xx#x*:::+*###
+**+:·  ·+*x*+:···:*x##x**+::··    ·:+*x*+:··:+*x#x*+::+*x##
+**+:   :+***:·  ·+*xxx*++::::·     ·+*xx*+:··:*x##x+:::*x##
+**+·   :+**+:· ·:+*xx*+:::+++:··   ·:*xxx*+:::*x##x*+::+x##
+**:·  ·:+**+·  ·:+*x*+:··:+***+:·  ·:+x##x*:::+x###*+::+x##
+*+:·  ·:+**:·  ·:***+:···+*##x*+::··:+x##x*+::+x###x+::+x##
+*+:   ·:**+:·  ·:***:·  ·+x##x***+:::+*###x+::+*###x+::+x##
**+:   ·:**+:   ·:**+:   ·+*x#**x#x+::+*###x+::+*###x+::+*##
**+:   ·:**+:   ·:**+:   ·:**+*###x*::+*###x*::+*###x+::+*##
**+:   ·:**+:   ·:**+:   ·+*x#**x#x+::+*###x+::+*###x+::+*##
+*+:   ·:**+:·  ·:***:·  ·+x##x***+:::+*###x+::+*###x+::+x##
+*+:·  ·:+**:·  ·:***+:···+*##x*+::··:+x##x*+::+x###x+::+x##
+**:·  ·:+**+·  ·:+*x*+:··:+***+:·  ·:+x##x*:::+x###*+::+x##
+**+·   :+**+:· ·:+*xx*+:::+++:··   ·:*xxx*+:::*x##x*+::+x##
+**+:   :+***:·  ·+*xxx*++::::·     ·+*xx*+:··:*x##x+:::*x##
+**+:·  ·+*x*+:···:*x##x**+::··    ·:+*x*+:··:+*x#x*+::+*x##
+***+·  ·:*xx*+:··:+x####x**+::···::+***+:···:+xx#x*:::+*###
:***+:· ·:+xxx*:··:+*x#####x**++++++***+:·· ·:*xxx*+:·:+x##x

The alphabet

 0–14%
·14–29%
:29–43%
+43–57%
*57–71%
x71–86%
#86–100%

Reference

exportsignaturewhat it is
Field(x, y, t) => numberA scalar field. The package ships none of its own.
Range{ lo, hi }The low and high ends a field is rescaled against.
Lattice{ columns, rows, span }The cell counts, and the field units the short side covers.
rampreadonly string[]Seven characters, from empty to full ink.
glyphAt(level, alphabet?) => stringThe glyph a level in [0, 1] quantizes to.
calibrate(field, options?) => RangeA field's range, sampled across space and a few instants of time. An estimate, not a bound.
normalize(field, range) => FieldThe field wrapped into one that lands in [0, 1], clamping the rest.
sample(field, lattice, t) => Float64ArrayThe field at every cell center, in row order, as levels[row * columns + col].
fieldPoint(lattice, col, row) => { x, y }The field coordinates of a cell, from the origin at its center. Takes fractional indices.
latticePoint(lattice, x, y) => { col, row }The cell a field point falls on, as fractional indices.