diff --git a/modules/rnetwork/components/folk-graph-viewer.ts b/modules/rnetwork/components/folk-graph-viewer.ts index 73b607b..6a705ac 100644 --- a/modules/rnetwork/components/folk-graph-viewer.ts +++ b/modules/rnetwork/components/folk-graph-viewer.ts @@ -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); }