Add five minute panoramic sky rotation
This commit is contained in:
parent
3416cac155
commit
02697dae1f
2 changed files with 79 additions and 10 deletions
|
|
@ -1254,9 +1254,11 @@ if (typeof CONSTELLATIONS !== 'undefined') {
|
|||
constellationArtImages.set(constellation.id, entry);
|
||||
}
|
||||
}
|
||||
const VIEW_AZ = 90; // centro da tela olha para o LESTE — a tela física do
|
||||
// usuário está voltada p/ leste, então o wallpaper age
|
||||
// como janela: estrelas nascem subindo dentro da cena
|
||||
const CAMERA_HOME_AZ = 90; // posição inicial: leste
|
||||
const CAMERA_TURN_MS = 5 * 60 * 1000;
|
||||
let viewAz = CAMERA_HOME_AZ;
|
||||
let cameraRotating = false;
|
||||
let cameraRotationStartMs = Date.now();
|
||||
const FOV_AZ = 140; // largura da tela em graus de azimute
|
||||
const ALT_TOP = 75; // altitude no topo da tela
|
||||
const HORIZON_F = 0.585; // fração da altura da tela até o horizonte
|
||||
|
|
@ -1267,6 +1269,23 @@ let skyTimeScale = 1;
|
|||
let skyClockRealMs = Date.now();
|
||||
let skyClockSimMs = skyClockRealMs;
|
||||
|
||||
function currentCameraAz() {
|
||||
if (!cameraRotating) return CAMERA_HOME_AZ;
|
||||
const phase = ((Date.now() - cameraRotationStartMs) % CAMERA_TURN_MS) /
|
||||
CAMERA_TURN_MS;
|
||||
return (CAMERA_HOME_AZ + phase * 360) % 360;
|
||||
}
|
||||
|
||||
function setCameraRotating(on) {
|
||||
on = !!on;
|
||||
if (on === cameraRotating) return;
|
||||
cameraRotating = on;
|
||||
cameraRotationStartMs = Date.now();
|
||||
viewAz = CAMERA_HOME_AZ;
|
||||
lastProjMs = -Infinity;
|
||||
projectSky();
|
||||
}
|
||||
|
||||
function skyNowMs() {
|
||||
const realNow = Date.now();
|
||||
return skyClockSimMs + (realNow - skyClockRealMs) * skyTimeScale +
|
||||
|
|
@ -1310,7 +1329,7 @@ function altAz(raDeg, decDeg, lstDeg) {
|
|||
|
||||
/* Projeção cilíndrica: azimute -> x, altitude -> y. null = fora da tela */
|
||||
function skyToScreen(alt, az, W, horizonPx) {
|
||||
const daz = ((az - VIEW_AZ) % 360 + 540) % 360 - 180;
|
||||
const daz = ((az - viewAz) % 360 + 540) % 360 - 180;
|
||||
if (Math.abs(daz) > FOV_AZ / 2 || alt < -3 || alt > ALT_TOP) return null;
|
||||
return { x: (0.5 + daz / FOV_AZ) * W, y: horizonPx * (1 - alt / ALT_TOP) };
|
||||
}
|
||||
|
|
@ -1380,7 +1399,7 @@ function buildMwGrid() {
|
|||
const ca = Math.cos(alt * DEG), sa = Math.sin(alt * DEG);
|
||||
for (let gx = 0; gx < gw; gx++, i++) {
|
||||
const sx = (gx + 0.5) * MW_SAMPLE_SCALE;
|
||||
const az = (VIEW_AZ + (sx / W - 0.5) * FOV_AZ) * DEG;
|
||||
const az = (CAMERA_HOME_AZ + (sx / W - 0.5) * FOV_AZ) * DEG;
|
||||
const north = ca * Math.cos(az), east = ca * Math.sin(az);
|
||||
wx[i] = -sl * north + cl * sa;
|
||||
wy[i] = -east;
|
||||
|
|
@ -1408,13 +1427,36 @@ function renderMilkyWay(lstDeg, glareOn) {
|
|||
const m20 = E2G[2][0] * cL + E2G[2][1] * sL;
|
||||
const m21 = E2G[2][0] * sL - E2G[2][1] * cL;
|
||||
const m22 = E2G[2][2];
|
||||
/* Gira o viewport em torno do zênite sem reconstruir a grade de milhões
|
||||
de pixels. A matriz horizontal é incorporada à equatorial-galáctica. */
|
||||
const cameraDelta = (viewAz - CAMERA_HOME_AZ) * DEG;
|
||||
const cr = Math.cos(cameraDelta), sr = Math.sin(cameraDelta);
|
||||
const clat = Math.cos(obsLat * DEG), slat = Math.sin(obsLat * DEG);
|
||||
const r00 = cr * slat * slat + clat * clat;
|
||||
const r01 = -slat * sr;
|
||||
const r02 = clat * slat * (1 - cr);
|
||||
const r10 = slat * sr;
|
||||
const r11 = cr;
|
||||
const r12 = -clat * sr;
|
||||
const r20 = r02;
|
||||
const r21 = clat * sr;
|
||||
const r22 = cr * clat * clat + slat * slat;
|
||||
const n00 = m00 * r00 + m01 * r10 + m02 * r20;
|
||||
const n01 = m00 * r01 + m01 * r11 + m02 * r21;
|
||||
const n02 = m00 * r02 + m01 * r12 + m02 * r22;
|
||||
const n10 = m10 * r00 + m11 * r10 + m12 * r20;
|
||||
const n11 = m10 * r01 + m11 * r11 + m12 * r21;
|
||||
const n12 = m10 * r02 + m11 * r12 + m12 * r22;
|
||||
const n20 = m20 * r00 + m21 * r10 + m22 * r20;
|
||||
const n21 = m20 * r01 + m21 * r11 + m22 * r21;
|
||||
const n22 = m20 * r02 + m21 * r12 + m22 * r22;
|
||||
const tw = mwTex.w, th = mwTex.h, tex = mwTex.data, px = img.data;
|
||||
const inv2pi = 1 / (2 * Math.PI), invpi = 1 / Math.PI;
|
||||
|
||||
for (let i = 0, j = 0; i < wx.length; i++, j += 4) {
|
||||
const gx = m00 * wx[i] + m01 * wy[i] + m02 * wz[i];
|
||||
const gy = m10 * wx[i] + m11 * wy[i] + m12 * wz[i];
|
||||
const gz = m20 * wx[i] + m21 * wy[i] + m22 * wz[i];
|
||||
const gx = n00 * wx[i] + n01 * wy[i] + n02 * wz[i];
|
||||
const gy = n10 * wx[i] + n11 * wy[i] + n12 * wz[i];
|
||||
const gz = n20 * wx[i] + n21 * wy[i] + n22 * wz[i];
|
||||
const l = Math.atan2(gy, gx);
|
||||
const b = Math.asin(Math.max(-1, Math.min(1, gz)));
|
||||
const absB = Math.abs(b / DEG);
|
||||
|
|
@ -1512,7 +1554,7 @@ function drawConstellationArt(c, constellation, lst, W, horizonPx, previewProjec
|
|||
return { x: sc.x, y: sc.y, alt: ALT_TOP / 2 };
|
||||
}
|
||||
const aa = altAz(anchor.sky[0], anchor.sky[1], lst);
|
||||
const daz = ((aa.az - VIEW_AZ) % 360 + 540) % 360 - 180;
|
||||
const daz = ((aa.az - viewAz) % 360 + 540) % 360 - 180;
|
||||
return {
|
||||
x: (0.5 + daz / FOV_AZ) * W,
|
||||
y: horizonPx * (1 - aa.alt / ALT_TOP),
|
||||
|
|
@ -1738,6 +1780,7 @@ function projectSky() {
|
|||
}
|
||||
const bctx = renderCanvas.getContext('2d');
|
||||
bctx.clearRect(0, 0, W, bufferH);
|
||||
viewAz = currentCameraAz();
|
||||
const lst = localSiderealDeg(skyNowMs());
|
||||
const glareOn = !document.body.classList.contains('balada') &&
|
||||
!document.body.classList.contains('chuva');
|
||||
|
|
@ -1824,7 +1867,7 @@ function resizeCanvas() {
|
|||
}
|
||||
|
||||
function drawStars(ts) {
|
||||
const projectionInterval = skyTimeScale > 1 ? 4000 : 30000;
|
||||
const projectionInterval = (skyTimeScale > 1 || cameraRotating) ? 4000 : 30000;
|
||||
if (ts - lastProjMs > projectionInterval) { lastProjMs = ts; projectSky(); }
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
const transition = Math.max(0, Math.min(1,
|
||||
|
|
@ -1871,6 +1914,21 @@ let skyTimeScaleFileVal = null;
|
|||
.finally(() => setTimeout(pollSkyTimeScaleFile, 800));
|
||||
})();
|
||||
|
||||
/* Giro panorâmico controlado por toggle-camera-rotation.sh. */
|
||||
let cameraRotationFileVal = null;
|
||||
(function pollCameraRotationFile() {
|
||||
fetch('file:///tmp/skeledance-camera-rotation?t=' + Date.now())
|
||||
.then(r => r.text())
|
||||
.then(t => {
|
||||
const v = t.trim();
|
||||
if (v === cameraRotationFileVal) return;
|
||||
cameraRotationFileVal = v;
|
||||
setCameraRotating(v === '1');
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => setTimeout(pollCameraRotationFile, 800));
|
||||
})();
|
||||
|
||||
/* ============================================================
|
||||
FLOOR CANVAS — perspective grid with true vanishing point
|
||||
============================================================ */
|
||||
|
|
|
|||
11
toggle-camera-rotation.sh
Executable file
11
toggle-camera-rotation.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
STATE_FILE=/tmp/skeledance-camera-rotation
|
||||
|
||||
if [ "$(cat "$STATE_FILE" 2>/dev/null)" = "1" ]; then
|
||||
printf '0\n' > "$STATE_FILE"
|
||||
printf 'Câmera fixa olhando para leste\n'
|
||||
else
|
||||
printf '1\n' > "$STATE_FILE"
|
||||
printf 'Rotação panorâmica ativa: 360° em 5 minutos\n'
|
||||
fi
|
||||
Loading…
Add table
Add a link
Reference in a new issue