import type { CSSProperties } from "react"; import { buildVoronoiMesh, type VoronoiCell } from "@/lib/voronoi-mesh"; import { cn } from "@/lib/utils"; /** Wider mesh plane — pairs with slice scaling so pebbles are not horizontally stretched. */ const MESH_W = 140; const MESH_H = 100; /** Narrow green range — low contrast between “stones” on section backgrounds */ const ROCK_TONES = ["#1a5c38", "#1d5f3c", "#216342", "#246845", "#286a48", "#2b6e4b"] as const; const GROUT = "rgba(8, 38, 22, 0.2)"; const ROCK_MESH = buildVoronoiMesh(42, MESH_W, MESH_H, 0x475256, { siteGenerator: "poisson", }); function hexToRgb(hex: string) { const n = parseInt(hex.slice(1), 16); return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 }; } function mixHex(a: string, b: string, t: number) { const c1 = hexToRgb(a); const c2 = hexToRgb(b); const u = (v: number) => Math.round(v).toString(16).padStart(2, "0"); return `#${u(c1.r + (c2.r - c1.r) * t)}${u(c1.g + (c2.g - c1.g) * t)}${u(c1.b + (c2.b - c1.b) * t)}`; } function rockFill(cell: VoronoiCell, index: number) { const cx = cell.points.reduce((s, p) => s + p[0], 0) / cell.points.length; const cy = cell.points.reduce((s, p) => s + p[1], 0) / cell.points.length; const yNorm = cy / MESH_H; const gradient = mixHex("#2a6f4a", "#1a5c38", Math.min(1, yNorm * 0.45 + 0.25)); const tone = ROCK_TONES[(index * 11 + Math.floor(cx * 0.55)) % ROCK_TONES.length]; return mixHex(gradient, tone, 0.38); } type Props = { className?: string; style?: CSSProperties; /** Slight vertical wash (two-days section). */ gradient?: boolean; }; export function RoundedRockVoronoiBackground({ className, style, gradient = false, }: Props) { const cells = ROCK_MESH.map((cell, i) => ({ d: cell.d, fill: rockFill(cell, i), })); return (
{cells.map((cell, i) => ( ))}
{gradient && (
)}
); }