mirror of
git://f0xx.org/android_cast
synced 2026-07-29 05:37:52 +03:00
sync landing
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user