diff --git a/assets/hub-globe.js b/assets/hub-globe.js index 59023a2..a8e9ae9 100644 --- a/assets/hub-globe.js +++ b/assets/hub-globe.js @@ -11,7 +11,7 @@ * globe3d=8: hub SVG ellipse trace (breaks under rotation — deprecated). * globe3d=9: v4 sphere grid only (thin E–W + N–S) — proper rotation baseline. * globe3d=10: v4 visible-arc → screen ellipse (hub ry), disk-clipped. - * globe3d=11: v10 grid + visible-hemisphere continent wrap (ortho hub + mercator flat). + * globe3d=11: v10 grid + mercator-flat land wrap (lon center 12°, inverse Mercator Y). */ export const GLOBE_VIEW = { imgW: 1920, imgH: 1080, cx: 720, cy: 560, r: 322, rim: 330 }; @@ -20,6 +20,8 @@ const DRAG_ROTATION_PER_PX = 0.004; const OCEAN_FALLBACK = { r: 22, g: 38, b: 60 }; const FRONT_Z = 0.04; const LIMB_Z = 0.002; +/** hub-logo-continents ortho / flat map center (extract_red_polygons.py). */ +const MERCATOR_LON_CENTER = (12 * Math.PI) / 180; const RASTER_SCALE = 2; const AXIS_STEPS = 160; const SPHERE_AXIS_STEPS = 180; @@ -877,19 +879,44 @@ function texLonLat(tex) { }; } +function wrapLon(lon) { + let l = lon; + while (l > Math.PI) { + l -= Math.PI * 2; + } + while (l < -Math.PI) { + l += Math.PI * 2; + } + return l; +} + +/** Inverse Mercator row — matches extract_red_polygons.py mercator_y_to_lat. */ +function latToMercatorRow(lat, h) { + const clamped = Math.max(-1.52, Math.min(1.52, lat)); + const t = 0.5 + Math.asinh(Math.tan(clamped)) / (2 * Math.PI); + return Math.max(0, Math.min(h - 1, (1 - t) * (h - 1))); +} + function sampleMercFlat(mercFlat, lon, lat) { - const u = ((lon + Math.PI) / (2 * Math.PI)) * (mercFlat.w - 1); - const v = (0.5 - lat / Math.PI) * (mercFlat.h - 1); + const u = ((wrapLon(lon) + Math.PI) / (2 * Math.PI)) * (mercFlat.w - 1); + const v = latToMercatorRow(lat, mercFlat.h); return sampleImageDataBilinear(mercFlat.data, mercFlat.w, mercFlat.h, u, v); } +/** Sphere tex → mercator-flat plate (lon offset + inverse Mercator Y). */ +function sampleMercFlatFromTex(mercFlat, tex) { + const ll = texLonLat(tex); + const lon = wrapLon(ll.lon + MERCATOR_LON_CENTER); + return sampleMercFlat(mercFlat, lon, ll.lat); +} + function isLandPixel(r, g, b, a) { return a > 10 && r > 100 && g < 140 && b < 140; } /** hub-logo-continents-mercator-{globe,flat}.svg grey-blue fills (not red JPG extract). */ function isHubContinentPixel(r, g, b, a) { - if (a < 12) { + if (a < 8) { return false; } if (r > 168 && g > 198 && b > 218) { @@ -905,6 +932,10 @@ function isHubContinentPixel(r, g, b, a) { return b >= 100 && g >= 95 && r >= 68; } +function hubContinentRgba(sample) { + return [sample[0], sample[1], sample[2], 255]; +} + /** Light hub grid strokes baked into hub-logo-fragment-globe.svg (vector grid drawn separately). */ function isGridPixel(r, g, b, a) { if (a < 12) { @@ -969,19 +1000,14 @@ function sampleLandRgbaMercator(mercFlat, tex) { return null; } -/** globe3d=11 — same visible hemisphere as v10 grid (z > LIMB_Z); ortho hub then mercator wrap. */ -function sampleLandVisibleHemisphere(continents, mercFlat, tex) { +/** globe3d=11 — visible hemisphere (z > LIMB_Z); mercator-flat wrap (single source, no ortho ghost). */ +function sampleLandVisibleHemisphere(mercFlat, tex) { if (tex.z <= LIMB_Z) { return null; } - const ortho = sampleHub(continents, tex); - if (isHubContinentPixel(ortho[0], ortho[1], ortho[2], ortho[3])) { - return [ortho[0], ortho[1], ortho[2], Math.min(255, ortho[3])]; - } - const ll = texLonLat(tex); - const flat = sampleMercFlat(mercFlat, ll.lon, ll.lat); + const flat = sampleMercFlatFromTex(mercFlat, tex); if (isHubContinentPixel(flat[0], flat[1], flat[2], flat[3])) { - return [flat[0], flat[1], flat[2], Math.min(255, flat[3])]; + return hubContinentRgba(flat); } return null; } @@ -1196,7 +1222,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]; }) ); diff --git a/index.php b/index.php index 48befd2..37e30cc 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 = '20260704globe14'; + var logoBuild = '20260704globe15'; function hubAsset(rel) { var link = document.querySelector('link[href*="hub.css"]');