mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-08 12:23:59 +00:00
tools(rpo-replay): v6 — round-2 critic fixes (attributed quote, Δv wording, bigger inset, label plates and keep-outs, CA framing, pacing)
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_0187rSCa4AKJPGw6uiuPSGyX
This commit is contained in:
5 files changed
+104
-85
No files matched your search
+19
-16
@@ -29,12 +29,12 @@ export function createHud(ctx) {
|
||||
];
|
||||
const cap = (id, title, body, extra = {}) => ({ s0: span(id)[0], s1: span(id)[1], title, body, ...extra });
|
||||
const CAPTIONS = [
|
||||
cap('hook', 'A Russian military satellite came within about half a kilometre of a U.S. radar satellite',
|
||||
cap('hook', `A Russian military satellite came within <span class="c-cos nw">~${CA_M} m</span> of a U.S. one`,
|
||||
'Rebuilt from public tracking data.', { hook: true, s0: -1 }),
|
||||
cap('climb', 'Climb higher to fall behind',
|
||||
'Two burns lift <b class="c-cos">Cosmos 2612</b> <b>47 km</b>. A higher orbit is a longer lap, so it falls behind.'),
|
||||
cap('lap', 'Lapped, twice',
|
||||
'Losing <b>~6,700 km a day</b>, it is lapped by <b class="c-x36">X36</b> on Oct 5 and Oct 12, while Earth\'s bulge swings the two orbits into line.'),
|
||||
'Losing <b>~6,700 km a day</b>, it gets lapped by <b class="c-x36">X36</b> on Oct 5 and Oct 12. Earth\'s bulge, plus a 4 m/s nudge, swing the orbits into line.'),
|
||||
cap('drop', 'Drop into the same orbit',
|
||||
'Oct 12: from <b>54 km</b> directly overhead it dives into X36\'s orbit, landing <b>~500 km behind</b>.'),
|
||||
cap('close', 'Speed up, then brake',
|
||||
@@ -46,7 +46,7 @@ export function createHud(ctx) {
|
||||
cap('approach', 'Closing in, slowly',
|
||||
'Creeping in at <b>a few centimetres a second</b>, while both fly at <b>27,000 km/h</b>.'),
|
||||
cap('ca', `Closest pass: <span class="c-cos nw">~${CA_M} m</span>`,
|
||||
'In Earth\'s shadow, ~01:17 UTC. LeoLabs radar: <b>~500 m</b>. Public estimates: <b>150–650 m</b>.'),
|
||||
'In Earth\'s shadow, drifting slower than a tortoise. LeoLabs radar: <b>~500 m</b>. Estimates: <b>150–650 m</b>.'),
|
||||
cap('sunrise', 'Then the sun comes up',
|
||||
'Twenty minutes later both leave Earth\'s shadow. Within the hour, Cosmos backs away.'),
|
||||
cap('leave', 'Back off, and wait',
|
||||
@@ -55,7 +55,7 @@ export function createHud(ctx) {
|
||||
const BURN_NOTES = {
|
||||
'Phasing raise': 'two burns raise its orbit 46 km',
|
||||
'Plane trim': 'tilts its orbit into line',
|
||||
'Descent to X36 orbit': 'drops 48 km into X36\'s orbit',
|
||||
'Descent to X36 orbit': 'drops 47 km into X36\'s orbit',
|
||||
'Closing burn': 'dips 2.3 km to speed up',
|
||||
'Braking burn': 'rises 2.5 km to stop',
|
||||
'Back-off': 'rises 140 m and starts to drift back',
|
||||
@@ -224,7 +224,7 @@ export function createHud(ctx) {
|
||||
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 · ` : ''}${fmtNum(st.sep / 1000)} km apart in a straight line`;
|
||||
if (s > caS && s < span('leave')[0] + 0.5) ctxLine += ` · closest ${CA_M} m`;
|
||||
ro.querySelector('.ctx').textContent = lapMode ? `${Math.abs(r) > 20e3 ? `${fmtNum(Math.abs(r) / 1000)} km higher · ` : ''}behind X36` : ctxLine;
|
||||
ro.querySelector('.ctx').textContent = lapMode ? `${st.da > 20e3 ? `${Math.round(st.da / 1000)} km higher on average · ` : ''}behind X36` : ctxLine;
|
||||
const relLine = ro.querySelector('.rel');
|
||||
relLine.textContent = st.sep < 2e4 && sh.mode === 'true' ? (st.relSpeed < 0.2 ? `relative speed ~${Math.max(1, Math.round(st.relSpeed * 100))} cm/s (modelled)` : `relative speed ~${st.relSpeed.toFixed(1)} m/s (modelled)`) : '';
|
||||
|
||||
@@ -234,13 +234,15 @@ export function createHud(ctx) {
|
||||
const trueSize = !enlarged && st.camDistX < 200;
|
||||
magEl.style.opacity = (enlarged || trueSize) ? fade(s, climbS, END, 0.3) : 0;
|
||||
magEl.classList.toggle('enlarged', enlarged);
|
||||
magEl.textContent = enlarged ? `Satellites enlarged ${Math.round(st.mag)}×` : sh.id === 'ca' ? 'True size · faint bar: ±1.5 km tracking uncertainty' : 'True size';
|
||||
magEl.classList.toggle('low', !sh.inset);
|
||||
magEl.textContent = enlarged ? `Satellites enlarged ${Math.round(st.mag)}×` : sh.id === 'ca' ? 'True size · tracking error ±1.5 km > the gap' : 'True size';
|
||||
const relChip = $('#relchip');
|
||||
relChip.style.opacity = ['drop', 'close', 'dance', 'dance-ff', 'approach', 'leave'].includes(sh.id) ? fade(s, sh.s0, sh.s1, 0.3) : 0;
|
||||
relChip.style.opacity = 0;
|
||||
|
||||
// Inset
|
||||
const insA = sh.inset && s < END - 0.1 ? fade(s, Math.max(sh.s0, climbS + 0.8), END, 0.3) : 0;
|
||||
inset.draw(s, t, (st.sepG || st.sep) / 1000, { alpha: insA * live, trailHours: sh.inset || 1, cut: CUTS.some((c) => Math.abs(s - c) < 0.05) });
|
||||
insetEl.style.opacity = insA * live > 0.01 ? 1 : 0;
|
||||
inset.draw(s, t, (st.sepG || st.sep) / 1000, { alpha: insA * live, trailHours: sh.inset || 1, cut: CUTS.some((c) => Math.abs(s - c) < 0.05), behindNote: sh.id === 'climb' || sh.id === 'lap' });
|
||||
|
||||
// Timeline
|
||||
const px = timeline.x(Math.min(Math.max(t, TL.t0), TL.t1));
|
||||
@@ -355,6 +357,10 @@ export function createHud(ctx) {
|
||||
el.style.left = `${left}px`;
|
||||
el.style.top = `${top}px`;
|
||||
el.style.transform = 'none';
|
||||
if (overlap({ l: left, r: left + w, t: top, b: top + h }) > 0.35 * w * h) {
|
||||
el.style.opacity = 0;
|
||||
return;
|
||||
}
|
||||
el.style.textAlign = q[0] > 0 ? 'left' : 'right';
|
||||
obstacles.push({ left, right: left + w, top, bottom: top + h });
|
||||
const ax = q[0] > 0 ? left - 6 * U : left + w + 6 * U;
|
||||
@@ -386,14 +392,11 @@ export function createHud(ctx) {
|
||||
if (!ec.dataset.built) {
|
||||
ec.dataset.built = '1';
|
||||
ec.innerHTML = `<div class="panel">
|
||||
<div class="big">~${CA_M} m</div>
|
||||
<div class="range">closest pass, Oct 18, in Earth's shadow · LeoLabs ~500 m · estimates 150–650 m</div>
|
||||
<div class="stats">
|
||||
<div><b>18</b><span>days from first burn to closest pass</span></div>
|
||||
<div><b>${BURNS.length}</b><span>manoeuvres</span></div>
|
||||
<div><b>~${Math.round(D.total_dv)} m/s</b><span>of thrust spent</span></div>
|
||||
</div>
|
||||
<div class="why">U.S. officials called it <b>"consistent with on-orbit weapons operations."</b><br>By Oct 25 it was still parked ~480 km behind, in the same orbit.</div>
|
||||
<div class="kicker">Closest pass <span class="c-cos">~${CA_M} m</span> · Oct 18 · in Earth's shadow</div>
|
||||
<div class="quote">“Consistent with on-orbit weapons operations.”</div>
|
||||
<div class="who">— Thomas DiNanno, U.S. Under Secretary of State</div>
|
||||
<div class="still">By Oct 25, Cosmos 2612 was still parked ~480 km behind, in the same orbit.</div>
|
||||
<div class="statline">18 days from first burn · ${BURNS.length} manoeuvres · ~${Math.round(D.total_dv)} m/s Δv · LeoLabs ~500 m · estimates 150–650 m</div>
|
||||
</div>`;
|
||||
}
|
||||
$('#endlogo').style.opacity = endA;
|
||||
|
||||
+15
-12
@@ -32,7 +32,7 @@ export function createInset({ D, U, relAt, canvas }) {
|
||||
ctx.globalAlpha = opts.alpha;
|
||||
|
||||
// Zoom: half-width (km) follows the guiding separation; eased frame to frame.
|
||||
const target = Math.min(9200, Math.max(1.4, 1.6 * sepGuideKm));
|
||||
const target = Math.min(9200, Math.max(1.0, 1.25 * sepGuideKm));
|
||||
if (state.hw === null || Math.abs(s - state.lastS) > 0.2 || opts.cut) state.hw = target;
|
||||
else state.hw = Math.exp(Math.log(state.hw) + (Math.log(target) - Math.log(state.hw)) * 0.12);
|
||||
state.lastS = s;
|
||||
@@ -51,13 +51,16 @@ export function createInset({ D, U, relAt, canvas }) {
|
||||
ctx.roundRect(0, 0, W, H, r);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
const head = 44 * U * dpr;
|
||||
const head = 52 * U * dpr;
|
||||
ctx.fillStyle = 'rgba(244,247,250,0.92)';
|
||||
ctx.font = `800 ${22 * U * dpr}px Inter, sans-serif`;
|
||||
ctx.font = `800 ${26 * U * dpr}px Inter, sans-serif`;
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText('SEEN FROM X36', pad, head / 2 + 4 * dpr);
|
||||
const tw = ctx.measureText('SEEN FROM X36 ').width;
|
||||
ctx.fillStyle = '#ff5a3c';
|
||||
ctx.fillText('· red = Cosmos', pad + tw, head / 2 + 4 * dpr);
|
||||
ctx.fillStyle = 'rgba(244,247,250,0.6)';
|
||||
ctx.font = `600 ${20 * U * dpr}px Inter, sans-serif`;
|
||||
ctx.font = `700 ${24 * U * dpr}px Inter, sans-serif`;
|
||||
ctx.textAlign = 'right';
|
||||
ctx.fillText('flight →', W - pad, head / 2 + 4 * dpr);
|
||||
ctx.textAlign = 'left';
|
||||
@@ -133,7 +136,7 @@ export function createInset({ D, U, relAt, canvas }) {
|
||||
const [x, y] = P(...toXY(relAt(tt)));
|
||||
if (prev) {
|
||||
ctx.strokeStyle = `rgba(255,90,60,${0.08 + 0.85 * (k / n) ** 1.5})`;
|
||||
ctx.lineWidth = (1.5 + 2.5 * (k / n)) * dpr;
|
||||
ctx.lineWidth = (2.5 + 4 * (k / n)) * U * 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(prev[0], prev[1]);
|
||||
ctx.lineTo(x, y);
|
||||
@@ -162,15 +165,15 @@ export function createInset({ D, U, relAt, canvas }) {
|
||||
ctx.fill();
|
||||
ctx.shadowBlur = 0;
|
||||
};
|
||||
dot(xxp, xyp, '#5ce1e6', 7 * U);
|
||||
dot(cxp, cyp, '#ff5a3c', 7 * U);
|
||||
ctx.font = `800 ${20 * U * dpr}px Inter, sans-serif`;
|
||||
dot(xxp, xyp, '#5ce1e6', 11 * U);
|
||||
dot(cxp, cyp, '#ff5a3c', 11 * U);
|
||||
ctx.font = `800 ${26 * U * dpr}px Inter, sans-serif`;
|
||||
ctx.fillStyle = '#5ce1e6';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText('X36', xxp, xyp - 20 * U * dpr);
|
||||
ctx.fillText('X36', xxp, xyp - 24 * U * dpr);
|
||||
ctx.fillStyle = '#ff5a3c';
|
||||
const below = cyp > xyp + 10 * dpr || Math.abs(cxp - xxp) < 40 * U * dpr;
|
||||
ctx.fillText('COSMOS', Math.min(Math.max(cxp, px0 + 50 * U * dpr), px0 + pw - 50 * U * dpr), cyp + (below ? 24 : -20) * U * dpr);
|
||||
ctx.fillText(opts.behindNote ? '← COSMOS falling behind' : 'COSMOS', Math.min(Math.max(cxp, px0 + 150 * U * dpr), px0 + pw - 150 * U * dpr), cyp + (below ? 32 : -24) * U * dpr);
|
||||
ctx.textAlign = 'left';
|
||||
ctx.restore();
|
||||
|
||||
@@ -185,13 +188,13 @@ export function createInset({ D, U, relAt, canvas }) {
|
||||
ctx.lineTo(pad + sbpx, by);
|
||||
ctx.stroke();
|
||||
ctx.fillStyle = 'rgba(244,247,250,0.85)';
|
||||
ctx.font = `700 ${19 * U * dpr}px 'JetBrains Mono', monospace`;
|
||||
ctx.font = `700 ${24 * U * dpr}px 'JetBrains Mono', monospace`;
|
||||
ctx.textBaseline = 'bottom';
|
||||
ctx.fillText(fmt(sb), pad, by - 6 * dpr);
|
||||
if (exag > 1.5) {
|
||||
ctx.textAlign = 'right';
|
||||
ctx.fillStyle = 'rgba(255,181,71,0.95)';
|
||||
ctx.font = `700 ${19 * U * dpr}px Inter, sans-serif`;
|
||||
ctx.font = `700 ${24 * U * dpr}px Inter, sans-serif`;
|
||||
ctx.fillText(`heights ×${Math.round(exag)}`, W - pad, by - 6 * dpr);
|
||||
ctx.textAlign = 'left';
|
||||
}
|
||||
|
||||
@@ -90,9 +90,11 @@
|
||||
#readout .val small { font-size: 0.42em; color: var(--ink-2); margin-left: 0.12em; letter-spacing: 0; }
|
||||
#readout .ctx { margin-top: calc(10 * var(--u)); font: 600 calc(32 * var(--u))/1.2 Inter, sans-serif; color: var(--ink); }
|
||||
|
||||
#mag { position: absolute; left: calc(56 * var(--u) + 536 * var(--u)); bottom: calc(246 * var(--u)); padding: calc(7 * var(--u)) calc(14 * var(--u)); border-radius: calc(6 * var(--u));
|
||||
#mag { position: absolute; left: var(--pad); bottom: calc(590 * var(--u)); padding: calc(7 * var(--u)) calc(14 * var(--u)); border-radius: calc(6 * var(--u));
|
||||
font: 800 calc(28 * var(--u))/1 Inter, sans-serif; letter-spacing: 0.02em; color: #000; background: #fff; }
|
||||
#mag.enlarged { background: var(--burn); }
|
||||
#mag.low { bottom: calc(196 * var(--u)); }
|
||||
body.portrait #mag.low { bottom: calc(440 * var(--u)); }
|
||||
|
||||
.nw { white-space: nowrap; }
|
||||
#clock .pills { display: flex; gap: calc(10 * var(--u)); justify-content: flex-end; margin-top: calc(16 * var(--u)); }
|
||||
@@ -106,7 +108,7 @@
|
||||
#clock .light.night { color: #b9c8ff; } #clock .light.night i { background: #20263a; border: calc(3 * var(--u)) solid #b9c8ff; }
|
||||
#readout .rel { margin-top: calc(6 * var(--u)); font: 600 calc(28 * var(--u))/1.2 Inter, sans-serif; color: var(--ink-2); }
|
||||
/* Inset + chips */
|
||||
#inset { position: absolute; left: var(--pad); bottom: calc(196 * var(--u)); width: calc(520 * var(--u)); height: calc(330 * var(--u)); }
|
||||
#inset { position: absolute; left: var(--pad); bottom: calc(196 * var(--u)); width: calc(640 * var(--u)); height: calc(380 * var(--u)); }
|
||||
#relchip { position: absolute; left: calc(56 * var(--u) + 536 * var(--u)); bottom: calc(196 * var(--u)); padding: calc(7 * var(--u)) calc(14 * var(--u));
|
||||
border-radius: calc(6 * var(--u)); font: 800 calc(26 * var(--u))/1 Inter, sans-serif; color: #fff; background: rgba(255,90,60,0.85); opacity: 0; }
|
||||
.event { background: rgba(4,8,14,0.62); padding: calc(10 * var(--u)) calc(16 * var(--u)) calc(10 * var(--u)) calc(14 * var(--u)); border-radius: calc(8 * var(--u)); width: fit-content; }
|
||||
@@ -129,6 +131,7 @@
|
||||
/* Tags + dimension */
|
||||
#leaders { position: absolute; inset: 0; width: 100%; height: 100%; }
|
||||
.tag { position: absolute; white-space: nowrap; }
|
||||
.tag { background: rgba(3,6,12,0.55); padding: calc(6 * var(--u)) calc(12 * var(--u)); border-radius: calc(8 * var(--u)); }
|
||||
.tag .name { font: 800 calc(40 * var(--u))/1.05 Inter, sans-serif; letter-spacing: 0.01em; }
|
||||
.tag .meta { font: 600 calc(28 * var(--u))/1.2 Inter, sans-serif; color: var(--ink-2); margin-top: calc(4 * var(--u)); }
|
||||
#dim { position: absolute; transform: translate(-50%, -50%); padding: calc(6 * var(--u)) calc(14 * var(--u)); white-space: nowrap;
|
||||
@@ -148,6 +151,14 @@
|
||||
#endcard .stats b { display: block; font: 700 calc(64 * var(--u))/1 'JetBrains Mono', monospace; letter-spacing: -0.03em; }
|
||||
#endcard .stats span { display: block; margin-top: calc(10 * var(--u)); font: 600 calc(28 * var(--u))/1.2 Inter, sans-serif; color: var(--ink-2); }
|
||||
#endcard .why { max-width: calc(1100 * var(--u)); margin: 0 auto; font: 500 calc(32 * var(--u))/1.35 Inter, sans-serif; color: var(--ink-2); }
|
||||
#endcard .kicker { font: 800 calc(30 * var(--u))/1.2 Inter, sans-serif; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2); }
|
||||
#endcard .quote { margin-top: calc(26 * var(--u)); font: 800 calc(76 * var(--u))/1.08 Inter, sans-serif; letter-spacing: -0.02em; }
|
||||
#endcard .who { margin-top: calc(18 * var(--u)); font: 600 calc(32 * var(--u))/1.2 Inter, sans-serif; color: var(--ink-2); }
|
||||
#endcard .still { margin-top: calc(34 * var(--u)); font: 700 calc(38 * var(--u))/1.25 Inter, sans-serif; color: var(--ink); }
|
||||
#endcard .statline { margin-top: calc(30 * var(--u)); font: 500 calc(22 * var(--u))/1.4 'JetBrains Mono', monospace; color: var(--ink-3); }
|
||||
body.portrait #endcard .quote { font-size: calc(72 * var(--u)); }
|
||||
body.portrait #endcard .still { font-size: calc(40 * var(--u)); }
|
||||
body.portrait #endcard .statline { font-size: calc(24 * var(--u)); }
|
||||
#endlogo { left: 50%; top: calc(70 * var(--u)); transform: translateX(-50%); }
|
||||
#endlogo img { height: calc(96 * var(--u)); }
|
||||
#endlogo span { font-size: calc(40 * var(--u)); }
|
||||
@@ -166,11 +177,11 @@
|
||||
body.portrait #events { top: calc(700 * var(--u)); width: auto; right: var(--pad); }
|
||||
body.portrait #readout { right: auto; left: var(--pad); bottom: calc(420 * var(--u)); text-align: left; }
|
||||
body.portrait #readout .val { font-size: calc(104 * var(--u)); }
|
||||
body.portrait #mag { left: auto; right: var(--pad); bottom: calc(912 * var(--u)); }
|
||||
body.portrait #mag { left: auto; right: var(--pad); bottom: calc(934 * var(--u)); }
|
||||
body.portrait #tlwrap { display: none; }
|
||||
body.portrait #inset { left: auto; right: var(--pad); bottom: calc(420 * var(--u)); width: calc(560 * var(--u)); height: calc(430 * var(--u)); }
|
||||
body.portrait #inset { left: auto; right: var(--pad); bottom: calc(420 * var(--u)); width: calc(620 * var(--u)); height: calc(500 * var(--u)); }
|
||||
body.portrait #relchip { left: auto; right: var(--pad); bottom: calc(866 * var(--u)); }
|
||||
body.portrait #readout { max-width: calc(400 * var(--u)); }
|
||||
body.portrait #readout { max-width: calc(340 * var(--u)); }
|
||||
body.portrait #readout .light { margin: 0 0 calc(18 * var(--u)); display: inline-flex; }
|
||||
body.portrait .ch span { font-size: calc(24 * var(--u)); }
|
||||
body.portrait #interstitial .d { font-size: calc(120 * var(--u)); }
|
||||
|
||||
+12
-10
@@ -195,7 +195,7 @@ function camAt(s) {
|
||||
sun: lerp(a.sun || 0, b.sun || 0),
|
||||
vup: lerp(a.vup || 0, b.vup || 0),
|
||||
comp: lerp(a.comp ?? 1, b.comp ?? 1),
|
||||
compY: lerp(a.compY ?? 0, b.compY ?? 0),
|
||||
compY: lerp(a.compY ?? 0, b.compY ?? 0) + (PORTRAIT ? 0 : lerp(a.compYL ?? 0, b.compYL ?? 0)),
|
||||
pairD: lerp(a.pairD || 0, b.pairD || 0),
|
||||
pairTh: lerp(a.pairTh || 0, b.pairTh || 0),
|
||||
aimRel: [0, 1, 2].map((k) => lerp(ar[k], br[k])),
|
||||
@@ -453,15 +453,16 @@ function setState(s) {
|
||||
v3.add(aim, v3.multiplyByScalar(RW, cam.aimRel[0], new v3()), aim);
|
||||
v3.add(aim, v3.multiplyByScalar(TW, cam.aimRel[1], new v3()), aim);
|
||||
v3.add(aim, v3.multiplyByScalar(NW, cam.aimRel[2], new v3()), aim);
|
||||
const dist = Math.max(cam.dist, cam.f * sepG) * (PORTRAIT && cam.earth > 0.5 ? 0.74 : 1);
|
||||
const dist = Math.max(cam.dist, cam.f * sepG) * (PORTRAIT && cam.earth > 0.5 ? 0.74 : 1) * (PORTRAIT && sh.id === 'hook' ? 0.6 : 1);
|
||||
const az = Cesium.Math.toRadians(cam.az);
|
||||
const el = Cesium.Math.toRadians(cam.el);
|
||||
const off = new v3();
|
||||
v3.add(off, v3.multiplyByScalar(cT, -Math.cos(el) * Math.cos(az), new v3()), off);
|
||||
v3.add(off, v3.multiplyByScalar(cN, Math.cos(el) * Math.sin(az), new v3()), off);
|
||||
v3.add(off, v3.multiplyByScalar(cR, Math.sin(el), new v3()), off);
|
||||
if (cam.sun > 0) {
|
||||
v3.normalize(v3.add(v3.multiplyByScalar(off, 1 - cam.sun, off), v3.multiplyByScalar(sunW, cam.sun, new v3()), off), off);
|
||||
const sunW8 = Math.min(0.8, cam.sun * (PORTRAIT ? 2 : 1));
|
||||
if (sunW8 > 0) {
|
||||
v3.normalize(v3.add(v3.multiplyByScalar(off, 1 - sunW8, off), v3.multiplyByScalar(sunW, sunW8, new v3()), off), off);
|
||||
}
|
||||
const camPos = v3.add(pivot, v3.multiplyByScalar(off, dist, new v3()), new v3());
|
||||
if (cam.pairD > 0) {
|
||||
@@ -469,7 +470,7 @@ function setState(s) {
|
||||
// (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 * (PORTRAIT ? 1.3 : 1));
|
||||
const th = Cesium.Math.toRadians(cam.pairTh * (PORTRAIT ? 0.7 : 1));
|
||||
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());
|
||||
@@ -495,10 +496,11 @@ function setState(s) {
|
||||
const aspect = W / H;
|
||||
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 ndcX = (PORTRAIT ? 0 : 0.18) * cam.comp;
|
||||
const ndcY = (PORTRAIT ? -0.04 : 0.06) * cam.comp + cam.compY;
|
||||
const ndcX = (PORTRAIT ? 0 : 0.24) * cam.comp;
|
||||
const ndcY = (PORTRAIT ? 0.14 : 0.06) * cam.comp + cam.compY;
|
||||
const ndcYp = ndcY + (PORTRAIT && cam.pairD > 0 ? 0.13 : 0);
|
||||
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, -ndcYp * tanV, new v3()), new v3()), dir);
|
||||
right = v3.normalize(v3.cross(dir, upHint, new v3()), new v3());
|
||||
up = v3.cross(right, dir, new v3());
|
||||
}
|
||||
@@ -612,7 +614,7 @@ function setState(s) {
|
||||
if (sh.id === 'ca') {
|
||||
const a0 = v3.add(cosW, v3.multiplyByScalar(TW, -1500, new v3()), new v3());
|
||||
const a1 = v3.add(cosW, v3.multiplyByScalar(TW, 1500, new v3()), new v3());
|
||||
polyline([a0, cosW, a1], [COL_COS.withAlpha(0), COL_COS.withAlpha(0.45), COL_COS.withAlpha(0)], 10);
|
||||
polyline([a0, cosW, a1], [COL_COS.withAlpha(0.25), COL_COS.withAlpha(0.6), COL_COS.withAlpha(0.25)], 22);
|
||||
}
|
||||
// RIC reference rings (orbital plane) during proximity
|
||||
const gridA = Math.min(1, Math.max(0, (8e3 - Math.max(sep, camDistX)) / 3e3)) * (cam.mag > 0.4 ? 1 : 0.35);
|
||||
@@ -640,7 +642,7 @@ function setState(s) {
|
||||
const r0 = relAt(t - 30);
|
||||
const relSpeed = Math.hypot(r1[0] - r0[0], r1[1] - r0[1], r1[2] - r0[2]) / 60;
|
||||
|
||||
return { sh, t, b, jd, x36W, cosW, RW, TW, NW, rel, sep, sepG, mag, gridA, lx, camDistX, vX: vXv, vC, relSpeed };
|
||||
return { sh, t, b, jd, x36W, cosW, RW, TW, NW, rel, sep, sepG, da: smaOffsetAt(t), mag, gridA, lx, camDistX, vX: vXv, vC, relSpeed };
|
||||
}
|
||||
|
||||
async function frame(s) {
|
||||
|
||||
+42
-42
@@ -39,87 +39,87 @@ export function buildStory({ sec, D }) {
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'lap', mode: 'ff', s0: 9.6, s1: 13.6, blur: 1, bgRate: 260, inset: 30,
|
||||
tau: [[9.6, iso('2026-09-30T13:00:00Z')], [13.6, iso('2026-10-11T23:40:00Z')]],
|
||||
id: 'lap', mode: 'ff', s0: 9.6, s1: 12.6, blur: 1, bgRate: 260, inset: 30,
|
||||
tau: [[9.6, iso('2026-09-30T13:00:00Z')], [12.6, iso('2026-10-11T23:40:00Z')]],
|
||||
cam: [
|
||||
{ s: 9.6, ...WIDE, dist: 2.9e7, az: 100, el: 30, atrail: 0, rtrail: 30 },
|
||||
{ s: 13.6, ...WIDE, dist: 2.5e7, az: 116, el: 28, atrail: 0, rtrail: 30 },
|
||||
{ s: 12.6, ...WIDE, dist: 2.5e7, az: 116, el: 28, atrail: 0, rtrail: 30 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'drop', mode: 'true', s0: 13.6, s1: 18.6, blur: 3, inset: 4,
|
||||
tau: [[13.6, iso('2026-10-12T00:00:00Z')], [18.6, iso('2026-10-12T03:30:00Z')]],
|
||||
id: 'drop', mode: 'true', s0: 12.6, s1: 17.6, blur: 3, inset: 4,
|
||||
tau: [[12.6, iso('2026-10-12T00:00:00Z')], [17.6, iso('2026-10-12T03:30:00Z')]],
|
||||
cam: [
|
||||
{ s: 13.6, dist: 2.4e5, f: 2.6, az: 62, el: 28, look: 0.5, fov: 36, mag: 0, rtrail: 3, atrail: 20, vup: 1 },
|
||||
{ s: 16.0, dist: 3.0e5, f: 2.6, az: 48, el: 30, look: 0.5, fov: 36, mag: 0, rtrail: 4, atrail: 20, vup: 1 },
|
||||
{ s: 18.6, dist: 4.0e5, f: 2.6, az: 34, el: 32, look: 0.5, fov: 36, mag: 0, rtrail: 4, atrail: 20, vup: 1 },
|
||||
{ s: 12.6, dist: 2.4e5, f: 2.6, az: 62, el: 28, look: 0.5, fov: 36, mag: 0, rtrail: 3, atrail: 20, vup: 1 },
|
||||
{ s: 15.0, dist: 3.0e5, f: 2.6, az: 48, el: 30, look: 0.5, fov: 36, mag: 0, rtrail: 4, atrail: 20, vup: 1 },
|
||||
{ s: 17.6, dist: 4.0e5, f: 2.6, az: 34, el: 32, look: 0.5, fov: 36, mag: 0, rtrail: 4, atrail: 20, vup: 1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'close', mode: 'ff', s0: 18.6, s1: 22.0, blur: 1, bgRate: 140, inset: 30,
|
||||
tau: [[18.6, iso('2026-10-12T03:40:00Z')], [20.3, iso('2026-10-15T06:00:00Z')], [22.0, iso('2026-10-16T07:00:00Z')]],
|
||||
id: 'close', mode: 'ff', s0: 17.6, s1: 21.0, blur: 1, bgRate: 140, inset: 30,
|
||||
tau: [[17.6, iso('2026-10-12T03:40:00Z')], [19.3, iso('2026-10-15T06:00:00Z')], [21.0, iso('2026-10-16T07:00:00Z')]],
|
||||
cam: [
|
||||
{ s: 18.6, dist: 1.0e6, f: 2.8, az: 30, el: 34, look: 0.5, fov: 34, mag: 0, rtrail: 30, atrail: 0, vup: 1 },
|
||||
{ s: 22.0, dist: 2.0e4, f: 2.8, az: 46, el: 34, look: 0.5, fov: 34, mag: 0, rtrail: 30, atrail: 0, vup: 1 },
|
||||
{ s: 17.6, dist: 1.0e6, f: 2.8, az: 30, el: 34, look: 0.5, fov: 34, mag: 0, rtrail: 30, atrail: 0, vup: 1 },
|
||||
{ s: 21.0, dist: 2.0e4, f: 2.8, az: 46, el: 34, look: 0.5, fov: 34, mag: 0, rtrail: 30, atrail: 0, vup: 1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'dance', mode: 'true', s0: 22.0, s1: 28.0, blur: 5, inset: 3.2,
|
||||
tau: [[22.0, iso('2026-10-16T07:10:00Z')], [28.0, iso('2026-10-16T12:10:00Z')]],
|
||||
id: 'dance', mode: 'true', s0: 21.0, s1: 27.0, blur: 5, inset: 3.2,
|
||||
tau: [[21.0, iso('2026-10-16T07:10:00Z')], [27.0, iso('2026-10-16T12:10:00Z')]],
|
||||
cam: [
|
||||
{ s: 22.0, dist: 5.5e3, f: 1.7, az: 62, el: 30, look: 0.5, fov: 38, mag: 1, mx: 40, rtrail: 3, atrail: 1.2, vup: 1 },
|
||||
{ s: 28.0, dist: 5.0e3, f: 1.7, az: 80, el: 26, look: 0.5, fov: 38, mag: 1, mx: 40, rtrail: 3, atrail: 1.2, vup: 1 },
|
||||
{ s: 21.0, dist: 5.5e3, f: 1.7, az: 62, el: 30, look: 0.5, fov: 38, mag: 1, mx: 40, rtrail: 3, atrail: 1.2, vup: 1 },
|
||||
{ s: 27.0, dist: 5.0e3, f: 1.7, az: 80, el: 26, look: 0.5, fov: 38, mag: 1, mx: 40, rtrail: 3, atrail: 1.2, vup: 1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'dance-ff', mode: 'ff', s0: 28.0, s1: 30.0, blur: 1, bgRate: 140, inset: 10,
|
||||
tau: [[28.0, iso('2026-10-16T12:20:00Z')], [30.0, iso('2026-10-17T22:20:00Z')]],
|
||||
id: 'dance-ff', mode: 'ff', s0: 27.0, s1: 29.0, blur: 1, bgRate: 140, inset: 10,
|
||||
tau: [[27.0, iso('2026-10-16T12:20:00Z')], [29.0, iso('2026-10-17T22:20:00Z')]],
|
||||
cam: [
|
||||
{ s: 28.0, dist: 4.5e3, f: 1.7, az: 72, el: 38, look: 0.5, fov: 38, mag: 1, mx: 40, rtrail: 12, atrail: 0, vup: 1 },
|
||||
{ s: 30.0, dist: 3.4e3, f: 1.7, az: 90, el: 32, look: 0.5, fov: 38, mag: 1, mx: 40, rtrail: 12, atrail: 0, vup: 1 },
|
||||
{ s: 27.0, dist: 4.5e3, f: 1.7, az: 72, el: 38, look: 0.5, fov: 38, mag: 1, mx: 40, rtrail: 12, atrail: 0, vup: 1 },
|
||||
{ s: 29.0, dist: 3.4e3, f: 1.7, az: 90, el: 32, look: 0.5, fov: 38, mag: 1, mx: 40, rtrail: 12, atrail: 0, vup: 1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'approach', mode: 'true', s0: 30.0, s1: 33.6, blur: 3, inset: 3,
|
||||
tau: [[30.0, iso('2026-10-17T22:30:00Z')], [32.0, CA - 40 * M], [33.6, CA - 5 * M]],
|
||||
id: 'approach', mode: 'true', s0: 29.0, s1: 32.6, blur: 3, inset: 3,
|
||||
tau: [[29.0, iso('2026-10-17T22:30:00Z')], [31.0, CA - 40 * M], [32.6, CA - 5 * M]],
|
||||
cam: [
|
||||
{ s: 30.0, dist: 3.0e3, f: 1.7, az: 96, el: 26, look: 0.5, fov: 38, mag: 1, mx: 30, rtrail: 3, atrail: 1.5, vup: 1 },
|
||||
{ s: 33.6, dist: 1.4e3, f: 1.7, az: 112, el: 18, look: 0.5, fov: 36, mag: 1, mx: 25, rtrail: 1.6, atrail: 1.5, vup: 1 },
|
||||
{ s: 29.0, dist: 3.0e3, f: 1.7, az: 96, el: 26, look: 0.5, fov: 38, mag: 1, mx: 30, rtrail: 3, atrail: 1.5, vup: 1 },
|
||||
{ s: 32.6, dist: 1.4e3, f: 1.7, az: 112, el: 18, look: 0.5, fov: 36, mag: 1, mx: 25, rtrail: 1.6, atrail: 1.5, vup: 1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'ca', mode: 'true', s0: 33.6, s1: 36.6, blur: 1, inset: 1, noSky: true,
|
||||
tau: [[33.6, CA - 4 * M], [36.6, CA + 2 * M]],
|
||||
id: 'ca', mode: 'true', s0: 32.6, s1: 35.6, blur: 1, inset: 0, noSky: true,
|
||||
tau: [[32.6, CA - 4 * M], [35.6, CA + 2 * M]],
|
||||
cam: [
|
||||
{ s: 33.6, dist: 26, az: 160, el: 0, look: 0, fov: 34, mag: 0, rtrail: 0.4, atrail: 0.6, comp: 0, pairD: 26, pairTh: 24 },
|
||||
{ s: 36.6, dist: 20, az: 163, el: 0, look: 0, fov: 34, mag: 0, rtrail: 0.4, atrail: 0.6, comp: 0, pairD: 20, pairTh: 14 },
|
||||
{ s: 32.6, dist: 26, az: 160, el: 0, look: 0, fov: 34, mag: 0, rtrail: 0.4, atrail: 0.6, comp: 0, compYL: 0.07, pairD: 26, pairTh: 24 },
|
||||
{ s: 35.6, dist: 20, az: 163, el: 0, look: 0, fov: 34, mag: 0, rtrail: 0.4, atrail: 0.6, comp: 0, compYL: 0.07, pairD: 20, pairTh: 14 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'sunrise', mode: 'true', s0: 36.6, s1: 39.0, blur: 1, inset: 1.2,
|
||||
tau: [[36.6, CA + 6 * M], [39.0, CA + 27 * M]],
|
||||
id: 'sunrise', mode: 'true', s0: 35.6, s1: 38.0, blur: 1, inset: 1.2,
|
||||
tau: [[35.6, CA + 6 * M], [38.0, CA + 27 * M]],
|
||||
cam: [
|
||||
{ s: 36.6, dist: 1.3e3, f: 2.2, az: 96, el: 16, look: 0.5, fov: 36, mag: 1, mx: 25, rtrail: 1.6, atrail: 1.5, vup: 1 },
|
||||
{ s: 39.0, dist: 2.2e3, f: 2.4, az: 104, el: 22, look: 0.5, fov: 36, mag: 1, mx: 25, rtrail: 2, atrail: 1.5, vup: 1 },
|
||||
{ s: 35.6, dist: 1.3e3, f: 2.2, az: 96, el: 16, look: 0.5, fov: 36, mag: 1, mx: 25, rtrail: 1.6, atrail: 1.5, vup: 1 },
|
||||
{ s: 38.0, dist: 2.2e3, f: 2.4, az: 104, el: 22, look: 0.5, fov: 36, mag: 1, mx: 25, rtrail: 2, atrail: 1.5, vup: 1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'leave', mode: 'ff', s0: 39.0, s1: 42.4, blur: 1, bgRate: 140, inset: 40,
|
||||
tau: [[39.0, CA + 30 * M], [40.8, iso('2026-10-19T08:00:00Z')], [42.4, iso('2026-10-21T12:00:00Z')]],
|
||||
id: 'leave', mode: 'ff', s0: 38.0, s1: 40.6, blur: 1, bgRate: 140, inset: 40,
|
||||
tau: [[38.0, CA + 30 * M], [39.3, iso('2026-10-19T08:00:00Z')], [40.6, iso('2026-10-21T12:00:00Z')]],
|
||||
cam: [
|
||||
{ s: 39.0, dist: 1.2e4, f: 2.8, az: 110, el: 30, look: 0.4, fov: 36, mag: 1, mx: 40, rtrail: 8, atrail: 0, vup: 1 },
|
||||
{ s: 40.8, dist: 4.0e5, f: 2.8, az: 70, el: 30, look: 0.5, fov: 34, mag: 0, rtrail: 30, atrail: 0, vup: 1 },
|
||||
{ s: 42.4, dist: 1.6e6, f: 2.8, az: 44, el: 30, look: 0.5, fov: 34, mag: 0, rtrail: 60, atrail: 0, vup: 1 },
|
||||
{ s: 38.0, dist: 1.2e4, f: 2.8, az: 110, el: 30, look: 0.4, fov: 36, mag: 1, mx: 40, rtrail: 8, atrail: 0, vup: 1 },
|
||||
{ s: 39.3, dist: 4.0e5, f: 2.8, az: 70, el: 30, look: 0.5, fov: 34, mag: 0, rtrail: 30, atrail: 0, vup: 1 },
|
||||
{ s: 40.6, dist: 1.6e6, f: 2.8, az: 44, el: 30, look: 0.5, fov: 34, mag: 0, rtrail: 60, atrail: 0, vup: 1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'end', mode: 'true', s0: 42.4, s1: 45.4, blur: 1, inset: 0,
|
||||
tau: [[42.4, iso('2026-10-21T12:00:00Z')], [45.4, iso('2026-10-21T13:30:00Z')]],
|
||||
id: 'end', mode: 'true', s0: 40.6, s1: 43.6, blur: 1, inset: 0,
|
||||
tau: [[40.6, iso('2026-10-21T12:00:00Z')], [43.6, iso('2026-10-21T13:30:00Z')]],
|
||||
cam: [
|
||||
{ s: 42.4, ...WIDE, dist: 3.4e7, az: 92, el: 28, atrail: 80, rtrail: 0 },
|
||||
{ s: 45.4, ...WIDE, dist: 3.1e7, az: 100, el: 26, atrail: 80, rtrail: 0 },
|
||||
{ s: 40.6, ...WIDE, dist: 3.4e7, az: 92, el: 28, atrail: 80, rtrail: 0 },
|
||||
{ s: 43.6, ...WIDE, dist: 3.1e7, az: 100, el: 26, atrail: 80, rtrail: 0 },
|
||||
],
|
||||
},
|
||||
];
|
||||
return { shots, duration: 45.4 };
|
||||
return { shots, duration: 43.6 };
|
||||
}
|
||||
Reference in new issue
Block a user