diff --git a/package-lock.json b/package-lock.json
index f426db3..3947cb4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1214,8 +1214,8 @@
"resolved": "sketches/2024-07-23-fusilli",
"link": true
},
- "node_modules/fusilli-animated": {
- "resolved": "sketches/2024-08-12-fusilli-animated",
+ "node_modules/fusilli-animata": {
+ "resolved": "sketches/2024-08-12-fusilli-animata",
"link": true
},
"node_modules/glob": {
@@ -2492,8 +2492,8 @@
"version": "0.0.0",
"license": "MIT"
},
- "sketches/2024-08-12-fusilli-animated": {
- "name": "fusilli-animated",
+ "sketches/2024-08-12-fusilli-animata": {
+ "name": "fusilli-animata",
"version": "0.0.0",
"license": "MIT"
},
diff --git a/sketches/2024-08-12-fusilli-animated/index.html b/sketches/2024-08-12-fusilli-animata/index.html
similarity index 92%
rename from sketches/2024-08-12-fusilli-animated/index.html
rename to sketches/2024-08-12-fusilli-animata/index.html
index 6da5d32..eea02ae 100644
--- a/sketches/2024-08-12-fusilli-animated/index.html
+++ b/sketches/2024-08-12-fusilli-animata/index.html
@@ -6,7 +6,7 @@
-
Fusilli animated - 2024-08-12
+ fusilli-animata - 2024-08-12
diff --git a/sketches/2024-08-12-fusilli-animated/package.json b/sketches/2024-08-12-fusilli-animata/package.json
similarity index 85%
rename from sketches/2024-08-12-fusilli-animated/package.json
rename to sketches/2024-08-12-fusilli-animata/package.json
index 2c7d800..3926327 100644
--- a/sketches/2024-08-12-fusilli-animated/package.json
+++ b/sketches/2024-08-12-fusilli-animata/package.json
@@ -1,5 +1,5 @@
{
- "name": "fusilli-animated",
+ "name": "fusilli-animata",
"type": "module",
"version": "0.0.0",
"scripts": {
diff --git a/sketches/2024-08-12-fusilli-animated/src/config.ts b/sketches/2024-08-12-fusilli-animata/src/config.ts
similarity index 76%
rename from sketches/2024-08-12-fusilli-animated/src/config.ts
rename to sketches/2024-08-12-fusilli-animata/src/config.ts
index 7401c59..1bc7781 100644
--- a/sketches/2024-08-12-fusilli-animated/src/config.ts
+++ b/sketches/2024-08-12-fusilli-animata/src/config.ts
@@ -1,17 +1,18 @@
import type { TileType } from "./types.js";
-export const GRID = {
- SIZE: 24,
+export const CANVAS_SIZE = Math.min(window.innerWidth, window.innerHeight);
+// export const CANVAS_SIZE = 720;
- // COL: 0
- // ROW: 0
-};
+// amount per edge
+export const TILES_AMOUNT = 16;
+// COL: 0
+// ROW: 0
-// const CANVAS_SIZE = Math.min(window.innerWidth, window.innerHeight);
-const CANVAS_SIZE = 640;
+export const MIN_LIFETIME = 180;
+export const MAX_LIFETIME = 300;
+export const FADE_TRANSITION_LGTH = 16;
-// const SIDE_LGTH = Math.floor(Math.min(window.innerWidth, window.innerHeight) / (0.5 * GRID.SIZE));
-const SIDE_LGTH = Math.floor(CANVAS_SIZE / GRID.SIZE);
+const SIDE_LGTH = Math.floor(CANVAS_SIZE / TILES_AMOUNT);
export const TILE = {
SIDE_LGTH: SIDE_LGTH,
diff --git a/sketches/2024-08-12-fusilli-animated/src/core/nodes.ts b/sketches/2024-08-12-fusilli-animata/src/core/nodes.ts
similarity index 100%
rename from sketches/2024-08-12-fusilli-animated/src/core/nodes.ts
rename to sketches/2024-08-12-fusilli-animata/src/core/nodes.ts
diff --git a/sketches/2024-08-12-fusilli-animated/src/core/observer.ts b/sketches/2024-08-12-fusilli-animata/src/core/observer.ts
similarity index 96%
rename from sketches/2024-08-12-fusilli-animated/src/core/observer.ts
rename to sketches/2024-08-12-fusilli-animata/src/core/observer.ts
index 38ed05b..45212fc 100644
--- a/sketches/2024-08-12-fusilli-animated/src/core/observer.ts
+++ b/sketches/2024-08-12-fusilli-animata/src/core/observer.ts
@@ -4,11 +4,9 @@ import type {
NodeHash,
} from "../types";
-import { GRID, EDGES } from "../config";
+import { TILES_AMOUNT, EDGES } from "../config";
import * as util from './utils.js';
-const { SIZE } = GRID;
-
export class NodesObserver {
occupied: Set;
col: number;
@@ -30,14 +28,14 @@ export class NodesObserver {
if (this.edge === 0) {
this.col += 1;
- this.col %= SIZE;
+ this.col %= TILES_AMOUNT;
}
if (this.edge === 0 && this.col === 0) {
this.row += 1;
}
- if (this.row === SIZE) {
+ if (this.row === TILES_AMOUNT) {
this.done = true;
}
}
diff --git a/sketches/2024-08-12-fusilli-animata/src/core/tiles.ts b/sketches/2024-08-12-fusilli-animata/src/core/tiles.ts
new file mode 100644
index 0000000..f5205e8
--- /dev/null
+++ b/sketches/2024-08-12-fusilli-animata/src/core/tiles.ts
@@ -0,0 +1,81 @@
+import type { TileType, TileHash, TileProps } from "../types.js";
+import { TILES, TILE } from "../config.js";
+
+export function createTiles(size: number) {
+ const result = new Map();
+ const weights = (Object.keys(TILES) as TileType[])
+ .reduce((all, type) => {
+ const length = TILES[type].weight;
+ return [
+ ...all,
+ ...Array.from({ length }, () => type)
+ ];
+ }, [] as TileType[]);
+
+ const { SIDE_LGTH, HALF_SIDE_LGTH } = TILE;
+
+ let hash: TileHash;
+
+ for (let row = 0; row < size; row += 1) {
+ for (let col = 0; col < size; col += 1) {
+ hash = `${col}.${row}`;
+ result.set(hash, createTileProps(col, row));
+ }
+ }
+
+ function createTileProps(col: number, row: number): TileProps {
+ const type = pickType();
+ const rotation = pickRotation();
+
+ return {
+ type,
+ lanes: rotateTileHash(type, rotation),
+ col,
+ row,
+ rotation,
+ position: getPositionCoords(col, row),
+ center: getCenterCoords(col, row),
+ };
+ }
+
+ function pickType(): TileType {
+ const index = Math.floor(Math.random() * weights.length);
+ return weights[index];
+ }
+
+ function pickRotation() {
+ return Math.floor(Math.random() * 4);
+ }
+
+ function rotateTileHash(type: TileType, rotation: number) {
+ let i = 0;
+ while (i < rotation) {
+ type = type.slice(-1) + type.slice(0, 3);
+ i += 1;
+ }
+ return type;
+ }
+
+ function getPositionCoords(
+ col: number,
+ row: number
+ ): [number, number] {
+ return [
+ col * SIDE_LGTH,
+ row * SIDE_LGTH,
+ ];
+ }
+
+ function getCenterCoords(
+ col: number,
+ row: number
+ ): [number, number] {
+ return [
+ col * SIDE_LGTH + HALF_SIDE_LGTH,
+ row * SIDE_LGTH + HALF_SIDE_LGTH,
+ ];
+ }
+
+ return result;
+}
+
diff --git a/sketches/2024-08-12-fusilli-animated/src/core/utils.ts b/sketches/2024-08-12-fusilli-animata/src/core/utils.ts
similarity index 91%
rename from sketches/2024-08-12-fusilli-animated/src/core/utils.ts
rename to sketches/2024-08-12-fusilli-animata/src/core/utils.ts
index 948f939..4cc3628 100644
--- a/sketches/2024-08-12-fusilli-animated/src/core/utils.ts
+++ b/sketches/2024-08-12-fusilli-animata/src/core/utils.ts
@@ -10,7 +10,7 @@ import type {
} from "../types.js";
import {
- GRID,
+ TILES_AMOUNT,
EDGES,
LANES
} from "../config.js";
@@ -160,14 +160,6 @@ export function assertNonNullable(
// #####
-const { SIZE } = GRID;
-
-const matrixNext = [
- [0, 1],
- [-1, 0],
- [0, -1],
- [1, 0]
-];
export function getNextTile(
tiles: TilesMap,
@@ -175,13 +167,20 @@ export function getNextTile(
row: number,
nextEdge: Edge,
): TileEntry | undefined {
+ const matrix = [
+ [0, 1],
+ [-1, 0],
+ [0, -1],
+ [1, 0]
+ ];
+
const index = EDGES.indexOf(nextEdge);
// unecessary ??
if (index < 0) return;
- col += matrixNext[index][0];
- row += matrixNext[index][1];
+ col += matrix[index][0];
+ row += matrix[index][1];
if (isOutOfBounds(col, row)) return;
@@ -192,12 +191,6 @@ export function getNextTile(
}
-const matrixPrev = [
- [0, -1],
- [1, 0],
- [0, 1],
- [-1, 0]
-];
export function getPrevTile(
tiles: TilesMap,
@@ -205,12 +198,19 @@ export function getPrevTile(
row: number,
edge: Edge
): TileEntry | undefined {
+ const matrix = [
+ [0, -1],
+ [1, 0],
+ [0, 1],
+ [-1, 0]
+ ];
+
const index = EDGES.indexOf(edge);
if (index < 0) return;
- col += matrixPrev[index][0];
- row += matrixPrev[index][1];
+ col += matrix[index][0];
+ row += matrix[index][1];
if (isOutOfBounds(col, row)) return;
@@ -221,7 +221,7 @@ export function getPrevTile(
}
function isOutOfBounds(col: number, row: number) {
- return col < 0 || row < 0 || col >= SIZE || row >= SIZE;
+ return col < 0 || row < 0 || col >= TILES_AMOUNT || row >= TILES_AMOUNT;
}
// #####
diff --git a/sketches/2024-08-12-fusilli-animated/src/renderer/lane.ts b/sketches/2024-08-12-fusilli-animata/src/renderer/lane.ts
similarity index 99%
rename from sketches/2024-08-12-fusilli-animated/src/renderer/lane.ts
rename to sketches/2024-08-12-fusilli-animata/src/renderer/lane.ts
index 63944ad..3d9dfc0 100644
--- a/sketches/2024-08-12-fusilli-animated/src/renderer/lane.ts
+++ b/sketches/2024-08-12-fusilli-animata/src/renderer/lane.ts
@@ -186,7 +186,7 @@ function laneAnglesToAnglesArray(array: [number, number, number, number]) {
// access precalculated coords
-export const laneRenderer: Record<
+export const LaneRenderer: Record<
Lane,
(
p: P5,
diff --git a/sketches/2024-08-12-fusilli-animata/src/renderer/nodes.ts b/sketches/2024-08-12-fusilli-animata/src/renderer/nodes.ts
new file mode 100644
index 0000000..71241d6
--- /dev/null
+++ b/sketches/2024-08-12-fusilli-animata/src/renderer/nodes.ts
@@ -0,0 +1,154 @@
+import type { Color } from "p5";
+
+import type {
+ Edge,
+ Lane,
+ TilesMap,
+ TileHash,
+ TileProps,
+ NodeHash,
+ P5,
+} from "../types.js";
+
+import { FADE_TRANSITION_LGTH, MIN_LIFETIME, MAX_LIFETIME } from "../config.js";
+import { getIndexOfEdge, parseNodeHash } from "../core/utils.js";
+
+import { LANE, LaneRenderer } from "./lane.js";
+
+const { WEIGHT_THICK, WEIGHT_THIN } = LANE;
+
+type RendererProps = {
+ lane: Lane,
+ x: number,
+ y: number,
+ rotation: number;
+};
+
+export class NodesRenderer {
+ static hue_offset: number;
+ static hue_delta: number;
+ static sat_min: number;
+ static sat_delta: number;
+ static lgt_min: number;
+ static lgt_delta: number;
+
+ static shuffleColorValues() {
+ const pick = (array: T[]) => array[Math.floor(Math.random() * array.length)];
+
+ this.hue_offset = pick([0, 60, 120, 180, 240, 300]);
+ this.hue_delta = pick([60, 120, 180]);
+
+ this.sat_min = 25;
+ this.sat_delta = 50;
+
+ this.lgt_min = 20;
+ this.lgt_delta = 40;
+ }
+
+ static createColors(p: P5, bg: Color, nodes: NodeHash[]): Color[] {
+ /** @todo */
+ const hue = Math.floor((this.hue_offset + this.hue_delta * Math.random()) % 360);
+ const sat = this.sat_min + this.sat_delta * Math.random();
+ const lgt = this.lgt_min + Math.min(this.lgt_delta, 2 * nodes.length);
+
+ const a = p.color(`hsl(${hue}, 100%, 100%)`);
+ const b = p.color(`hsl(${hue}, ${sat}%, ${lgt}%)`);
+
+ const colors: Color[] = [];
+ let t: number;
+
+ for (let i = 0; i < FADE_TRANSITION_LGTH; i += 1) {
+ t = i / (FADE_TRANSITION_LGTH - 1);
+ colors.push(p.lerpColor(a, b, t));
+ }
+
+ for (let i = 0; i < FADE_TRANSITION_LGTH; i += 1) {
+ t = i / (FADE_TRANSITION_LGTH - 1);
+ colors.push(p.lerpColor(b, bg, t));
+ }
+
+ return colors;
+ }
+
+ parsed: RendererProps[];
+ colors: Color[];
+
+ birthtime: number;
+ lifetime: number;
+ duration: number;
+ done: boolean;
+
+ constructor(p: P5, bg: Color, tiles: TilesMap, nodes: NodeHash[]) {
+ this.parsed = [];
+
+ this.birthtime = p.frameCount;
+ this.lifetime = 0;
+ this.duration = Math.max(MIN_LIFETIME, Math.min(MAX_LIFETIME, nodes.length + FADE_TRANSITION_LGTH));
+ this.done = false;
+
+ this.colors = NodesRenderer.createColors(p, bg, nodes);
+
+ let hash: TileHash;
+ let props: TileProps | undefined;
+ let edge: Edge;
+ let lane: Lane;
+ let x: number;
+ let y: number;
+ let rotation: number;
+
+ nodes.forEach(node => {
+ [hash, , , edge, lane] = parseNodeHash(node);
+ props = tiles.get(hash);
+
+ if (props) {
+ [x, y] = props.center;
+ rotation = getIndexOfEdge(edge);
+ this.parsed.push({
+ lane,
+ x,
+ y,
+ rotation
+ });
+ };
+ });
+ }
+
+ draw(p: P5, bg: Color) {
+ this.lifetime = p.frameCount - this.birthtime;
+
+ if (this.lifetime === 0 || this.done === true) return;
+
+ if (this.lifetime - this.duration - this.parsed.length - 2 * FADE_TRANSITION_LGTH > 0) {
+ console.log('Done');
+ this.done = true;
+ return;
+ }
+
+ const ii = Math.min(this.lifetime, this.parsed.length);
+
+ let index: number;
+ let color: Color;
+
+ for (let i = 0; i < ii; i += 1) {
+ if (this.lifetime - this.duration - i - 2 * FADE_TRANSITION_LGTH > 0) continue;
+
+ const { lane, x, y, rotation } = this.parsed[i];
+
+ index = this.lifetime < this.duration
+ ? Math.max(0, Math.min(FADE_TRANSITION_LGTH - 1, this.lifetime - i))
+ : Math.max(FADE_TRANSITION_LGTH, Math.min(this.colors.length - 1, this.lifetime - this.duration - i));
+
+ color = this.colors[index];
+
+ p.stroke(bg);
+ p.strokeWeight(WEIGHT_THICK);
+ p.strokeCap(p.SQUARE);
+ LaneRenderer[lane](p, x, y, rotation);
+
+ p.strokeWeight(WEIGHT_THIN);
+ p.strokeCap(p.ROUND);
+ p.stroke(color);
+ LaneRenderer[lane](p, x, y, rotation);
+ }
+ }
+}
\ No newline at end of file
diff --git a/sketches/2024-08-12-fusilli-animata/src/sketch.ts b/sketches/2024-08-12-fusilli-animata/src/sketch.ts
new file mode 100644
index 0000000..665140d
--- /dev/null
+++ b/sketches/2024-08-12-fusilli-animata/src/sketch.ts
@@ -0,0 +1,62 @@
+import type { Color } from "p5";
+import type { P5, NodeHash } from "./types.js";
+
+import { CANVAS_SIZE, TILES_AMOUNT } from "./config.js";
+import { createTiles } from "./core/tiles.js";
+import { createNodes } from "./core/nodes.js";
+import { NodesObserver } from "./core/observer.js";
+import { NodesRenderer } from "./renderer/nodes.js";
+
+export const sketch = (p: P5) => {
+ let tiles = createTiles(TILES_AMOUNT);
+ let observer = new NodesObserver();
+
+ let graphs: NodeHash[][] = [];
+ let renderers: NodesRenderer[] = [];
+
+ let background: Color;
+
+ p.setup = () => {
+ p.createCanvas(CANVAS_SIZE, CANVAS_SIZE);
+ p.ellipseMode(p.RADIUS);
+ p.background(background = p.color('hsl(0, 0%, 8%)'));
+
+ NodesRenderer.shuffleColorValues();
+ };
+
+ p.draw = () => {
+ p.background(background);
+ const start = !observer.done
+ ? observer.getNextUnoccupied(tiles)
+ : null;
+
+ if (start) {
+ const nodes = createNodes(tiles, start, observer);
+ graphs.push(nodes);
+
+ const renderer = new NodesRenderer(p, background, tiles, nodes);
+ renderers.push(renderer);
+ }
+
+ p.noFill();
+ renderers.forEach(r => r.draw(p, background));
+
+ if (renderers.every(renderer => renderer.done)) {
+ tiles = createTiles(TILES_AMOUNT);
+ observer = new NodesObserver();
+
+ graphs = [];
+ renderers = [];
+
+ NodesRenderer.shuffleColorValues();
+ }
+ };
+
+ p.mouseClicked = (e) => {
+ if (p.isLooping()) {
+ p.noLoop();
+ } else {
+ p.loop();
+ }
+ };
+};
\ No newline at end of file
diff --git a/sketches/2024-08-12-fusilli-animated/src/types.ts b/sketches/2024-08-12-fusilli-animata/src/types.ts
similarity index 100%
rename from sketches/2024-08-12-fusilli-animated/src/types.ts
rename to sketches/2024-08-12-fusilli-animata/src/types.ts
diff --git a/sketches/2024-08-12-fusilli-animated/tsconfig.json b/sketches/2024-08-12-fusilli-animata/tsconfig.json
similarity index 65%
rename from sketches/2024-08-12-fusilli-animated/tsconfig.json
rename to sketches/2024-08-12-fusilli-animata/tsconfig.json
index 05494e9..8ee6848 100644
--- a/sketches/2024-08-12-fusilli-animated/tsconfig.json
+++ b/sketches/2024-08-12-fusilli-animata/tsconfig.json
@@ -1,6 +1,7 @@
{
"extends": "../../tsconfig.sketch.json",
"include": [
- "./src"
+ "./src",
+ "./src/sketch.js"
],
}
\ No newline at end of file
diff --git a/sketches/2024-08-12-fusilli-animated/vite.config.ts b/sketches/2024-08-12-fusilli-animata/vite.config.ts
similarity index 100%
rename from sketches/2024-08-12-fusilli-animated/vite.config.ts
rename to sketches/2024-08-12-fusilli-animata/vite.config.ts
diff --git a/sketches/2024-08-12-fusilli-animated/src/colors.ts b/sketches/2024-08-12-fusilli-animated/src/colors.ts
deleted file mode 100644
index 7970981..0000000
--- a/sketches/2024-08-12-fusilli-animated/src/colors.ts
+++ /dev/null
@@ -1,236 +0,0 @@
-// import type { Coords, NodeHash } from "./types.js";
-// import { GRID } from "./config.js";
-// // import { Grid } from "./grid/index.js";
-// import * as util from './grid/utils.js';
-// // import { log } from "./logger.js";
-
-// const { SIZE } = GRID;
-
-// type ConfigType = 'rgb' | 'hsl';
-
-// type ConfigRgb = {
-// min: number;
-// max: number;
-// delta: number;
-// };
-
-// type ConfigHsl = {
-// hueOff: number;
-// hueDelta: number;
-// hueJitter: number;
-// lgtMin: number;
-// lgtDelta: number;
-// };
-
-// // type ColorConfig = T extends ConfigType
-// // ? T extends 'rgb'
-// // ? ConfigRgb
-// // : T extends 'hsl'
-// // ? ConfigHsl
-// // : never
-// // : never;
-
-// type ColorGenerator = T extends ConfigType
-// ? T extends 'rgb'
-// ? ReturnType
-// : T extends 'hsl'
-// ? ReturnType
-// : never
-// : never;
-
-
-// // export class Colors {
-// // type: ConfigType;
-// // config: ConfigRgb | ConfigHsl;
-// // data: Set = new Set();
-// // generator: ColorGenerator;
-
-// // constructor(graphs: Grid['graphs']) {
-// // this.type = throwCoin() ? 'rgb' : 'hsl';
-
-// // this.config = this.type === 'rgb'
-// // ? createConfigValues(this.type)
-// // : createConfigValues(this.type);
-
-// // this.generator = this.type === 'rgb'
-// // ? createRGBColorGenerator(this.config as ConfigRgb)
-// // : createHSLColorGenerator(this.config as ConfigHsl, graphs);
-
-// // // log(JSON.stringify({ type: this.type, vals: this.config }, null, 2));
-// // }
-
-// // process(nodes: NodeHash[]) {
-// // const [x, y] = getAvgCoords(nodes);
-// // const [a, b, c] = this.generator(x, y, nodes.length);
-
-// // const color = this.type === 'rgb'
-// // ? `rgb(${a}, ${b}, ${c})`
-// // : `hsl(${a}, ${b}%, ${c}%)`;
-
-// // this.data.add(color);
-// // return color;
-// // }
-// // }
-
-// function createConfigValues(type: ConfigType) {
-// if (type === 'rgb') {
-// const min = 34;
-// const max = 255;
-
-// return {
-// min,
-// max,
-// delta: max - min
-// } as ConfigRgb;
-// }
-
-// return {
-// hueOff: Math.floor(270 * Math.random()),
-// hueDelta: [90, 180][Math.floor(2 * Math.random())],
-// hueJitter: 30,
-// lgtMin: 20,
-// lgtDelta: 60
-// } as ConfigHsl;
-// }
-
-// // function getAvgCoords(nodes: ProcessedNodeHash[]) {
-// // const minMaxCoords = nodes.reduce((all, node) => {
-// // const [col, row] = util.getHash(node).slice(0, -2).split('.').map(val => parseInt(val)) as Coords;
-
-// // all[0] = Math.min(all[0], col);
-// // all[1] = Math.max(all[1], col);
-// // all[2] = Math.min(all[2], row);
-// // all[3] = Math.max(all[3], row);
-
-// // return all;
-// // }, [Infinity, -Infinity, Infinity, -Infinity]);
-
-// // return [
-// // minMaxCoords[0] + 0.5 * (minMaxCoords[1] - minMaxCoords[0]),
-// // minMaxCoords[2] + 0.5 * (minMaxCoords[3] - minMaxCoords[2]),
-// // ];
-// // }
-
-// function throwCoin(n = 0.5) {
-// return Math.random() < n;
-// }
-
-// // ####
-
-// function createRGBColorGenerator(config: ConfigRgb) {
-// const a: ShufflerParams = [
-// throwCoin(),
-// throwCoin(),
-// throwCoin()
-// ];
-
-// const b: ShufflerParams = throwCoin()
-// ? [a[0], !a[1], !a[2]]
-// : [!a[0], throwCoin(), throwCoin()];
-
-// // mapped to [r, g, b]
-// const colors = shuffleArray([
-// createShuffler(...a),
-// createShuffler(...b),
-// throwCoin() ? 0.25 : 0.75,
-// ]);
-
-// const generate = (x: number, y: number) => {
-// return colors.map(item => typeof item === 'function'
-// ? item(x, y) : item
-// );
-// };
-
-// return (
-// x: number,
-// y: number,
-// _: number
-// ) => {
-// x /= SIZE;
-// y /= SIZE;
-// const vals = generate(x, y);
-// return vals.map(n => clampRGBValue(config.min + n * config.delta));
-// };
-// }
-
-// function createShuffler(axis: boolean, map: boolean, invert: boolean) {
-// return (x: number, y: number) => {
-// const v = (axis ? x : y);
-// return shuffleValue(v, map, invert);
-// };
-// }
-
-// type ShufflerParams = Parameters;
-
-// function shuffleValue(n: number, map: boolean, inv: boolean) {
-// if (map) {
-// n = Math.abs(n * 2 - 1);
-// }
-
-// n += 0.2 * (Math.random() * (throwCoin() ? 1 : -1));
-
-// return inv ? 1 - n : n;
-// }
-
-// function shuffleArray(array: any[]) {
-// let currentIndex = array.length;
-
-// while (currentIndex != 0) {
-// let randomIndex = Math.floor(Math.random() * currentIndex);
-// currentIndex--;
-
-// [
-// array[currentIndex],
-// array[randomIndex]
-// ] = [
-// array[randomIndex],
-// array[currentIndex]
-// ];
-// }
-
-// return array;
-// }
-
-// function clampRGBValue(n: number) {
-// return Math.max(0, Math.min(255, Math.round(n)));
-// }
-
-// // #####
-
-// function createHSLColorGenerator(config: ConfigHsl, data: NodeHash[][]) {
-// // const dataLength = data.length - 1;
-// const maxNodesLength = data.reduce(
-// (all, nodes) => nodes.length > all ? nodes.length : all,
-// -Infinity
-// );
-
-// // const accLength = data.reduce((acc, nodes) => acc += nodes.length - 1, 0);
-// // const avgNodesLength = (Math.round(accLength / data.length));
-
-// return (
-// x: number,
-// y: number,
-// length: number,
-// ) => {
-// x /= SIZE;
-// y /= SIZE;
-
-// const a = Math.atan2(
-// y - 0.5,
-// x - 0.5
-// ) / Math.PI;
-
-// const b = length / maxNodesLength;
-
-// const jitter = Math.random() * (throwCoin() ? 1 : -1);
-// const hue = config.hueOff + a * config.hueDelta + jitter * config.hueJitter;
-
-// const lgt = config.lgtMin + b * config.lgtDelta;
-
-// return [
-// (hue + 360) % 360,
-// 100,
-// lgt,
-// ].map(val => Math.floor(val));
-// };
-// }
\ No newline at end of file
diff --git a/sketches/2024-08-12-fusilli-animated/src/core/tiles.ts b/sketches/2024-08-12-fusilli-animated/src/core/tiles.ts
deleted file mode 100644
index b8e7a89..0000000
--- a/sketches/2024-08-12-fusilli-animated/src/core/tiles.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import type { TileType, TileHash, TileProps } from "../types.js";
-import { TILES, TILE } from "../config.js";
-
-const weights = (Object.keys(TILES) as TileType[])
- .reduce((all, type) => {
- const length = TILES[type].weight;
- return [
- ...all,
- ...Array.from({ length }, () => type)
- ];
- }, [] as TileType[]);
-
-const tiles = new Map();
-let hash: TileHash;
-
-export function createTiles(size: number) {
- tiles.clear();
-
- for (let row = 0; row < size; row += 1) {
- for (let col = 0; col < size; col += 1) {
- hash = `${col}.${row}`;
- tiles.set(hash, createTileProps(col, row));
- }
- }
-
- return tiles;
-}
-
-export function createTileProps(col: number, row: number): TileProps {
- const type = pickType();
- const rotation = pickRotation();
-
- return {
- type,
- lanes: rotateTileHash(type, rotation),
- col,
- row,
- rotation,
- position: getPositionCoords(col, row),
- center: getCenterCoords(col, row),
- };
-}
-
-function pickType(): TileType {
- const index = Math.floor(Math.random() * weights.length);
- return weights[index];
-}
-
-function pickRotation() {
- return Math.floor(Math.random() * 4);
-}
-
-export function rotateTileHash(type: TileType, rotation: number) {
- let i = 0;
- while (i < rotation) {
- type = type.slice(-1) + type.slice(0, 3);
- i += 1;
- }
- return type;
-}
-
-const { SIDE_LGTH, HALF_SIDE_LGTH } = TILE;
-
-export function getPositionCoords(
- col: number,
- row: number
-): [number, number] {
- return [
- col * SIDE_LGTH,
- row * SIDE_LGTH,
- ];
-}
-
-export function getCenterCoords(
- col: number,
- row: number
-): [number, number] {
- return [
- col * SIDE_LGTH + HALF_SIDE_LGTH,
- row * SIDE_LGTH + HALF_SIDE_LGTH,
- ];
-}
\ No newline at end of file
diff --git a/sketches/2024-08-12-fusilli-animated/src/renderer/nodes.ts b/sketches/2024-08-12-fusilli-animated/src/renderer/nodes.ts
deleted file mode 100644
index 77a64a5..0000000
--- a/sketches/2024-08-12-fusilli-animated/src/renderer/nodes.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import type { Color } from "p5";
-
-import type {
- Edge,
- Lane,
- TilesMap,
- TileHash,
- TileProps,
- NodeHash,
- P5,
-} from "../types.js";
-
-import { getIndexOfEdge, parseNodeHash } from "../core/utils.js";
-
-import { LANE, laneRenderer } from "./lane.js";
-
-const { WEIGHT_THICK, WEIGHT_THIN } = LANE;
-
-
-type RendererProps = {
- lane: Lane,
- x: number,
- y: number,
- rotation: number;
-};
-
-
-export class NodesRenderer {
- parsed: RendererProps[];
- step: number;
- done: boolean;
-
- constructor(tiles: TilesMap, nodes: NodeHash[]) {
- this.parsed = [];
- this.step = 1;
- this.done = false;
-
- let hash: TileHash;
- let props: TileProps | undefined;
- let edge: Edge;
- let lane: Lane;
- let x: number;
- let y: number;
- let rotation: number;
-
- nodes.forEach(node => {
- [hash, , , edge, lane] = parseNodeHash(node);
- props = tiles.get(hash);
-
- if (props) {
- [x, y] = props.center;
- rotation = getIndexOfEdge(edge);
- this.parsed.push({
- lane,
- x,
- y,
- rotation
- });
- };
- });
- }
-
- draw(p: P5, bg: Color) {
- const ii = Math.min(this.step, this.parsed.length);
-
- for (let i = 0; i < ii; i += 1) {
- // const t = Math.max(0.5 * i / this.step);
- const t = i / this.step;
- const { lane, x, y, rotation } = this.parsed[i];
-
- p.stroke(bg);
- p.strokeWeight(WEIGHT_THICK);
- p.strokeCap(p.SQUARE);
- laneRenderer[lane](p, x, y, rotation);
-
- p.strokeWeight(WEIGHT_THIN);
- p.strokeCap(p.ROUND);
- p.stroke(104 + 151 * t);
- // ctx.stroke(255);
- laneRenderer[lane](p, x, y, rotation);
- }
-
- this.step += 1;
- }
-}
\ No newline at end of file
diff --git a/sketches/2024-08-12-fusilli-animated/src/sketch.ts b/sketches/2024-08-12-fusilli-animated/src/sketch.ts
deleted file mode 100644
index dca677e..0000000
--- a/sketches/2024-08-12-fusilli-animated/src/sketch.ts
+++ /dev/null
@@ -1,143 +0,0 @@
-import type { Color } from "p5";
-import type { P5, NodeHash } from "./types.js";
-
-import { GRID, TILE } from "./config.js";
-import { createTiles } from "./core/tiles.js";
-import { createNodes } from "./core/nodes.js";
-import { NodesObserver } from "./core/observer.js";
-import { NodesRenderer } from "./renderer/nodes.js";
-
-const { SIZE } = GRID;
-const { SIDE_LGTH } = TILE;
-
-const maxFrames = 640;
-
-let tiles = createTiles(SIZE);
-let observer = new NodesObserver();
-
-let graphs: NodeHash[][] = [];
-let renderers: NodesRenderer[] = [];
-
-let background: Color;
-
-export const sketch = (p: P5) => {
- p.setup = () => {
- p.createCanvas(SIZE * SIDE_LGTH, SIZE * SIDE_LGTH);
- p.ellipseMode(p.RADIUS);
- p.background(background = p.color('hsl(0, 0%, 10%)'));
- // p.frameRate(10);
-
- // p.saveGif(Date.now().toString(), frames, { units: 'frames' });
- };
-
- p.draw = () => {
- p.background(background);
- // const hsl = `hsl(${p.frameCount % 360}, 20%, 10%)`;
- // p.background(hsl);
- // drawCheckboard(p);
- const start = observer.done ? undefined : observer.getNextUnoccupied(tiles);
-
- if (start) {
- const nodes = createNodes(tiles, start, observer);
- graphs.push(nodes);
-
- const renderer = new NodesRenderer(tiles, nodes);
- renderers.push(renderer);
- }
-
- if (observer.done && p.frameCount > maxFrames) {
- p.noLoop();
- console.log('Done', p.frameCount);
- }
-
- p.noFill();
- renderers.forEach(r => r.draw(p, background));
- };
-
- p.mouseClicked = (e) => {
- if (p.isLooping()) {
- p.noLoop();
- } else {
- p.loop();
- }
-
- // if (e instanceof UIEvent && e.target instanceof HTMLCanvasElement) {
- // setTimeout(() => {
- // grid = new Grid(SIZE);
- // colors = createColors(p, grid.colors);
- // p.redraw(1);
-
- // }, 300);
- // }
- };
-};
-
-// function createColors(p: P5, input: Set) {
-// return {
-// background: p.color(17),
-// ...[...input].reduce((all, color) => ({
-// ...all,
-// [color]: p.color(color)
-// }), {})
-// } as Record;
-// }
-
-// function drawCheckboard(p: P5) {
-// let x: number;
-// let y: number;
-
-// p.background('#333');
-// p.noStroke();
-// p.fill('#0003');
-
-// for (let j = 0; j < SIZE; j += 1) {
-// for (let i = 0; i < SIZE; i += 1) {
-// if (
-// (i % 2 === 0 && j % 2 === 0) ||
-// (i % 2 === 1 && j % 2 === 1)
-// ) {
-// x = i * SIDE_LGTH;
-// y = j * SIDE_LGTH;
-// p.rect(x, y, SIDE_LGTH, SIDE_LGTH);
-// }
-// }
-// }
-// }
-
-// function drawNodes(p: P5, data: Grid) {
-// const bg = colors.background;
-
-// let x: number;
-// let y: number;
-
-// p.noFill();
-// p.strokeWeight(Math.max(1, 0.1 * SIDE_LGTH));
-
-// const tiles = [...data.parsed.values()];
-// tiles.forEach((tile, j) => {
-// [x, y] = tile.coords;
-
-// // order matters
-// (['N', 'E', 'S', 'W'] as Edge[]).forEach((edge, rotation) => {
-// const { lane, color } = tile.edges[edge];
-
-// if (tile.hasIntersections) {
-// p.stroke(bg);
-// p.strokeWeight(WEIGHT_THICK);
-// p.strokeCap(p.SQUARE);
-// renderer[lane](p, x, y, rotation);
-
-// // p.erase();
-// // p.strokeWeight(WEIGHT_THICK);
-// // p.strokeCap(p.SQUARE);
-// // renderer[lane](p, x, y, rotation);
-// // p.noErase();
-// }
-
-// p.stroke(color);
-// p.strokeWeight(WEIGHT_THIN);
-// p.strokeCap(p.ROUND);
-// renderer[lane](p, x, y, rotation);
-// });
-// });
-// }
\ No newline at end of file