1
0
mirror of git://f0xx.org/ac/ac-be-hub synced 2026-08-09 20:59:23 +03:00

fix(globe3d=11): geographic mercator land with full lon wrap

Ortho hub SVG only covers one fitted hemisphere — left empty on rotate.
Sample mercator-flat by lon/lat with U-wrap and inner_sy lat compress.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Anton Afanasyeu
2026-08-05 18:42:13 +02:00
parent c0c99b7cce
commit 3d45bde853
2 changed files with 14 additions and 16 deletions

View File

@@ -11,7 +11,7 @@
* globe3d=8: hub SVG ellipse trace (breaks under rotation — deprecated). * globe3d=8: hub SVG ellipse trace (breaks under rotation — deprecated).
* globe3d=9: v4 sphere grid only (thin EW + NS) — proper rotation baseline. * globe3d=9: v4 sphere grid only (thin EW + NS) — proper rotation baseline.
* globe3d=10: v4 visible-arc → screen ellipse (hub ry), disk-clipped. * globe3d=10: v4 visible-arc → screen ellipse (hub ry), disk-clipped.
* globe3d=11: v10 grid + ortho hub land + affine flat-plate wrap fallback. * globe3d=11: v10 grid + geographic mercator-flat land (360° U-wrap).
*/ */
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 };
@@ -27,6 +27,8 @@ const CONTINENT_INNER_TX = 223.66088631984587;
const CONTINENT_INNER_TY = 238.0; const CONTINENT_INNER_TY = 238.0;
const CONTINENT_INNER_SX = 1.2408477842003853; const CONTINENT_INNER_SX = 1.2408477842003853;
const CONTINENT_INNER_SY = 1.2408477842003853; const CONTINENT_INNER_SY = 1.2408477842003853;
/** Map plate center longitude (extract_red_polygons.py CENTER_LON). */
const MERCATOR_LON_CENTER = (12 * Math.PI) / 180;
const RASTER_SCALE = 2; const RASTER_SCALE = 2;
const AXIS_STEPS = 160; const AXIS_STEPS = 160;
const SPHERE_AXIS_STEPS = 180; const SPHERE_AXIS_STEPS = 180;
@@ -948,11 +950,13 @@ function sampleFlatPlateFromHub(mercFlat, hpX, hpY) {
return sampleImageDataBilinearWrapU(mercFlat.data, mercFlat.w, mercFlat.h, sx, sy); return sampleImageDataBilinearWrapU(mercFlat.data, mercFlat.w, mercFlat.h, sx, sy);
} }
/** Sphere tex → mercator-flat plate (lon offset + inverse Mercator Y). */ /** Sphere tex → mercator-flat plate (lon center + inverse Mercator Y). */
function sampleMercFlatFromTex(mercFlat, tex) { function sampleMercFlatFromTex(mercFlat, tex) {
const ll = texLonLat(tex); const ll = texLonLat(tex);
const lon = wrapLon(ll.lon + (12 * Math.PI) / 180); const lon = wrapLon(ll.lon + MERCATOR_LON_CENTER);
return sampleMercFlat(mercFlat, lon, ll.lat); const lat =
Math.max(-1.52, Math.min(1.52, ll.lat / CONTINENT_INNER_SY));
return sampleMercFlat(mercFlat, lon, lat);
} }
function isLandPixel(r, g, b, a) { function isLandPixel(r, g, b, a) {
@@ -1063,19 +1067,13 @@ function sampleLandRgbaMercator(mercFlat, tex) {
return null; return null;
} }
/** globe3d=11 — ortho hub (reference NS) + wrapped flat-plate fallback. */ /** globe3d=11 — geographic mercator wrap (360°); z > LIMB_Z only. */
function sampleLandVisibleHemisphere(continents, mercFlat, tex) { function sampleLandVisibleHemisphere(mercFlat, tex) {
if (tex.z <= LIMB_Z) { if (tex.z <= LIMB_Z) {
return null; return null;
} }
const ortho = sampleHub(continents, tex); const flat = sampleMercFlatFromTex(mercFlat, tex);
if (isSolidContinentPixel(ortho[0], ortho[1], ortho[2], ortho[3])) { if (isHubContinentPixel(flat[0], flat[1], flat[2], flat[3])) {
return hubContinentRgba(ortho);
}
const hpX = GLOBE_VIEW.cx + tex.x * GLOBE_VIEW.r;
const hpY = GLOBE_VIEW.cy - tex.y * GLOBE_VIEW.r;
const flat = sampleFlatPlateFromHub(mercFlat, hpX, hpY);
if (flat && isHubContinentPixel(flat[0], flat[1], flat[2], flat[3])) {
return hubContinentRgba(flat); return hubContinentRgba(flat);
} }
return null; return null;
@@ -1291,7 +1289,7 @@ 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) {
const l = sampleLandVisibleHemisphere(continents, mercFlat, tex); const l = sampleLandVisibleHemisphere(mercFlat, tex);
return l || [0, 0, 0, 0]; return l || [0, 0, 0, 0];
}) })
); );

View File

@@ -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 = '20260704globe16'; var logoBuild = '20260704globe17';
function hubAsset(rel) { function hubAsset(rel) {
var link = document.querySelector('link[href*="hub.css"]'); var link = document.querySelector('link[href*="hub.css"]');