mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
Bring launches up as the world time passes them, and play the whole track
Away from live, a launch shows from its lift-off on: its marker, label, paths and orbit stay hidden while the world time is before it, so scrubbing or playing brings the launches up in the order they flew. The selected mission stays whenever it flies, and live shows every launch as before. Play on the timeline runs the whole track in about a minute, from its start when at live, and runs the clock itself past weather's own replay. It goes live when it reaches now, or holds at an end that lies ahead of now. Going live puts the rate back to 1.
This commit is contained in:
1 parent
48093108b1
commit
0aa54dfa97
9 files changed
+326
-25
No files matched your search
@@ -8,6 +8,8 @@ const LIVE_TICK_MS = 1000;
|
||||
const LIVE_EDGE = 0.005;
|
||||
/** The least that counts as at now, past the readout's own second. */
|
||||
const LIVE_EDGE_MS = 5000;
|
||||
/** How long play takes to run the whole track, in real ms. */
|
||||
const PLAY_THROUGH_MS = 60_000;
|
||||
|
||||
/**
|
||||
* The world clock as the timeline shows it: `{live, playing, timeMs,
|
||||
@@ -20,17 +22,55 @@ const LIVE_EDGE_MS = 5000;
|
||||
* track's end keep up with now.
|
||||
*/
|
||||
export const useWorldTimeStore = defineStore('gev-world-time', () => {
|
||||
const spanOf = (state) => {
|
||||
if (!state.supported) return null;
|
||||
// The time the layers cover, reaching to now.
|
||||
const reach = () => {
|
||||
const coverage = worldClock.coverage();
|
||||
if (!coverage) return null;
|
||||
const now = worldClock.wallNow();
|
||||
return Object.freeze({
|
||||
start: Math.min(coverage.start, now, state.timeMs),
|
||||
end: Math.max(coverage.end, now, state.timeMs),
|
||||
return {
|
||||
start: Math.min(coverage.start, now),
|
||||
end: Math.max(coverage.end, now),
|
||||
marks: coverage.marks,
|
||||
};
|
||||
};
|
||||
const spanOf = (state) => {
|
||||
if (!state.supported) return null;
|
||||
const covered = reach();
|
||||
if (!covered) return null;
|
||||
return Object.freeze({
|
||||
start: Math.min(covered.start, state.timeMs),
|
||||
end: Math.max(covered.end, state.timeMs),
|
||||
marks: covered.marks,
|
||||
});
|
||||
};
|
||||
const nearNow = (ms, span) =>
|
||||
Math.abs(ms - worldClock.wallNow()) <=
|
||||
Math.max(LIVE_EDGE_MS, span ? (span.end - span.start) * LIVE_EDGE : 0);
|
||||
// Whether the track ends at now (and moves on with it), not past it.
|
||||
const endsAtNow = (covered) =>
|
||||
covered.end - worldClock.wallNow() <= LIVE_EDGE_MS;
|
||||
// Play stops at the end of the track: at now it goes live, past now (a
|
||||
// satellite's future) it holds there.
|
||||
let stopping = false;
|
||||
const stopAtEnd = (state) => {
|
||||
if (stopping || !state.playing || state.live || !state.supported)
|
||||
return false;
|
||||
const covered = reach();
|
||||
if (!covered || state.timeMs < covered.end) return false;
|
||||
stopping = true;
|
||||
try {
|
||||
if (endsAtNow(covered)) worldClock.goLive();
|
||||
else {
|
||||
// Back to the end first, so nothing hears the overshoot as paused.
|
||||
worldClock.seek(covered.end);
|
||||
worldClock.pause();
|
||||
worldClock.seek(covered.end);
|
||||
}
|
||||
} finally {
|
||||
stopping = false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const read = (state = worldClock.getState()) =>
|
||||
Object.freeze({
|
||||
live: state.live,
|
||||
@@ -54,6 +94,7 @@ export const useWorldTimeStore = defineStore('gev-world-time', () => {
|
||||
}
|
||||
};
|
||||
const unsubscribe = worldClock.subscribe((state) => {
|
||||
if (stopAtEnd(state)) return;
|
||||
time.value = read(state);
|
||||
syncTicker();
|
||||
});
|
||||
@@ -66,13 +107,30 @@ export const useWorldTimeStore = defineStore('gev-world-time', () => {
|
||||
|
||||
return {
|
||||
time,
|
||||
togglePlay: () => worldClock.togglePlay(),
|
||||
/**
|
||||
* Play runs the whole track in about a minute, from the start when it is
|
||||
* at its end (at live, say), as a video plays again from the top. It runs
|
||||
* the clock itself, past weather's own replay: weather follows the time
|
||||
* frame by frame like every other layer the track covers.
|
||||
*/
|
||||
togglePlay() {
|
||||
const { playing, live, timeMs, span } = time.value;
|
||||
if (playing) return worldClock.pause();
|
||||
const covered = reach();
|
||||
if (!span || !covered) return worldClock.play({ rate: 1 });
|
||||
const length = span.end - span.start;
|
||||
if (live || timeMs >= covered.end - LIVE_EDGE_MS)
|
||||
worldClock.seek(span.start);
|
||||
// An end at now moves on with it, so play outruns it by the track.
|
||||
const rate = endsAtNow(covered)
|
||||
? 1 + length / PLAY_THROUGH_MS
|
||||
: Math.max(1, length / PLAY_THROUGH_MS);
|
||||
worldClock.play({ rate, continuous: true });
|
||||
},
|
||||
goLive: () => worldClock.goLive(),
|
||||
/** Scrub to `ms`: playback stops where the hand puts it; at now, live. */
|
||||
scrub(ms) {
|
||||
const span = time.value.span;
|
||||
const edge = span ? (span.end - span.start) * LIVE_EDGE : 0;
|
||||
if (Math.abs(ms - worldClock.wallNow()) <= Math.max(LIVE_EDGE_MS, edge)) {
|
||||
if (nearNow(ms, time.value.span)) {
|
||||
worldClock.goLive();
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user