Some checks are pending
Deploy to Cloudflare Workers (OpenNext) / deploy (push) Waiting to run
62 lines
2.0 KiB
TypeScript
62 lines
2.0 KiB
TypeScript
import type { Vec2 } from "@/lib/voronoi-mesh";
|
|
|
|
function mulberry32(seed: number) {
|
|
let s = seed >>> 0;
|
|
return () => {
|
|
s = (s + 0x6d2b79f5) >>> 0;
|
|
let t = Math.imul(s ^ (s >>> 15), 1 | s);
|
|
t ^= t + Math.imul(t ^ (t >>> 7), 61 | t);
|
|
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
};
|
|
}
|
|
|
|
/** Gentle wave along the footer midline — peaks cross 50% so facet tops stay visible. */
|
|
export function buildFooterMidlineCurve(
|
|
width: number,
|
|
segments: number,
|
|
seed: number,
|
|
midY: number,
|
|
amplitude: number
|
|
): Vec2[] {
|
|
const rand = mulberry32(seed);
|
|
const pts: Vec2[] = [[0, midY + (rand() - 0.5) * amplitude * 0.35]];
|
|
for (let i = 1; i <= segments; i++) {
|
|
const x = (width * i) / segments;
|
|
const t = i / segments;
|
|
const wave =
|
|
Math.sin(t * Math.PI * 3.8 + seed * 0.002) * amplitude * 0.5 +
|
|
Math.sin(t * Math.PI * 7.4 + 0.8) * amplitude * 0.22;
|
|
const jitter = (rand() - 0.5) * amplitude * 0.4;
|
|
const y = midY + wave + jitter;
|
|
pts.push([x, Math.max(midY - amplitude * 0.85, Math.min(midY + amplitude * 0.9, y))]);
|
|
}
|
|
return pts;
|
|
}
|
|
|
|
/** Closed SVG path: curved top, square bottom (clip region for facets). */
|
|
export function midlineCurveToClipPath(edge: Vec2[], width: number, height: number): string {
|
|
if (edge.length < 2) return "";
|
|
const [x0, y0] = edge[0];
|
|
let d = `M ${x0},${y0}`;
|
|
for (let i = 1; i < edge.length; i++) {
|
|
const [x, y] = edge[i];
|
|
const [px, py] = edge[i - 1];
|
|
const cx = (px + x) / 2;
|
|
d += ` Q ${cx},${py} ${x},${y}`;
|
|
}
|
|
d += ` L ${width},${height} L 0,${height} Z`;
|
|
return d;
|
|
}
|
|
|
|
/** CSS clip-path (percent) — matches SVG curve for the HTML layer. */
|
|
export function midlineCurveToClipPathPercent(
|
|
edge: Vec2[],
|
|
width: number,
|
|
height: number
|
|
): string {
|
|
const pct = (x: number, y: number) =>
|
|
`${((x / width) * 100).toFixed(2)}% ${((y / height) * 100).toFixed(2)}%`;
|
|
const top = edge.map(([x, y]) => pct(x, y)).join(", ");
|
|
return `polygon(${top}, 100% 100%, 0% 100%)`;
|
|
}
|