mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
tools(rpo-replay): v3 — review fixes: pair-locked true-size shot, fixed scale per beat, 5 chapters, safe zones, neutral copy
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_0187rSCa4AKJPGw6uiuPSGyX
This commit is contained in:
3 files changed
+26
-9
No files matched your search
@@ -21,7 +21,7 @@ export function createHud(ctx) {
|
|||||||
const CAPTIONS = [
|
const CAPTIONS = [
|
||||||
{
|
{
|
||||||
s0: -1, s1: 4.4, hook: true,
|
s0: -1, s1: 4.4, hook: true,
|
||||||
title: `A Russian military satellite shadowed a U.S.-registered one — to <span class="c-cos">~${CA_M} m</span>`,
|
title: `A Russian military satellite shadowed a U.S.-registered one — to <span class="c-cos" style="white-space:nowrap">~${CA_M} m</span>`,
|
||||||
body: 'How <b class="c-cos">Cosmos 2612</b> matched <b class="c-x36">ICEYE-X36</b>\'s orbit over 22 days, rebuilt from public tracking data.',
|
body: 'How <b class="c-cos">Cosmos 2612</b> matched <b class="c-x36">ICEYE-X36</b>\'s orbit over 22 days, rebuilt from public tracking data.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -207,9 +207,10 @@ export function createHud(ctx) {
|
|||||||
const [r, tt] = st.rel;
|
const [r, tt] = st.rel;
|
||||||
let ctxLine;
|
let ctxLine;
|
||||||
const caS = EVENTS.find((e) => e.kind === 'ca').s;
|
const caS = EVENTS.find((e) => e.kind === 'ca').s;
|
||||||
if (st.sep < 1500) ctxLine = `≈ ${Math.max(1, Math.round(st.sep / 100))} football fields${s > caS && s < 36.5 ? ` · closest ${CA_M} m` : ''}`;
|
if (st.sep < 1500) ctxLine = `≈ ${Math.max(1, Math.round(st.sep / 100))} football fields`;
|
||||||
else if (st.sep < 2e6) ctxLine = `${Math.abs(tt) >= 1e4 ? fmtNum(Math.abs(tt) / 1000) + ' km' : (Math.abs(tt) / 1000).toFixed(1) + ' km'} ${tt < 0 ? 'behind' : 'ahead'}${Math.abs(r) > 20e3 ? ` · ${fmtNum(Math.abs(r) / 1000)} km ${r > 0 ? 'higher' : 'lower'}` : ''}`;
|
else if (st.sep < 2e6) ctxLine = `${Math.abs(tt) >= 1e4 ? fmtNum(Math.abs(tt) / 1000) + ' km' : (Math.abs(tt) / 1000).toFixed(1) + ' km'} ${tt < 0 ? 'behind' : 'ahead'}${Math.abs(r) > 20e3 ? ` · ${fmtNum(Math.abs(r) / 1000)} km ${r > 0 ? 'higher' : 'lower'}` : ''}`;
|
||||||
else ctxLine = `${Math.abs(r) > 20e3 ? `${fmtNum(Math.abs(r) / 1000)} km higher · ` : ''}far side of the orbit`;
|
else ctxLine = `${Math.abs(r) > 20e3 ? `${fmtNum(Math.abs(r) / 1000)} km higher · ` : ''}far side of the orbit`;
|
||||||
|
if (s > caS && s < 36.5) ctxLine += ` · closest ${CA_M} m`;
|
||||||
ro.querySelector('.ctx').textContent = ctxLine;
|
ro.querySelector('.ctx').textContent = ctxLine;
|
||||||
|
|
||||||
// Scale chip: amber when enlarged, white at true size.
|
// Scale chip: amber when enlarged, white at true size.
|
||||||
@@ -234,10 +235,11 @@ export function createHud(ctx) {
|
|||||||
|
|
||||||
// Event toasts (max two, newest on top)
|
// Event toasts (max two, newest on top)
|
||||||
let html = '';
|
let html = '';
|
||||||
if (st.lx < 0.5 && st.sep < 5e3 && s < 41.2) html += '<div class="event shadow"><i></i><div><div class="t">In Earth\'s shadow</div><div class="sub">orbital night · sunrise in minutes</div></div></div>';
|
const quiet = s > 31.85 && s < 33.95; // true-size shot: headline + pulse carry it
|
||||||
|
if (!quiet && st.lx < 0.5 && st.sep < 5e3 && s < 41.2) html += '<div class="event shadow"><i></i><div><div class="t">In Earth\'s shadow</div><div class="sub">orbital night · sunrise in minutes</div></div></div>';
|
||||||
const active = EVENTS.map((e) => ({ e, a: fade(s, e.s, e.s + (e.kind === 'ca' ? 3.6 : e.kind === 'vantor' ? 2.8 : 2.0), 0.2) }))
|
const active = EVENTS.map((e) => ({ e, a: fade(s, e.s, e.s + (e.kind === 'ca' ? 3.6 : e.kind === 'vantor' ? 2.8 : 2.0), 0.2) }))
|
||||||
.filter((x) => x.a > 0).slice(-2).reverse();
|
.filter((x) => x.a > 0).slice(-2).reverse();
|
||||||
for (const { e, a } of active) {
|
for (const { e, a } of quiet ? [] : active) {
|
||||||
html += `<div class="event ${e.kind}" style="opacity:${a * live}"><i></i><div><div class="t">${e.title}</div><div class="sub">${e.sub}</div></div></div>`;
|
html += `<div class="event ${e.kind}" style="opacity:${a * live}"><i></i><div><div class="t">${e.title}</div><div class="sub">${e.sub}</div></div></div>`;
|
||||||
}
|
}
|
||||||
evEl.innerHTML = html;
|
evEl.innerHTML = html;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
#logo, #endlogo { position: absolute; display: flex; align-items: center; gap: calc(14 * var(--u)); }
|
#logo, #endlogo { position: absolute; display: flex; align-items: center; gap: calc(14 * var(--u)); }
|
||||||
#logo { left: var(--pad); top: calc(44 * var(--u)); }
|
#logo { left: var(--pad); top: calc(44 * var(--u)); }
|
||||||
#logo img { height: calc(58 * var(--u)); }
|
#logo img { height: calc(58 * var(--u)); }
|
||||||
#logo span, #endlogo span { font: 800 calc(26 * var(--u))/1 Inter, sans-serif; letter-spacing: 0.16em; }
|
#logo span, #endlogo span { white-space: nowrap; font: 800 calc(26 * var(--u))/1 Inter, sans-serif; letter-spacing: 0.16em; }
|
||||||
|
|
||||||
/* Chapters */
|
/* Chapters */
|
||||||
#chapters { position: absolute; left: calc(520 * var(--u)); right: calc(440 * var(--u)); top: calc(58 * var(--u));
|
#chapters { position: absolute; left: calc(520 * var(--u)); right: calc(440 * var(--u)); top: calc(58 * var(--u));
|
||||||
|
|||||||
@@ -192,8 +192,8 @@ const CAM = [
|
|||||||
{ s: 26.0, dist: 4.6e3, f: 2.4, az: 76, el: 30, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 40, trail: 9 },
|
{ s: 26.0, dist: 4.6e3, f: 2.4, az: 76, el: 30, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 40, trail: 9 },
|
||||||
{ s: 29.7, dist: 2.0e3, f: 2.2, az: 104, el: 24, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 25, trail: 4 },
|
{ s: 29.7, dist: 2.0e3, f: 2.2, az: 104, el: 24, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 25, trail: 4 },
|
||||||
{ s: 31.9, dist: 1.0e3, f: 2.2, az: 116, el: 22, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 25, trail: 3 },
|
{ s: 31.9, dist: 1.0e3, f: 2.2, az: 116, el: 22, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 25, trail: 3 },
|
||||||
{ s: 31.9, cut: true, dist: 25, f: 0, az: 165, el: 8, look: 0.0, earth: 0, fov: 34, mag: 0, trail: 0.5, aimRel: [0, -60, 0] },
|
{ s: 31.9, cut: true, dist: 26, f: 0, az: 160, el: 0, look: 0.0, earth: 0, fov: 34, mag: 0, trail: 0.5, comp: 0, pairD: 26, pairTh: 22 },
|
||||||
{ s: 33.9, dist: 22, f: 0, az: 170, el: 7, look: 0.0, earth: 0, fov: 34, mag: 0, trail: 0.5, aimRel: [0, -70, 0] },
|
{ s: 33.9, dist: 22, f: 0, az: 163, el: 0, look: 0.0, earth: 0, fov: 34, mag: 0, trail: 0.5, comp: 0, pairD: 22, pairTh: 18 },
|
||||||
{ s: 33.9, cut: true, dist: 1.3e3, f: 2.2, az: 96, el: 18, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 25, trail: 1.6 },
|
{ s: 33.9, cut: true, dist: 1.3e3, f: 2.2, az: 96, el: 18, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 25, trail: 1.6 },
|
||||||
{ s: 35.6, dist: 1.6e3, f: 2.4, az: 100, el: 22, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 25, trail: 2 },
|
{ s: 35.6, dist: 1.6e3, f: 2.4, az: 100, el: 22, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 25, trail: 2 },
|
||||||
{ s: 37.0, dist: 1.2e4, f: 2.8, az: 110, el: 30, look: 0.4, earth: 0, fov: 36, mag: 1, mx: 40, trail: 4 },
|
{ s: 37.0, dist: 1.2e4, f: 2.8, az: 110, el: 30, look: 0.4, earth: 0, fov: 36, mag: 1, mx: 40, trail: 4 },
|
||||||
@@ -227,6 +227,9 @@ function camAt(s) {
|
|||||||
earth: lerp(a.earth, b.earth),
|
earth: lerp(a.earth, b.earth),
|
||||||
fov: lerp(a.fov, b.fov),
|
fov: lerp(a.fov, b.fov),
|
||||||
mag: lerp(a.mag, b.mag),
|
mag: lerp(a.mag, b.mag),
|
||||||
|
comp: lerp(a.comp ?? 1, b.comp ?? 1),
|
||||||
|
pairD: lerp(a.pairD || 0, b.pairD || 0),
|
||||||
|
pairTh: lerp(a.pairTh || 0, b.pairTh || 0),
|
||||||
mx: Math.exp(lerp(Math.log(a.mx || 1), Math.log(b.mx || 1))),
|
mx: Math.exp(lerp(Math.log(a.mx || 1), Math.log(b.mx || 1))),
|
||||||
trail: Math.exp(lerp(Math.log(a.trail), Math.log(b.trail))),
|
trail: Math.exp(lerp(Math.log(a.trail), Math.log(b.trail))),
|
||||||
aimRel: [0, 1, 2].map((k) => lerp(ar[k], br[k])),
|
aimRel: [0, 1, 2].map((k) => lerp(ar[k], br[k])),
|
||||||
@@ -453,6 +456,18 @@ async function frame(s) {
|
|||||||
v3.normalize(v3.add(v3.multiplyByScalar(off, 1 - cam.sun, off), v3.multiplyByScalar(sunW, cam.sun, new v3()), off), off);
|
v3.normalize(v3.add(v3.multiplyByScalar(off, 1 - cam.sun, off), v3.multiplyByScalar(sunW, cam.sun, new v3()), off), off);
|
||||||
}
|
}
|
||||||
const camPos = v3.add(pivot, v3.multiplyByScalar(off, dist, new v3()), new v3());
|
const camPos = v3.add(pivot, v3.multiplyByScalar(off, dist, new v3()), new v3());
|
||||||
|
if (cam.pairD > 0) {
|
||||||
|
// Pair-locked: camera just off X36, angled pairTh from the X36->Cosmos line
|
||||||
|
// (sideways, in the orbit's horizontal), aimed at the bisector of the two.
|
||||||
|
const u = v3.normalize(v3.subtract(cosW, x36W, new v3()), new v3());
|
||||||
|
const perp = v3.normalize(v3.subtract(NW, v3.multiplyByScalar(u, v3.dot(NW, u), new v3()), new v3()), new v3());
|
||||||
|
const th = Cesium.Math.toRadians(cam.pairTh);
|
||||||
|
v3.add(x36W, v3.multiplyByScalar(u, -cam.pairD * Math.cos(th), new v3()), camPos);
|
||||||
|
v3.add(camPos, v3.multiplyByScalar(perp, cam.pairD * Math.sin(th), new v3()), camPos);
|
||||||
|
const a1 = v3.normalize(v3.subtract(x36W, camPos, new v3()), new v3());
|
||||||
|
const a2 = v3.normalize(v3.subtract(cosW, camPos, new v3()), new v3());
|
||||||
|
v3.add(camPos, v3.add(a1, a2, new v3()), aim);
|
||||||
|
}
|
||||||
const minR = 6.371e6 + 1.2e5;
|
const minR = 6.371e6 + 1.2e5;
|
||||||
if (v3.magnitude(camPos) < minR) v3.multiplyByScalar(v3.normalize(camPos, camPos), minR, camPos);
|
if (v3.magnitude(camPos) < minR) v3.multiplyByScalar(v3.normalize(camPos, camPos), minR, camPos);
|
||||||
const dir = v3.normalize(v3.subtract(aim, camPos, new v3()), new v3());
|
const dir = v3.normalize(v3.subtract(aim, camPos, new v3()), new v3());
|
||||||
@@ -465,8 +480,8 @@ async function frame(s) {
|
|||||||
const aspect = W / H;
|
const aspect = W / H;
|
||||||
const tanH = PORTRAIT ? Math.tan(fovR / 2) * aspect : Math.tan(fovR / 2);
|
const tanH = PORTRAIT ? Math.tan(fovR / 2) * aspect : Math.tan(fovR / 2);
|
||||||
const tanV = PORTRAIT ? Math.tan(fovR / 2) : Math.tan(fovR / 2) / aspect;
|
const tanV = PORTRAIT ? Math.tan(fovR / 2) : Math.tan(fovR / 2) / aspect;
|
||||||
const ndcX = PORTRAIT ? 0 : 0.2;
|
const ndcX = (PORTRAIT ? 0 : 0.2) * cam.comp;
|
||||||
const ndcY = PORTRAIT ? -0.02 : -0.06;
|
const ndcY = (PORTRAIT ? -0.02 : -0.06) * cam.comp;
|
||||||
v3.normalize(v3.add(v3.add(dir, v3.multiplyByScalar(right, -ndcX * tanH, new v3()), new v3()),
|
v3.normalize(v3.add(v3.add(dir, v3.multiplyByScalar(right, -ndcX * tanH, new v3()), new v3()),
|
||||||
v3.multiplyByScalar(up, -ndcY * tanV, new v3()), new v3()), dir);
|
v3.multiplyByScalar(up, -ndcY * tanV, new v3()), new v3()), dir);
|
||||||
right = v3.normalize(v3.cross(dir, RW, new v3()), new v3());
|
right = v3.normalize(v3.cross(dir, RW, new v3()), new v3());
|
||||||
|
|||||||
Reference in new issue
Block a user