feat(rnetwork): concentric spheres layout with Fibonacci distribution

Replace flat ring layout with 3D sphere distribution using Fibonacci
spiral for even node placement. Wireframe sphere guides replace flat
ring guides — visible from every camera angle.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-15 17:05:30 -07:00
parent 246b51b2e0
commit 03de21ddd5
1 changed files with 18 additions and 15 deletions

View File

@ -1075,9 +1075,9 @@ class FolkGraphViewer extends HTMLElement {
n.fx = 0; n.fy = 0; n.fz = 0;
}
this.placeRing(adminNodes, 30);
this.placeRing(memberNodes, 80);
this.placeRing(viewerNodes, 160);
this.placeSphere(adminNodes, 30);
this.placeSphere(memberNodes, 80);
this.placeSphere(viewerNodes, 160);
this.graph.graphData(data);
this.graph.d3ReheatSimulation();
@ -1087,14 +1087,18 @@ class FolkGraphViewer extends HTMLElement {
this.addRingGuides();
}
private placeRing(nodes: GraphNode[], radius: number) {
private placeSphere(nodes: GraphNode[], radius: number) {
const count = nodes.length;
if (count === 0) return;
// Fibonacci sphere — even distribution on a sphere surface
const goldenAngle = Math.PI * (3 - Math.sqrt(5));
for (let i = 0; i < count; i++) {
const angle = (2 * Math.PI * i) / count;
nodes[i].fx = Math.cos(angle) * radius;
nodes[i].fy = 0;
nodes[i].fz = Math.sin(angle) * radius;
const y = 1 - (2 * i) / (count - 1 || 1); // -1 to 1
const r = Math.sqrt(1 - y * y);
const theta = goldenAngle * i;
nodes[i].fx = Math.cos(theta) * r * radius;
nodes[i].fy = y * radius;
nodes[i].fz = Math.sin(theta) * r * radius;
}
}
@ -1116,23 +1120,22 @@ class FolkGraphViewer extends HTMLElement {
const scene = this.graph.scene();
if (!scene) return;
const ringRadii = [
const sphereRadii = [
{ r: 30, color: 0xa78bfa, label: "Admin" },
{ r: 80, color: 0x10b981, label: "Member" },
{ r: 160, color: 0x3b82f6, label: "Viewer" },
];
for (const { r, color } of ringRadii) {
const geometry = new THREE.RingGeometry(r - 0.5, r + 0.5, 128);
for (const { r, color } of sphereRadii) {
// Wireframe sphere guide
const geometry = new THREE.SphereGeometry(r, 24, 16);
const material = new THREE.MeshBasicMaterial({
color,
transparent: true,
opacity: 0.15,
side: THREE.DoubleSide,
opacity: 0.06,
wireframe: true,
});
const mesh = new THREE.Mesh(geometry, material);
mesh.rotation.x = -Math.PI / 2; // flat on XZ plane
mesh.position.y = 0;
scene.add(mesh);
this.ringGuides.push(mesh);
}