diff --git a/assets/hub-globe.js b/assets/hub-globe.js index ccc7046..0284c46 100644 --- a/assets/hub-globe.js +++ b/assets/hub-globe.js @@ -8,6 +8,8 @@ * globe3d=5: hub ellipse trace (deprecated — breaks under rotation). * globe3d=6: v4 math + latitude band fill (continuous arc paths). * globe3d=7: v4 math + wide centerline stroke bands (no fill quads). + * globe3d=8: hub SVG ellipse → sphere trace, wide stroke, gap-safe (ellipses at rest). + * globe3d=9: v8 parallels + v4 sphere meridians (split best-of). */ export const GLOBE_VIEW = { imgW: 1920, imgH: 1080, cx: 720, cy: 560, r: 322, rim: 330 }; @@ -208,13 +210,9 @@ function ellipseBodyRing(ellipse, steps) { const out = []; for (let i = 0; i <= steps; i++) { const a = (i / steps) * Math.PI * 2; - const body = hubPixelToBody( - ellipse.cx + ellipse.rx * Math.cos(a), - ellipse.cy + ellipse.ry * Math.sin(a) + out.push( + hubPixelToBody(ellipse.cx + ellipse.rx * Math.cos(a), ellipse.cy + ellipse.ry * Math.sin(a)) ); - if (body) { - out.push(body); - } } return out; } @@ -278,6 +276,10 @@ function strokeBodyRing(ctx, cx, cy, r, rotY, bodies, styleAt, limbZ) { } for (let i = 0; i < bodies.length; i++) { + if (!bodies[i]) { + flush(); + continue; + } const view = bodyToView(bodies[i], rotY); if (view.z <= zCut) { flush(); @@ -486,6 +488,67 @@ function drawSphereGridBanded(ctx, cx, cy, r, rotY, layoutScale, opts) { ctx.restore(); } +/** Hub ellipse centerline on sphere, wide stroke — stays elliptical under Y rotation (globe3d=8). */ +function strokeHubEllipseParallel(ctx, cx, cy, r, rotY, ellipse, layoutScale) { + const scale = r / GLOBE_VIEW.r; + const bandPx = Math.max(ellipse.bright ? 5 : 8, 2 * ellipse.ry * scale); + const par = { bright: ellipse.bright }; + const ring = ellipseBodyRing(ellipse, AXIS_STEPS); + strokeBodyRing( + ctx, + cx, + cy, + r, + rotY, + ring, + function (z) { + return parallelBandStyle(par, z, bandPx, 'halo'); + }, + LIMB_Z + ); + strokeBodyRing( + ctx, + cx, + cy, + r, + rotY, + ring, + function (z) { + return parallelBandStyle(par, z, bandPx, 'core'); + }, + LIMB_Z + ); +} + +/** globe3d=8 — hub E–W ellipses on sphere; v4 N–S meridians. */ +function drawHubEllipseGridRotated(ctx, cx, cy, r, rotY, layoutScale) { + ctx.save(); + ctx.beginPath(); + ctx.arc(cx, cy, r, 0, Math.PI * 2); + ctx.clip(); + + HUB_EW_PARALLELS.forEach(function (ellipse) { + strokeHubEllipseParallel(ctx, cx, cy, r, rotY, ellipse, layoutScale); + }); + + SPHERE_MERIDIANS.forEach(function (mer) { + strokeBodyRing( + ctx, + cx, + cy, + r, + rotY, + meridianBodyRingAtLon(mer.lon, SPHERE_AXIS_STEPS), + function (z) { + return meridianStyle(mer.prime, layoutScale, z, LIMB_Z); + }, + LIMB_Z + ); + }); + + ctx.restore(); +} + /** globe3d=7 — v4 sphere model, wide stroke bands (no path fill). */ function drawSphereGridWideStroke(ctx, cx, cy, r, rotY, layoutScale, opts) { const parallelsOnly = !!(opts && opts.parallelsOnly); @@ -623,6 +686,10 @@ function drawGlobeGrid(ctx, cx, cy, r, rotY, layoutScale, renderVersion) { drawSphereGridWideStroke(ctx, cx, cy, r, rotY, layoutScale, { parallelsOnly: false }); return; } + if (renderVersion === 8 || renderVersion === 9) { + drawHubEllipseGridRotated(ctx, cx, cy, r, rotY, layoutScale); + return; + } if (renderVersion >= 2) { drawSphereGrid(ctx, cx, cy, r, rotY, layoutScale, { parallelsOnly: false }); return; @@ -1014,17 +1081,19 @@ export async function mountHubGlobe(stage, opts) { raf = requestAnimationFrame(tick); stage.dataset.globe3d = String(renderVersion); stage.dataset.globeAxis = - renderVersion >= 7 - ? 'ew-sphere-stroke' - : renderVersion >= 6 - ? 'ew-sphere-band' - : renderVersion >= 5 - ? 'ew-hub-ellipse' - : renderVersion >= 3 - ? 'ew-sphere' - : renderVersion >= 2 - ? 'ew-ns-sphere' - : 'ew-ns-grid'; + renderVersion >= 8 + ? 'ew-hub-ellipse-rot' + : renderVersion >= 7 + ? 'ew-sphere-stroke' + : renderVersion >= 6 + ? 'ew-sphere-band' + : renderVersion >= 5 + ? 'ew-hub-ellipse' + : renderVersion >= 3 + ? 'ew-sphere' + : renderVersion >= 2 + ? 'ew-ns-sphere' + : 'ew-ns-grid'; return { destroy() { diff --git a/index.php b/index.php index e888047..686f990 100644 --- a/index.php +++ b/index.php @@ -187,7 +187,7 @@ function hub_h(string $s): string { var supportsLayout = !!(window.CSS && CSS.supports && CSS.supports('object-fit', 'cover')); var logoEnabled = !!(stage && supportsSvg && supportsLayout); - var logoBuild = '20260704globe7'; + var logoBuild = '20260704globe8'; function hubAsset(rel) { var link = document.querySelector('link[href*="hub.css"]');