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).
|
* Hub landing globe — orthographic Canvas2D (?globe3d=N).
|
||||||
* globe3d=0: flat SVG layers (index.php).
|
* globe3d=0: flat SVG layers (index.php).
|
||||||
* globe3d=1: legacy hybrid ortho front + mercator back.
|
* globe3d=1: legacy hybrid ortho front + mercator back + hub SVG grid trace.
|
||||||
* globe3d=2+: mercator wrap on visible hemisphere + full-sphere grid rings.
|
* 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 };
|
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 AXIS_STEPS = 160;
|
||||||
const SPHERE_AXIS_STEPS = 180;
|
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 = [
|
const SPHERE_PARALLELS = [
|
||||||
{ lat: 0, bright: true },
|
{ lat: 0, bright: true },
|
||||||
{ lat: Math.asin((560 - 392) / 322), bright: false },
|
{ lat: hubParallelLat(392), bright: false },
|
||||||
{ lat: Math.asin((560 - 728) / 322), bright: false },
|
{ lat: hubParallelLat(728), bright: false },
|
||||||
];
|
];
|
||||||
|
|
||||||
/** Longitudes at equator from hub N–S meridian artwork (x=620 / x=820). */
|
/** Longitudes at equator from hub N–S meridian artwork (x=620 / x=820). */
|
||||||
@@ -311,8 +319,9 @@ function meridianBodyRingAtLon(lon, steps) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Full-sphere grid rings (globe3d>=2) — visible segments only, no front-hemisphere SVG trace. */
|
/** Full-sphere grid rings (globe3d>=4). */
|
||||||
function drawSphereGrid(ctx, cx, cy, r, rotY, layoutScale) {
|
function drawSphereGrid(ctx, cx, cy, r, rotY, layoutScale, opts) {
|
||||||
|
const parallelsOnly = !!(opts && opts.parallelsOnly);
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
||||||
@@ -333,6 +342,7 @@ function drawSphereGrid(ctx, cx, cy, r, rotY, layoutScale) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!parallelsOnly) {
|
||||||
SPHERE_MERIDIANS.forEach(function (mer) {
|
SPHERE_MERIDIANS.forEach(function (mer) {
|
||||||
strokeBodyRing(
|
strokeBodyRing(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -347,12 +357,30 @@ function drawSphereGrid(ctx, cx, cy, r, rotY, layoutScale) {
|
|||||||
LIMB_Z
|
LIMB_Z
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Hub SVG grid: E–W ellipses + N–S meridian paths (matches globe3d=0 artwork geometry). */
|
/** Hub SVG ellipses traced on sphere (globe3d=5) — matches static art at yaw 0. */
|
||||||
function drawHubGrid(ctx, cx, cy, r, rotY, layoutScale) {
|
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 (globe3d=1). */
|
||||||
|
function drawHubGrid(ctx, cx, cy, r, rotY, layoutScale, opts) {
|
||||||
|
const parallelsOnly = !!(opts && opts.parallelsOnly);
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
||||||
@@ -364,15 +392,55 @@ function drawHubGrid(ctx, cx, cy, r, rotY, layoutScale) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!parallelsOnly) {
|
||||||
HUB_NS_MERIDIANS.forEach(function (mer) {
|
HUB_NS_MERIDIANS.forEach(function (mer) {
|
||||||
strokeBodyRing(ctx, cx, cy, r, rotY, hubPointsToBodyRing(mer.points), function (z) {
|
strokeBodyRing(ctx, cx, cy, r, rotY, hubPointsToBodyRing(mer.points), function (z) {
|
||||||
return meridianStyle(mer.prime, layoutScale, z);
|
return meridianStyle(mer.prime, layoutScale, z);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ctx.restore();
|
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) {
|
function sampleHub(layer, tex) {
|
||||||
const hp = {
|
const hp = {
|
||||||
x: GLOBE_VIEW.cx + tex.x * GLOBE_VIEW.r,
|
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;
|
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) {
|
if (tex.z <= FRONT_Z) {
|
||||||
return [OCEAN_FALLBACK.r, OCEAN_FALLBACK.g, OCEAN_FALLBACK.b, 255];
|
return OCEAN_SOLID;
|
||||||
}
|
}
|
||||||
const s = sampleHub(globe, tex);
|
const s = sampleHub(globe, tex);
|
||||||
if (s[3] > 8 && !isGridPixel(s[0], s[1], s[2], s[3])) {
|
if (s[3] > 8 && !isGridPixel(s[0], s[1], s[2], s[3])) {
|
||||||
return [s[0], s[1], s[2], 255];
|
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) {
|
function sampleLandRgbaLegacy(continents, mercFlat, tex) {
|
||||||
@@ -633,28 +707,37 @@ export async function mountHubGlobe(stage, opts) {
|
|||||||
cy,
|
cy,
|
||||||
r,
|
r,
|
||||||
fillDiskRegion(ctx, cx, cy, r, rotation, function (tex) {
|
fillDiskRegion(ctx, cx, cy, r, rotation, function (tex) {
|
||||||
return sampleOceanRgba(globe, tex);
|
if (renderVersion >= 3) {
|
||||||
|
return sampleOceanSolid();
|
||||||
|
}
|
||||||
|
return sampleOceanClean(globe, tex);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
if (renderVersion === 1) {
|
||||||
blitLayer(
|
blitLayer(
|
||||||
ctx,
|
ctx,
|
||||||
cx,
|
cx,
|
||||||
cy,
|
cy,
|
||||||
r,
|
r,
|
||||||
fillDiskRegion(ctx, cx, cy, r, rotation, function (tex) {
|
fillDiskRegion(ctx, cx, cy, r, rotation, function (tex) {
|
||||||
const l =
|
const l = sampleLandRgbaLegacy(continents, mercFlat, tex);
|
||||||
renderVersion >= 2
|
return l || [0, 0, 0, 0];
|
||||||
? sampleLandRgbaMercator(mercFlat, tex)
|
})
|
||||||
: sampleLandRgbaLegacy(continents, mercFlat, tex);
|
);
|
||||||
|
} 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];
|
return l || [0, 0, 0, 0];
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
if (renderVersion >= 2) {
|
|
||||||
drawSphereGrid(ctx, cx, cy, r, rotation, layoutScale);
|
|
||||||
} else {
|
|
||||||
drawHubGrid(ctx, cx, cy, r, rotation, layoutScale);
|
|
||||||
}
|
}
|
||||||
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.save();
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
||||||
@@ -723,7 +806,14 @@ export async function mountHubGlobe(stage, opts) {
|
|||||||
|
|
||||||
raf = requestAnimationFrame(tick);
|
raf = requestAnimationFrame(tick);
|
||||||
stage.dataset.globe3d = String(renderVersion);
|
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 {
|
return {
|
||||||
destroy() {
|
destroy() {
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ function hub_h(string $s): string {
|
|||||||
var supportsLayout = !!(window.CSS && CSS.supports && CSS.supports('object-fit', 'cover'));
|
var supportsLayout = !!(window.CSS && CSS.supports && CSS.supports('object-fit', 'cover'));
|
||||||
var logoEnabled = !!(stage && supportsSvg && supportsLayout);
|
var logoEnabled = !!(stage && supportsSvg && supportsLayout);
|
||||||
|
|
||||||
var logoBuild = '20260704globe2';
|
var logoBuild = '20260704globe5';
|
||||||
|
|
||||||
function hubAsset(rel) {
|
function hubAsset(rel) {
|
||||||
var link = document.querySelector('link[href*="hub.css"]');
|
var link = document.querySelector('link[href*="hub.css"]');
|
||||||
|
|||||||
Reference in New Issue
Block a user