Files
websperiments/standalone/goldberg-sphere/placer_unfold.mjs
mikael-lovqvists-claude-agent 9600a2bc2a Add Goldberg Polyhedron Paint experiment
Interactive WebGL Goldberg polyhedron viewer and painter with PBR
shading, adjustable environment lighting, paint tools (pen, brush,
circle, fill, line, pick), undo/redo, colour palettes, and mesh
relaxation. Added to the standalone experiments index.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 16:00:19 +00:00

69 lines
2.3 KiB
JavaScript

// Placement via unfolding: each neighbor face is placed adjacent to its BFS
// parent by a similarity transform, then shrunk toward its centroid by
// DEPTH_SHRINK^depth so depth rings visually separate.
//
// Returns Map<face_index, { vertices_2d, centroid_2d, depth }>
import {
regular_polygon_2d,
centroid_2d,
similarity_transform_2d,
apply_transform_2d,
} from './geometry.mjs';
const DEPTH_SHRINK = 0.82;
export function place_unfold(poly, neighborhood, root_face_index, cx, cy, face_size) {
const placements = new Map();
for (const entry of neighborhood) {
const fi = entry.face.index;
const face = entry.face;
const n = face.size;
if (entry.parent_index === null) {
// Root face: regular polygon centred on canvas.
const pts = regular_polygon_2d(n, face_size).map(([x, y]) => [x + cx, y + cy]);
placements.set(fi, { vertices_2d: pts, centroid_2d: [cx, cy], depth: 0 });
} else {
const parent_pl = placements.get(entry.parent_index);
if (!parent_pl) { continue; }
const parent_face = poly.faces[entry.parent_index];
const pei = entry.parent_edge_index;
const pa = parent_pl.vertices_2d[pei];
const pb = parent_pl.vertices_2d[(pei + 1) % parent_face.size];
// Find which edge of this face leads back to the parent.
let nei = -1;
for (let j = 0; j < face.edge_neighbors.length; j++) {
if (face.edge_neighbors[j] === entry.parent_index) { nei = j; break; }
}
if (nei === -1) {
console.warn(`place_unfold: face ${fi} has no reverse edge to parent ${entry.parent_index}`);
continue;
}
// Winding is opposite: parent (a→b) ≡ neighbor (b→a).
const local_pts = regular_polygon_2d(n, 1);
const local_a = local_pts[nei];
const local_b = local_pts[(nei + 1) % n];
const transform = similarity_transform_2d(local_a, local_b, pb, pa);
let vertices_2d = local_pts.map(pt => apply_transform_2d(transform, pt));
const c = centroid_2d(vertices_2d);
// Shrink toward centroid after transform (before would be cancelled by scaling).
const shrink = Math.pow(DEPTH_SHRINK, entry.depth);
vertices_2d = vertices_2d.map(([x, y]) => [
c[0] + (x - c[0]) * shrink,
c[1] + (y - c[1]) * shrink,
]);
placements.set(fi, { vertices_2d, centroid_2d: c, depth: entry.depth });
}
}
return placements;
}