/** GRV greens for wavy interlocking footer tessellation. */ export const WAVY_TESS_PALETTE = [ "#0d3d26", "#1a5c38", "#256b45", "#2d7a52", "#3d9a66", "#5cb87a", ] as const; export type WavyTessCell = { d: string; fill: string }; function hashCell(col: number, row: number, cols: number) { return (col * 17 + row * 31 + ((col + row) % cols) * 11) % WAVY_TESS_PALETTE.length; } type MeshOptions = { /** Tilts wave flow diagonally (higher = more angled, less vertical). */ diagonalSkew?: number; amplitude?: number; }; /** * Diagonal wavy columns + horizontal ripples — interlocking mosaic (not flat vertical stripes). */ export function buildWavyTessellationMesh( cols: number, rows: number, width = 100, height = 100, options: MeshOptions = {} ): WavyTessCell[] { const skew = options.diagonalSkew ?? 0.72; const amp = (width / cols) * (options.amplitude ?? 0.62); const ampRow = amp * 0.48; const loops = 3.6; const boundaryX = (col: number, yNorm: number) => { const base = (col / cols) * width; const phase = col * 1.13 + 0.42; const u = yNorm + (col / cols) * skew; const v = yNorm - (col / cols) * skew * 0.45; const wave = Math.sin(u * Math.PI * 2 * loops + phase) + 0.52 * Math.sin(v * Math.PI * 2 * (loops + 0.85) - col * 0.4) + 0.28 * Math.cos((u + v) * Math.PI * loops * 0.55 + phase * 0.6); return base + amp * wave * 0.42; }; const boundaryY = (row: number, xNorm: number) => { const base = (row / rows) * height; const phase = row * 0.91 + 0.18; const u = xNorm + (row / rows) * skew; const wave = Math.sin(u * Math.PI * 2 * (loops * 0.9) + phase) + 0.4 * Math.cos(u * Math.PI * 2 * (loops * 1.15) - row * 0.55); return base + ampRow * wave * 0.38; }; const cells: WavyTessCell[] = []; const corner = (col: number, row: number) => { const yn = row / rows; const x = boundaryX(col, yn); const y = boundaryY(row, x / width); return { x, y }; }; for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { const bl = corner(c, r); const br = corner(c + 1, r); const tl = corner(c, r + 1); const tr = corner(c + 1, r + 1); const ymL = (bl.y + tl.y) / 2; const ymR = (br.y + tr.y) / 2; const d = [ `M ${bl.x.toFixed(2)} ${bl.y.toFixed(2)}`, `Q ${bl.x.toFixed(2)} ${ymL.toFixed(2)} ${tl.x.toFixed(2)} ${tl.y.toFixed(2)}`, `L ${tr.x.toFixed(2)} ${tr.y.toFixed(2)}`, `Q ${br.x.toFixed(2)} ${ymR.toFixed(2)} ${br.x.toFixed(2)} ${br.y.toFixed(2)}`, "Z", ].join(" "); const layer = (r + Math.floor(c / 2)) % 3; const fill = WAVY_TESS_PALETTE[hashCell(c + layer, r, cols)]; cells.push({ d, fill }); } } return cells; }