// constellation.jsx — La carte-constellation des 38 communes de la Pévèle Carembault // Nœuds lumineux + arcs qui pulsent à chaque Exaucement. const COMMUNES = [ // 38 communes Pévèle Carembault (approximation géographique normalisée 0..1) { n: "Orchies", x: 0.66, y: 0.46, big: true }, { n: "Templeuve-en-Pévèle",x: 0.42, y: 0.40, big: true }, { n: "Cysoing", x: 0.34, y: 0.32, big: true }, { n: "Pont-à-Marcq", x: 0.34, y: 0.50 }, { n: "Phalempin", x: 0.22, y: 0.62 }, { n: "Thumeries", x: 0.40, y: 0.66 }, { n: "Ostricourt", x: 0.28, y: 0.74 }, { n: "Wahagnies", x: 0.34, y: 0.70 }, { n: "Mons-en-Pévèle", x: 0.46, y: 0.56 }, { n: "Mérignies", x: 0.40, y: 0.52 }, { n: "Avelin", x: 0.30, y: 0.42 }, { n: "Attiches", x: 0.36, y: 0.58 }, { n: "Ennevelin", x: 0.42, y: 0.46 }, { n: "Tourmignies", x: 0.32, y: 0.56 }, { n: "Moncheaux", x: 0.40, y: 0.74 }, { n: "Genech", x: 0.42, y: 0.32 }, { n: "Bachy", x: 0.46, y: 0.22 }, { n: "Cobrieux", x: 0.40, y: 0.26 }, { n: "Bourghelles", x: 0.32, y: 0.26 }, { n: "Wannehain", x: 0.46, y: 0.28 }, { n: "Mouchin", x: 0.50, y: 0.30 }, { n: "Louvil", x: 0.36, y: 0.28 }, { n: "Bersée", x: 0.46, y: 0.46 }, { n: "Bouvignies", x: 0.62, y: 0.54 }, { n: "Coutiches", x: 0.56, y: 0.58 }, { n: "Beuvry-la-Forêt", x: 0.74, y: 0.52 }, { n: "Auchy-lez-Orchies", x: 0.62, y: 0.42 }, { n: "Landas", x: 0.74, y: 0.40 }, { n: "Nomain", x: 0.62, y: 0.34 }, { n: "Saméon", x: 0.78, y: 0.34 }, { n: "Aix-en-Pévèle", x: 0.54, y: 0.40 }, { n: "Cappelle-en-Pévèle", x: 0.50, y: 0.46 }, { n: "Chemy", x: 0.20, y: 0.54 }, { n: "La Neuville", x: 0.24, y: 0.46 }, { n: "Faumont", x: 0.56, y: 0.66 }, { n: "Rosult", x: 0.84, y: 0.42 }, { n: "Tilloy-lez-Marchiennes", x: 0.86, y: 0.50 }, { n: "Marchiennes", x: 0.86, y: 0.58 }, ]; // Pseudo-random but deterministic graph of "connections" (lignes lumineuses entre communes voisines) function buildEdges(nodes) { const edges = []; nodes.forEach((a, i) => { // Connect each node to its 2-3 closest neighbors const sorted = nodes .map((b, j) => ({ b, j, d: Math.hypot(a.x - b.x, a.y - b.y) })) .filter(o => o.j !== i) .sort((x, y) => x.d - y.d) .slice(0, 2 + (i % 2)); sorted.forEach(({ j }) => { const key = [Math.min(i, j), Math.max(i, j)].join("-"); if (!edges.find(e => e.key === key)) { edges.push({ key, i: Math.min(i, j), j: Math.max(i, j) }); } }); }); return edges; } const EDGES = buildEdges(COMMUNES); function Constellation({ variant = "hero", showLabels = "few" }) { // variant: "hero" (large), "compact" // showLabels: "all" | "few" | "none" const W = 1000, H = 750; const [pulses, setPulses] = React.useState([]); // active arc pulses const [activeNode, setActiveNode] = React.useState(null); const idRef = React.useRef(0); // Trigger random "exaucement" pulses React.useEffect(() => { const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches; if (reduced) return; const interval = setInterval(() => { const edge = EDGES[Math.floor(Math.random() * EDGES.length)]; const id = ++idRef.current; setPulses(p => [...p, { id, ...edge, t: Date.now() }]); setActiveNode(Math.random() < 0.5 ? edge.i : edge.j); setTimeout(() => { setPulses(p => p.filter(x => x.id !== id)); }, 2400); }, 900); return () => clearInterval(interval); }, []); return (
{/* Ambient halos */} {/* Subtle territory contour */} {/* Edges (constellation lines) */} {EDGES.map(e => { const a = COMMUNES[e.i], b = COMMUNES[e.j]; return ( ); })} {/* Pulse arcs */} {pulses.map(p => { const a = COMMUNES[p.i], b = COMMUNES[p.j]; const x1 = a.x * W, y1 = a.y * H; const x2 = b.x * W, y2 = b.y * H; // Curve via midpoint offset const mx = (x1 + x2) / 2; const my = (y1 + y2) / 2; const dx = x2 - x1, dy = y2 - y1; const len = Math.hypot(dx, dy); const cx = mx - (dy / len) * (len * 0.25); const cy = my - (-dx / len) * (len * 0.25); return ( {/* Spark traveling along arc */} ); })} {/* Nodes (38 communes) */} {COMMUNES.map((c, i) => { const cx = c.x * W, cy = c.y * H; const isActive = activeNode === i; const r = c.big ? 4.5 : 3; return ( {/* Outer glow */} {/* Core */} {/* Pulse ring */} {/* Labels */} {(showLabels === "all" || (showLabels === "few" && c.big)) && ( {c.n} )} ); })}
); } window.Constellation = Constellation; window.COMMUNES = COMMUNES; window.EDGES = EDGES;