import { useMemo } from 'react' // Deterministic isometric "sculpture" for a project without a demo image: the repo name seeds a small voxel // stack, so every project gets its own picture that stays the same between visits. function hash(text: string): number { let h = 2166136261 for (let i = 0; i < text.length; i++) { h ^= text.charCodeAt(i) h = Math.imul(h, 16777619) } return h >>> 0 } function random(seed: number) { let a = seed return () => { a = (a + 0x6d2b79f5) | 0 let t = Math.imul(a ^ (a >>> 15), 1 | a) t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t return ((t ^ (t >>> 14)) >>> 0) / 4294967296 } } const GRID = 5 const TILE = 54 const RISE = 30 const ACCENTS = ['#e0384b', '#5aa300', '#2c63e0'] const NEUTRAL = { top: 'var(--cube-top)', left: 'var(--cube-left)', right: 'var(--cube-right)' } function mix(hex: string, other: number, amount: number): string { const channel = (i: number) => parseInt(hex.slice(1 + i * 2, 3 + i * 2), 16) const to = (i: number) => Math.round(channel(i) * (1 - amount) + other * amount).toString(16).padStart(2, '0') return `#${to(0)}${to(1)}${to(2)}` } type Cube = { i: number; j: number; k: number; top: string; left: string; right: string } function build(name: string): Cube[] { const next = random(hash(name)) const cubes: Cube[] = [] const accent = ACCENTS[Math.floor(next() * ACCENTS.length)] const second = ACCENTS[Math.floor(next() * ACCENTS.length)] for (let i = 0; i < GRID; i++) { for (let j = 0; j < GRID; j++) { const edge = Math.abs(i - 2) + Math.abs(j - 2) const height = Math.max(0, Math.round(next() ** 1.3 * 4 - edge * 0.35)) for (let k = 0; k < height; k++) { const isTop = k === height - 1 const pick = next() const color = isTop && pick < 0.3 ? accent : isTop && pick < 0.4 ? second : null cubes.push( color ? { i, j, k, top: mix(color, 255, 0.22), left: color, right: mix(color, 0, 0.24) } : { i, j, k, ...NEUTRAL }, ) } } } if (cubes.length < 5) cubes.push({ i: 2, j: 2, k: 0, ...NEUTRAL }, { i: 2, j: 2, k: 1, top: mix(accent, 255, 0.22), left: accent, right: mix(accent, 0, 0.24) }) return cubes.sort((a, b) => a.i + a.j - (b.i + b.j) || a.k - b.k) } const face = (points: [number, number][]) => points.map((p) => p.join(',')).join(' ') export default function Cover({ name }: Readonly<{ name: string }>) { const cubes = useMemo(() => build(name), [name]) const originX = 220 const originY = 150 const point = (i: number, j: number, z: number, dx: number, dy: number): [number, number] => [ +(originX + (i - j) * (TILE / 2) + dx).toFixed(1), +(originY + (i + j) * (TILE / 4) + dy - z).toFixed(1), ] return ( ) }