cloudHues
Each cloud's colour, as a place in a palette of count (the page's --cm0…: the graph draws with them and the panel
beside it names clouds with them), or -1 for loose ends: the biggest cloud first, each taking the colour least like
those of the clouds nearest it.
import type { CloudLayout } from "#codebase/tcmts/share"; export function cloudHues(layout: CloudLayout, count = 14): number[] { const clouds = layout.clouds; const chosen: (number | undefined)[] = clouds.map(() => undefined); const order = [...clouds.keys()].sort((a, b) => clouds[b]!.n - clouds[a]!.n); for (const c of order) { const me = clouds[c]!; if (me.loose) continue; const near = clouds .map((d, i) => [i, Math.hypot(d.x - me.x, d.y - me.y) - d.r - me.r] as const) .filter(([i]) => i !== c && chosen[i] !== undefined) .sort((a, b) => a[1] - b[1]) .slice(0, 6); let best = 0; let bestScore = -Infinity; for (let p = 0; p < count; p++) { // (a neighbour's colour costs the more the nearer it is; a colour used often costs a little) let score = -chosen.filter((h) => h === p).length * 0.5; for (const [i, d] of near) if (chosen[i] === p) score -= 1000 / Math.max(20, d + 60); if (score > bestScore) { bestScore = score; best = p; } } chosen[c] = best; } return chosen.map((p) => p ?? -1); }