diff --git a/neode-ui/src/components/federation/NetworkMap3D.vue b/neode-ui/src/components/federation/NetworkMap3D.vue index 2747b5d4..d6db03b5 100644 --- a/neode-ui/src/components/federation/NetworkMap3D.vue +++ b/neode-ui/src/components/federation/NetworkMap3D.vue @@ -111,7 +111,9 @@ let frame = 0 let selfDots: GlobeDot[] = [] let selfRadiusPx = 15 -const PERSP = 3.2 +/** Perspective strength — measure() sets it per form factor: 3.2 gives the + * desktop scene real depth; a large value is near-orthographic (flat). */ +let persp = 3.2 let resizeObserver: ResizeObserver | null = null let intro: gsap.core.Timeline | null = null let spinTween: gsap.core.Tween | null = null @@ -155,7 +157,7 @@ function project(x: number, y: number, z: number) { const ct = Math.cos(cam.tilt), st = Math.sin(cam.tilt) const y2 = y * ct - z1 * st const z2 = y * st + z1 * ct - const s = PERSP / (PERSP + z2) + const s = persp / (persp + z2) return { x: width / 2 + x1 * unit * s * cam.zoom, y: centerY + y2 * unit * s * cam.zoom, @@ -554,10 +556,12 @@ function measure() { height = c.clientHeight svgRef.value?.setAttribute('viewBox', `0 0 ${width} ${height}`) - // Portrait (phone/companion): tilt the camera towards top-down so the - // orbit becomes a tall ellipse that uses the vertical space — nodes and - // labels spread out in two dimensions instead of stacking on a flat band. - cam.tilt = height > width * 1.15 ? -0.95 : -0.5 + // Portrait (phone/companion): go near top-down and near-orthographic so + // the map reads like the old 2D radial view — rings become circles, no + // perspective squash, maximum label spread. Desktop keeps the 3D depth. + const portrait = height > width + cam.tilt = portrait ? -1.35 : -0.5 + persp = portrait ? 16 : 3.2 // Auto-fit: sample the outermost orbit through the real camera math // (rotation-invariant — a ring about the Y axis projects identically at any @@ -577,7 +581,7 @@ function measure() { for (const y of [-0.2, 0.2]) { // covers yJitter + sway/bob amplitude const y2 = y * ct - z * st const z2 = y * st + z * ct - const s = PERSP / (PERSP + z2) + const s = persp / (persp + z2) maxAbsX = Math.max(maxAbsX, Math.abs(x * s)) yMin = Math.min(yMin, y2 * s) yMax = Math.max(yMax, y2 * s)