mirror of
git://f0xx.org/ac/ac-be-hub
synced 2026-08-09 20:59:23 +03:00
Hub globe3d=3..5: fix E-W grid rotation iterations.
Strip baked SVG grid from rotating ocean (solid fill v3+). v3 sphere parallels only; v4 +meridians; v5 hub ellipse trace. Continents skipped v3+ until wrap is revisited. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
/**
|
||||
* Hub landing globe — orthographic Canvas2D (?globe3d=N).
|
||||
* globe3d=0: flat SVG layers (index.php).
|
||||
* globe3d=1: legacy hybrid ortho front + mercator back.
|
||||
* globe3d=2+: mercator wrap on visible hemisphere + full-sphere grid rings.
|
||||
* globe3d=1: legacy hybrid ortho front + mercator back + hub SVG grid trace.
|
||||
* globe3d=2: mercator land attempt + full-sphere grid (superseded).
|
||||
* globe3d=3: solid ocean, E–W parallels only (true latitude rings) — no baked grid bleed.
|
||||
* globe3d=4: solid ocean, full sphere grid (parallels + meridians).
|
||||
* globe3d=5: solid ocean, E–W via hub ellipse → sphere trace (matches globe3d=0 art at rest).
|
||||
*/
|
||||
export const GLOBE_VIEW = { imgW: 1920, imgH: 1080, cx: 720, cy: 560, r: 322, rim: 330 };
|
||||
|
||||
@@ -15,11 +18,16 @@ const RASTER_SCALE = 2;
|
||||
const AXIS_STEPS = 160;
|
||||
const SPHERE_AXIS_STEPS = 180;
|
||||
|
||||
/** Latitudes derived from hub-logo-fragment-globe-grid-overlay.svg E–W ellipses. */
|
||||
/** Latitudes from hub grid overlay ellipse centers (meridian x = cx). */
|
||||
function hubParallelLat(hubCy) {
|
||||
const texY = (GLOBE_VIEW.cy - hubCy) / GLOBE_VIEW.r;
|
||||
return Math.asin(Math.max(-1, Math.min(1, texY)));
|
||||
}
|
||||
|
||||
const SPHERE_PARALLELS = [
|
||||
{ lat: 0, bright: true },
|
||||
{ lat: Math.asin((560 - 392) / 322), bright: false },
|
||||
{ lat: Math.asin((560 - 728) / 322), bright: false },
|
||||
{ lat: hubParallelLat(392), bright: false },
|
||||
{ lat: hubParallelLat(728), bright: false },
|
||||
];
|
||||
|
||||
/** Longitudes at equator from hub N–S meridian artwork (x=620 / x=820). */
|
||||
@@ -311,8 +319,9 @@ function meridianBodyRingAtLon(lon, steps) {
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Full-sphere grid rings (globe3d>=2) — visible segments only, no front-hemisphere SVG trace. */
|
||||
function drawSphereGrid(ctx, cx, cy, r, rotY, layoutScale) {
|
||||
/** Full-sphere grid rings (globe3d>=4). */
|
||||
function drawSphereGrid(ctx, cx, cy, r, rotY, layoutScale, opts) {
|
||||
const parallelsOnly = !!(opts && opts.parallelsOnly);
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
||||
@@ -333,26 +342,45 @@ function drawSphereGrid(ctx, cx, cy, r, rotY, 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
|
||||
);
|
||||
if (!parallelsOnly) {
|
||||
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();
|
||||
}
|
||||
|
||||
/** Hub SVG ellipses traced on sphere (globe3d=5) — matches static art at yaw 0. */
|
||||
function drawHubParallelGrid(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) {
|
||||
strokeBodyRing(ctx, cx, cy, r, rotY, ellipseBodyRing(ellipse, AXIS_STEPS), function (z) {
|
||||
return parallelStyle(ellipse.bright, layoutScale, z, LIMB_Z);
|
||||
}, LIMB_Z);
|
||||
});
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
/** Hub SVG grid: E–W ellipses + N–S meridian paths (matches globe3d=0 artwork geometry). */
|
||||
function drawHubGrid(ctx, cx, cy, r, rotY, layoutScale) {
|
||||
/** Hub SVG grid: E–W ellipses + N–S meridian paths (globe3d=1). */
|
||||
function drawHubGrid(ctx, cx, cy, r, rotY, layoutScale, opts) {
|
||||
const parallelsOnly = !!(opts && opts.parallelsOnly);
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
||||
@@ -364,15 +392,55 @@ function drawHubGrid(ctx, cx, cy, r, rotY, layoutScale) {
|
||||
});
|
||||
});
|
||||
|
||||
HUB_NS_MERIDIANS.forEach(function (mer) {
|
||||
strokeBodyRing(ctx, cx, cy, r, rotY, hubPointsToBodyRing(mer.points), function (z) {
|
||||
return meridianStyle(mer.prime, layoutScale, z);
|
||||
if (!parallelsOnly) {
|
||||
HUB_NS_MERIDIANS.forEach(function (mer) {
|
||||
strokeBodyRing(ctx, cx, cy, r, rotY, hubPointsToBodyRing(mer.points), function (z) {
|
||||
return meridianStyle(mer.prime, layoutScale, z);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function drawGlobeGrid(ctx, cx, cy, r, rotY, layoutScale, renderVersion) {
|
||||
if (renderVersion === 3) {
|
||||
drawSphereGrid(ctx, cx, cy, r, rotY, layoutScale, { parallelsOnly: true });
|
||||
return;
|
||||
}
|
||||
if (renderVersion === 4) {
|
||||
drawSphereGrid(ctx, cx, cy, r, rotY, layoutScale, { parallelsOnly: false });
|
||||
return;
|
||||
}
|
||||
if (renderVersion === 5) {
|
||||
drawHubParallelGrid(ctx, cx, cy, r, rotY, layoutScale);
|
||||
return;
|
||||
}
|
||||
if (renderVersion >= 2) {
|
||||
drawSphereGrid(ctx, cx, cy, r, rotY, layoutScale, { parallelsOnly: false });
|
||||
return;
|
||||
}
|
||||
drawHubGrid(ctx, cx, cy, r, rotY, layoutScale, { parallelsOnly: false });
|
||||
}
|
||||
|
||||
function drawOceanRim(ctx, globeImg, cx, cy, r, rim, layoutScale, renderVersion) {
|
||||
if (renderVersion >= 3) {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, rim, 0, Math.PI * 2);
|
||||
ctx.arc(cx, cy, r, 0, Math.PI * 2, true);
|
||||
ctx.clip();
|
||||
const g = ctx.createRadialGradient(cx, cy, r, cx, cy, rim);
|
||||
g.addColorStop(0, 'rgba(28,48,74,0.98)');
|
||||
g.addColorStop(1, 'rgba(14,24,38,0.92)');
|
||||
ctx.fillStyle = g;
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
return;
|
||||
}
|
||||
drawRim(ctx, globeImg, cx, cy, r, rim, layoutScale);
|
||||
}
|
||||
|
||||
function sampleHub(layer, tex) {
|
||||
const hp = {
|
||||
x: GLOBE_VIEW.cx + tex.x * GLOBE_VIEW.r,
|
||||
@@ -415,15 +483,21 @@ function isGridPixel(r, g, b, a) {
|
||||
return Math.max(r, g, b) - Math.min(r, g, b) < 85;
|
||||
}
|
||||
|
||||
function sampleOceanRgba(globe, tex) {
|
||||
const OCEAN_SOLID = [OCEAN_FALLBACK.r, OCEAN_FALLBACK.g, OCEAN_FALLBACK.b, 255];
|
||||
|
||||
function sampleOceanSolid() {
|
||||
return OCEAN_SOLID;
|
||||
}
|
||||
|
||||
function sampleOceanClean(globe, tex) {
|
||||
if (tex.z <= FRONT_Z) {
|
||||
return [OCEAN_FALLBACK.r, OCEAN_FALLBACK.g, OCEAN_FALLBACK.b, 255];
|
||||
return OCEAN_SOLID;
|
||||
}
|
||||
const s = sampleHub(globe, tex);
|
||||
if (s[3] > 8 && !isGridPixel(s[0], s[1], s[2], s[3])) {
|
||||
return [s[0], s[1], s[2], 255];
|
||||
}
|
||||
return [OCEAN_FALLBACK.r, OCEAN_FALLBACK.g, OCEAN_FALLBACK.b, 255];
|
||||
return OCEAN_SOLID;
|
||||
}
|
||||
|
||||
function sampleLandRgbaLegacy(continents, mercFlat, tex) {
|
||||
@@ -633,28 +707,37 @@ export async function mountHubGlobe(stage, opts) {
|
||||
cy,
|
||||
r,
|
||||
fillDiskRegion(ctx, cx, cy, r, rotation, function (tex) {
|
||||
return sampleOceanRgba(globe, tex);
|
||||
if (renderVersion >= 3) {
|
||||
return sampleOceanSolid();
|
||||
}
|
||||
return sampleOceanClean(globe, tex);
|
||||
})
|
||||
);
|
||||
blitLayer(
|
||||
ctx,
|
||||
cx,
|
||||
cy,
|
||||
r,
|
||||
fillDiskRegion(ctx, cx, cy, r, rotation, function (tex) {
|
||||
const l =
|
||||
renderVersion >= 2
|
||||
? sampleLandRgbaMercator(mercFlat, tex)
|
||||
: sampleLandRgbaLegacy(continents, mercFlat, tex);
|
||||
return l || [0, 0, 0, 0];
|
||||
})
|
||||
);
|
||||
if (renderVersion >= 2) {
|
||||
drawSphereGrid(ctx, cx, cy, r, rotation, layoutScale);
|
||||
} else {
|
||||
drawHubGrid(ctx, cx, cy, r, rotation, layoutScale);
|
||||
if (renderVersion === 1) {
|
||||
blitLayer(
|
||||
ctx,
|
||||
cx,
|
||||
cy,
|
||||
r,
|
||||
fillDiskRegion(ctx, cx, cy, r, rotation, function (tex) {
|
||||
const l = sampleLandRgbaLegacy(continents, mercFlat, tex);
|
||||
return l || [0, 0, 0, 0];
|
||||
})
|
||||
);
|
||||
} else if (renderVersion === 2) {
|
||||
blitLayer(
|
||||
ctx,
|
||||
cx,
|
||||
cy,
|
||||
r,
|
||||
fillDiskRegion(ctx, cx, cy, r, rotation, function (tex) {
|
||||
const l = sampleLandRgbaMercator(mercFlat, tex);
|
||||
return l || [0, 0, 0, 0];
|
||||
})
|
||||
);
|
||||
}
|
||||
drawRim(ctx, globeImg, cx, cy, r, rim, layoutScale);
|
||||
drawGlobeGrid(ctx, cx, cy, r, rotation, layoutScale, renderVersion);
|
||||
drawOceanRim(ctx, globeImg, cx, cy, r, rim, layoutScale, renderVersion);
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
||||
@@ -723,7 +806,14 @@ export async function mountHubGlobe(stage, opts) {
|
||||
|
||||
raf = requestAnimationFrame(tick);
|
||||
stage.dataset.globe3d = String(renderVersion);
|
||||
stage.dataset.globeAxis = renderVersion >= 2 ? 'ew-ns-sphere' : 'ew-ns-grid';
|
||||
stage.dataset.globeAxis =
|
||||
renderVersion >= 5
|
||||
? 'ew-hub-ellipse'
|
||||
: renderVersion >= 3
|
||||
? 'ew-sphere'
|
||||
: renderVersion >= 2
|
||||
? 'ew-ns-sphere'
|
||||
: 'ew-ns-grid';
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
|
||||
@@ -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 = '20260704globe2';
|
||||
var logoBuild = '20260704globe5';
|
||||
|
||||
function hubAsset(rel) {
|
||||
var link = document.querySelector('link[href*="hub.css"]');
|
||||
|
||||
Reference in New Issue
Block a user