1
0
mirror of git://f0xx.org/android_cast synced 2026-07-29 05:17:39 +03:00

sync landing

This commit is contained in:
Anton Afanasyeu
2026-06-06 17:31:42 +02:00
parent bcec787bf3
commit 5eaad41405
3 changed files with 57 additions and 82 deletions

View File

@@ -13,7 +13,7 @@ Review one layer: `?logoPart=space|globe|full`
Compare Earth grids: `?logoPart=globe&grid=3` (default) or `?logoPart=full&grid=4`
**3D globe background** (default): Canvas2D orthographic sphere — ocean from `hub-logo-fragment-globe.svg`, continents from `hub-logo-continents-mercator-flat.svg` (front hemisphere only), Mercator grid projected in 3D with depth-weighted lines. Slow Y-axis spin (~0.035 rad/s); drag horizontally to rotate. Disable: `?globe3d=0` (static SVG stack). Yaw override: `?globeYawDeg=24`. Deploy `assets/hub-globe.js` with `index.php` (see `DEPLOY.md`).
**3D globe background** (default): Canvas2D orthographic sphere — same assets as `?globe3d=0`: ocean from `hub-logo-fragment-globe.svg`, continents from `hub-logo-continents-mercator-globe.svg` (body-fixed disk, front face only). Mercator grid is 3D-projected with depth-weighted lines. Disable 3D: `?globe3d=0`. Yaw: `?globeYawDeg=90`.
**BE only.** FE `apps.f0xx.org` sends `location /``http://artc0.intra.raptor.org:80` (not :8089).

View File

