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