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=9: v4 sphere grid only (thin EW + NS) — proper rotation baseline.
* 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 };
@@ -27,6 +27,8 @@ const CONTINENT_INNER_TX = 223.66088631984587;
const CONTINENT_INNER_TY = 238.0;
const CONTINENT_INNER_SX = 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 AXIS_STEPS = 160;
const SPHERE_AXIS_STEPS = 180;
@@ -948,11 +950,13 @@ function sampleFlatPlateFromHub(mercFlat, hpX, hpY) {
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) {
const ll = texLonLat(tex);
const lon = wrapLon(ll.lon + (12 * Math.PI) / 180);
return sampleMercFlat(mercFlat, lon, ll.lat);
const lon = wrapLon(ll.lon + MERCATOR_LON_CENTER);
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) {
@@ -1063,19 +1067,13 @@ function sampleLandRgbaMercator(mercFlat, tex) {
return null;
}
/** globe3d=11 — ortho hub (reference NS) + wrapped flat-plate fallback. */
function sampleLandVisibleHemisphere(continents, mercFlat, tex) {
/** globe3d=11 — geographic mercator wrap (360°); z > LIMB_Z only. */
function sampleLandVisibleHemisphere(mercFlat, tex) {
if (tex.z <= LIMB_Z) {
return null;
}
const ortho = sampleHub(continents, tex);
if (isSolidContinentPixel(ortho[0], ortho[1], ortho[2], ortho[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])) {
const flat = sampleMercFlatFromTex(mercFlat, tex);
if (isHubContinentPixel(flat[0], flat[1], flat[2], flat[3])) {
return hubContinentRgba(flat);
}
return null;
@@ -1291,7 +1289,7 @@ export async function mountHubGlobe(stage, opts) {
cy,
r,
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];
})
);

View File

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