@@ -1,7 +1,8 @@
/**
* Hub landing globe — orthographic Canvas2D sphere.
* Ocean shading from hub-logo-fragment-globe.svg; continents from mercator-flat.svg.
* Y-axis spin with front-hemisphere-only land; grid lines are 3D-projected with depth weighting.
* Matches globe3d=0 reference: ocean from hub-logo-fragment-globe.svg,
* continents from hub-logo-continents-mercator-globe.svg (body-fixed orthographic disk).
* Y-axis spin; land only on front body face (z > 0); grid projected in 3D with depth weighting.
*/
export const GLOBE_VIEW = { imgW: 1920, imgH: 1080, cx: 720, cy: 560, r: 322, rim: 330 };
@@ -63,36 +64,10 @@ function sampleImageData(imgData, w, h, x, y) {
const ix = Math.max(0, Math.min(w - 1, x | 0));
const iy = Math.max(0, Math.min(h - 1, y | 0));
const i = (iy * w + ix) * 4;
return [
imgData[i],
imgData[i + 1],
imgData[i + 2],
imgData[i + 3],
];
}
function isLandPixel(r, g, b, a) {
return a > 8 && r > 120 && g < 130 && b < 130;
}
function sampleMercator(merc, lon, lat) {
const u = (lon + Math.PI) / (2 * Math.PI);
const v = 0.5 - lat / Math.PI;
const x = Math.round(u * (merc.w - 1));
const y = Math.round(v * (merc.h - 1));
return sampleImageData(merc.data, merc.w, merc.h, x, y);
}
function sampleGlobeOcean(globe, xw, yw) {
const sx = Math.round(GLOBE_VIEW.cx + xw * GLOBE_VIEW.r);
const sy = Math.round(GLOBE_VIEW.cy - yw * GLOBE_VIEW.r);
const rgba = sampleImageData(globe.data, globe.w, globe.h, sx, sy);
if (rgba[3] < 8) {
return [OCEAN_FALLBACK.r, OCEAN_FALLBACK.g, OCEAN_FALLBACK.b, 255];
}
return rgba;
return [imgData[i], imgData[i + 1], imgData[i + 2], imgData[i + 3]];
}
/** View unit sphere (nx,ny,nz) → body-fixed unit sphere before Y spin. */
function inverseRotateY(nx, ny, nz, rotY) {
const cosR = Math.cos(rotY);
const sinR = Math.sin(rotY);
@@ -111,10 +86,38 @@ function rotateY(x, y, z, rotY) {
};
}
/** Orthographic hub disk coords for body point on unit sphere (same as static SVG stack). */
function bodyToHubPixel(xb, yb) {
return {
x: Math.round(GLOBE_VIEW.cx + xb * GLOBE_VIEW.r),
y: Math.round(GLOBE_VIEW.cy - yb * GLOBE_VIEW.r),
};
}
/**
* Orthographic disk → world lon/lat → mercator land + globe ocean (front face only).
* Sample globe3d=0 continent + ocean stack in body-fixed frame.
* Continents layer is authoritative where opaque; else globe ocean shading.
*/
function drawGlobeDisk(ctx, cx, cy, r, rotY, merc, globe) {
function sampleBodySurface(continents, globe, xb, yb, zb) {
if (zb <= FRONT_Z) {
return [OCEAN_FALLBACK.r, OCEAN_FALLBACK.g, OCEAN_FALLBACK.b];
}
const p = bodyToHubPixel(xb, yb);
const land = sampleImageData(continents.data, continents.w, continents.h, p.x, p.y);
if (land[3] > 24) {
return [land[0], land[1], land[2]];
}
const ocean = sampleImageData(globe.data, globe.w, globe.h, p.x, p.y);
if (ocean[3] < 8) {
return [OCEAN_FALLBACK.r, OCEAN_FALLBACK.g, OCEAN_FALLBACK.b];
}
return [ocean[0], ocean[1], ocean[2]];
}
/**
* Visible orthographic disk → inverse Y-rotation → body-fixed texture (globe3d=0 reference).
*/
function drawGlobeDisk(ctx, cx, cy, r, rotY, continents, globe) {
const r2 = r * r;
const x0 = Math.max(0, Math.floor(cx - r));
const y0 = Math.max(0, Math.floor(cy - r));
@@ -143,34 +146,12 @@ function drawGlobeDisk(ctx, cx, cy, r, rotY, merc, globe) {
const nx = ox / r;
const ny = oy / r;
const nz = Math.sqrt(Math.max(0, 1 - nx * nx - ny * ny));
const wpos = inverseRotateY(nx, ny, nz, rotY);
const lon = Math.atan2(wpos.x, wpos.z);
const lat = Math.asin(Math.max(-1, Math.min(1, wpos.y)));
const body = inverseRotateY(nx, ny, nz, rotY);
const rgb = sampleBodySurface(continents, globe, body.x, body.y, body.z);
let r8;
let g8;
let b8;
if (wpos.z > FRONT_Z) {
const land = sampleMercator(merc, lon, lat);
if (isLandPixel(land[0], land[1], land[2], land[3])) {
r8 = land[0];
g8 = land[1];
b8 = land[2];
} else {
const ocean = sampleGlobeOcean(globe, wpos.x, wpos.y);
r8 = ocean[0];
g8 = ocean[1];
b8 = ocean[2];
}
} else {
r8 = OCEAN_FALLBACK.r;
g8 = OCEAN_FALLBACK.g;
b8 = OCEAN_FALLBACK.b;
}
px[p++] = r8;
px[p++] = g8;
px[p++] = b8;
px[p++] = rgb[0];
px[p++] = rgb[1];
px[p++] = rgb[2];
px[p++] = 255;
}
}
@@ -260,7 +241,6 @@ function drawGridOverlay(ctx, rect, rotY) {
const latRad = latFrac * Math.PI;
const cosLat = Math.cos(latRad);
const sinLat = Math.sin(latRad);
const styleFn = depthStyle(lwThin, 0.55, 0.2);
strokeCurve3D(
ctx,
cx,
@@ -272,8 +252,10 @@ function drawGridOverlay(ctx, rect, rotY) {
return { x: cosLat * Math.sin(lon), y: sinLat, z: cosLat * Math.cos(lon) };
},
function (z) {
const s = styleFn(z);
return { width: s.width, color: 'rgba(231,236,243,' + (0.2 + 0.52 * Math.max(0, (z - FRONT_Z) / (1 - FRONT_Z))).toFixed(3) + ')' };
return {
width: lwThin * (0.35 + 0.65 * Math.max(0, (z - FRONT_Z) / (1 - FRONT_Z))),
color: 'rgba(231,236,243,' + (0.2 + 0.52 * Math.max(0, (z - FRONT_Z) / (1 - FRONT_Z))).toFixed(3) + ')',
};
}
);
});
@@ -294,8 +276,7 @@ function drawGridOverlay(ctx, rect, rotY) {
return { x: cosLat * Math.sin(lonRad), y: Math.sin(lat), z: cosLat * Math.cos(lonRad) };
},
function (z) {
const s = styleFn(z);
return s;
return styleFn(z);
}
);
}
@@ -341,15 +322,15 @@ export async function mountHubGlobe(stage, opts) {
const urls = {
globe: hubAsset('assets/hub-logo-fragment-globe.svg', build),
mercatorFlat: hubAsset('assets/hub-logo-continents-mercator-flat.svg', build),
continents: hubAsset('assets/hub-logo-continents-mercator-globe.svg', build),
};
let globeImg;
let mercatorFlatImg;
let continentsImg;
try {
[globeImg, mercatorFlatImg] = await Promise.all([
[globeImg, continentsImg] = await Promise.all([
loadImage(urls.globe),
loadImage(urls.mercatorFlat),
loadImage(urls.continents),
]);
} catch (e) {
console.warn('HubGlobe: asset load failed', e);
@@ -362,10 +343,10 @@ export async function mountHubGlobe(stage, opts) {
h: GLOBE_VIEW.imgH,
data: rasterizeImage(globeImg, GLOBE_VIEW.imgW, GLOBE_VIEW.imgH).data,
};
const merc = {
w: mercatorFlatImg.naturalWidth || 800,
h: mercatorFlatImg.naturalHeight || 519,
data: rasterizeImage(mercatorFlatImg, mercatorFlatImg.naturalWidth || 800, mercatorFlatImg.naturalHeight || 519).data,
const continents = {
w: GLOBE_VIEW.imgW,
h: GLOBE_VIEW.imgH,
data: rasterizeImage(continentsImg, GLOBE_VIEW.imgW, GLOBE_VIEW.imgH).data,
};
let rotation = typeof opts.initialRotationY === 'number' ? opts.initialRotationY : 0;
@@ -402,16 +383,10 @@ export async function mountHubGlobe(stage, opts) {
const cy = (rect.cy - rect.top) * dpr;
const r = rect.r * dpr;
const rim = rect.rim * dpr;
const drawRect = {
cx,
cy,
r,
rim,
scale: rect.scale * dpr,
};
const drawRect = { cx, cy, r, rim, scale: rect.scale * dpr };
ctx.clearRect(0, 0, px, px);
drawGlobeDisk(ctx, cx, cy, r, rotation, merc, globe);
drawGlobeDisk(ctx, cx, cy, r, rotation, continents, globe);
drawRim(ctx, globeImg, cx, cy, r, rim, rect.scale * dpr);
drawGridOverlay(ctx, drawRect, rotation);
ctx.save();

View File

@@ -180,7 +180,7 @@ unset($__platformDir);
var supportsLayout = !!(window.CSS && CSS.supports && CSS.supports('object-fit', 'cover'));
var logoEnabled = !!(stage && supportsSvg && supportsLayout);
var logoBuild = '20260606globe';
var logoBuild = '20260606globe2';
function hubAsset(rel) {
var link = document.querySelector('link[href*="hub.css"]');