/**
* Géométrie de la spirale de Fibonacci (pure, sans React) — partagée par le
* composant et le script de génération des icônes.
*/
type Square = { x: number; y: number; s: number };
type Dir = "right" | "down" | "left" | "up";
const DIRS: Dir[] = ["right", "down", "left", "up"];
export function buildSpiral(levels = 8) {
const fib = [1, 1];
while (fib.length < levels) fib.push(fib[fib.length - 1] + fib[fib.length - 2]);
const squares: Square[] = [{ x: 0, y: 0, s: 1 }];
const dirs: Dir[] = ["right"]; // direction "virtuelle" du premier carré
let bbox = { x: 0, y: 0, w: 1, h: 1 };
for (let i = 1; i < fib.length; i++) {
const s = fib[i];
const d = DIRS[(i - 1) % 4];
let sq: Square;
if (d === "right") sq = { x: bbox.x + bbox.w, y: bbox.y, s };
else if (d === "down") sq = { x: bbox.x, y: bbox.y + bbox.h, s };
else if (d === "left") sq = { x: bbox.x - s, y: bbox.y, s };
else sq = { x: bbox.x, y: bbox.y - s, s };
squares.push(sq);
dirs.push(d);
const x0 = Math.min(bbox.x, sq.x);
const y0 = Math.min(bbox.y, sq.y);
const x1 = Math.max(bbox.x + bbox.w, sq.x + sq.s);
const y1 = Math.max(bbox.y + bbox.h, sq.y + sq.s);
bbox = { x: x0, y: y0, w: x1 - x0, h: y1 - y0 };
}
// Centre de l'arc d'un carré = coin situé du côté du carré précédent
// (opposé à sa direction) et du côté de la direction suivante.
const opposite: Record
= {
right: "left",
left: "right",
up: "down",
down: "up",
};
const corner = (sq: Square, a: Dir, b: Dir) => {
const x = a === "left" || b === "left" ? sq.x : sq.x + sq.s;
const y = a === "up" || b === "up" ? sq.y : sq.y + sq.s;
return { x, y };
};
const centers = squares.map((sq, i) => {
if (i === 0) return { x: sq.x, y: sq.y }; // coin haut-gauche du 1er carré
const next = i + 1 < dirs.length ? dirs[i + 1] : DIRS[i % 4];
return corner(sq, opposite[dirs[i]], next);
});
let d = "";
let prev: { x: number; y: number } | null = null;
squares.forEach((sq, i) => {
const c = centers[i];
// Les deux extrémités possibles de l'arc : à distance s sur chaque axe.
const dx = c.x === sq.x ? sq.x + sq.s : sq.x - 0; // extrémité horizontale
const dy = c.y === sq.y ? sq.y + sq.s : sq.y - 0;
const pH = { x: c.x === sq.x ? dx : sq.x, y: c.y };
const pV = { x: c.x, y: c.y === sq.y ? dy : sq.y };
let from = pV;
let to = pH;
if (prev) {
const same = (p: { x: number; y: number }) =>
Math.abs(p.x - prev!.x) < 1e-9 && Math.abs(p.y - prev!.y) < 1e-9;
if (same(pH)) {
from = pH;
to = pV;
}
}
const cross = (from.x - c.x) * (to.y - c.y) - (from.y - c.y) * (to.x - c.x);
const sweep = cross > 0 ? 1 : 0;
if (!prev) d += `M ${from.x} ${from.y} `;
d += `A ${sq.s} ${sq.s} 0 0 ${sweep} ${to.x} ${to.y} `;
prev = to;
});
return { squares, path: d.trim(), bbox };
}