fixed timeline and added qol
This commit is contained in:
parent
79e20ca37c
commit
63f79f8e56
7 changed files with 173 additions and 41 deletions
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { onDestroy } from 'svelte';
|
||||
import { getMap, plotPrediction, plotAnimatedMarker } from '$map';
|
||||
import { getMap, plotPrediction, plotAnimatedMarker, plotEndMarker } from '$map';
|
||||
import { timelineStore } from '$features/timeline/store';
|
||||
import { workspacesStore } from './store';
|
||||
import type { Workspace } from './types';
|
||||
|
|
@ -22,25 +22,34 @@
|
|||
// belong to workspaces which were removed from the store since last tick.
|
||||
const ownedPlotScenes = new Set<string>();
|
||||
const ownedCursorScenes = new Set<string>();
|
||||
// Last-rendered state per workspace scene — skip re-plot when nothing changed.
|
||||
const plotCache = new Map<string, { result: unknown; color: string; opacity: number }>();
|
||||
// Cursor scenes that have reached their flight end and show a static end marker.
|
||||
const doneCursorScenes = new Set<string>();
|
||||
|
||||
const sceneName = (w: Workspace) => `ws/${w.id}`;
|
||||
const cursorName = (w: Workspace) => `cursor/${w.id}`;
|
||||
|
||||
function updateGlobalRange(items: Workspace[]) {
|
||||
let min = Infinity;
|
||||
let max = -Infinity;
|
||||
let maxDuration = 0;
|
||||
const entries: Array<{ duration: number; color: string }> = [];
|
||||
for (const w of items) {
|
||||
if (!w.visible || !w.result) continue;
|
||||
const l = w.result.launch.datetime.getTime();
|
||||
const r = w.result.landing.datetime.getTime();
|
||||
if (l < min) min = l;
|
||||
if (r > max) max = r;
|
||||
}
|
||||
if (!isFinite(min) || !isFinite(max)) {
|
||||
timelineStore.setRange(0, 0);
|
||||
} else {
|
||||
timelineStore.setRange(min, max);
|
||||
const duration =
|
||||
w.result.landing.datetime.getTime() - w.result.launch.datetime.getTime();
|
||||
if (duration > maxDuration) maxDuration = duration;
|
||||
entries.push({ duration, color: w.color });
|
||||
}
|
||||
timelineStore.setRange(0, maxDuration);
|
||||
const seen = new Set<number>();
|
||||
const markers = entries
|
||||
.filter(({ duration }) => {
|
||||
if (duration >= maxDuration || seen.has(duration)) return false;
|
||||
seen.add(duration);
|
||||
return true;
|
||||
})
|
||||
.map(({ duration, color }) => ({ time: duration, color }));
|
||||
timelineStore.setMarkers(markers);
|
||||
}
|
||||
|
||||
function renderAll(items: Workspace[]) {
|
||||
|
|
@ -54,28 +63,39 @@
|
|||
if (ownedPlotScenes.has(name)) {
|
||||
map.disposeScene(name);
|
||||
ownedPlotScenes.delete(name);
|
||||
plotCache.delete(name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const scene = map.scene(name);
|
||||
plotPrediction(scene, w.result, { color: w.color, opacity: w.opacity });
|
||||
ownedPlotScenes.add(name);
|
||||
const cached = plotCache.get(name);
|
||||
if (!cached || cached.result !== w.result || cached.color !== w.color || cached.opacity !== w.opacity) {
|
||||
const scene = map.scene(name);
|
||||
plotPrediction(scene, w.result, { color: w.color, opacity: w.opacity });
|
||||
ownedPlotScenes.add(name);
|
||||
plotCache.set(name, { result: w.result, color: w.color, opacity: w.opacity });
|
||||
}
|
||||
}
|
||||
|
||||
for (const name of Array.from(ownedPlotScenes)) {
|
||||
if (!live.has(name)) {
|
||||
map.disposeScene(name);
|
||||
ownedPlotScenes.delete(name);
|
||||
plotCache.delete(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function positionAt(path: LatLngTuple[], time: number, launchMs: number, landingMs: number) {
|
||||
function positionAt(path: LatLngTuple[], elapsed: number, durationMs: number): LatLngTuple | null {
|
||||
if (path.length === 0) return null;
|
||||
if (launchMs === landingMs) return path[0];
|
||||
const t = Math.max(0, Math.min(1, (time - launchMs) / (landingMs - launchMs)));
|
||||
const idx = Math.min(path.length - 1, Math.floor(t * (path.length - 1)));
|
||||
return path[idx];
|
||||
if (durationMs === 0) return path[0];
|
||||
const t = Math.max(0, Math.min(1, elapsed / durationMs));
|
||||
const raw = t * (path.length - 1);
|
||||
const idx = Math.floor(raw);
|
||||
if (idx >= path.length - 1) return path[path.length - 1];
|
||||
const frac = raw - idx;
|
||||
const a = path[idx];
|
||||
const b = path[idx + 1];
|
||||
return [a[0] + (b[0] - a[0]) * frac, a[1] + (b[1] - a[1]) * frac] as LatLngTuple;
|
||||
}
|
||||
|
||||
function renderCursors(items: Workspace[], time: number) {
|
||||
|
|
@ -89,18 +109,32 @@
|
|||
if (ownedCursorScenes.has(name)) {
|
||||
map.disposeScene(name);
|
||||
ownedCursorScenes.delete(name);
|
||||
doneCursorScenes.delete(name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const p = positionAt(
|
||||
w.result.flight_path,
|
||||
time,
|
||||
w.result.launch.datetime.getTime(),
|
||||
w.result.landing.datetime.getTime(),
|
||||
);
|
||||
const durationMs =
|
||||
w.result.landing.datetime.getTime() - w.result.launch.datetime.getTime();
|
||||
const p = positionAt(w.result.flight_path, time, durationMs);
|
||||
if (!p) continue;
|
||||
const scene = map.scene(name);
|
||||
plotAnimatedMarker(scene, p[1], p[0]);
|
||||
const done = time >= durationMs;
|
||||
if (done) {
|
||||
if (!doneCursorScenes.has(name)) {
|
||||
// Transition into done state: swap to static end marker.
|
||||
scene.clear();
|
||||
plotEndMarker(scene, p[1], p[0]);
|
||||
doneCursorScenes.add(name);
|
||||
}
|
||||
// Position is clamped to landing — nothing more to update.
|
||||
} else {
|
||||
if (doneCursorScenes.has(name)) {
|
||||
// Transition back to active (user seeked backwards).
|
||||
scene.clear();
|
||||
doneCursorScenes.delete(name);
|
||||
}
|
||||
plotAnimatedMarker(scene, p[1], p[0]);
|
||||
}
|
||||
ownedCursorScenes.add(name);
|
||||
}
|
||||
|
||||
|
|
@ -108,6 +142,7 @@
|
|||
if (!live.has(name)) {
|
||||
map.disposeScene(name);
|
||||
ownedCursorScenes.delete(name);
|
||||
doneCursorScenes.delete(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -128,5 +163,6 @@
|
|||
for (const name of ownedCursorScenes) map.disposeScene(name);
|
||||
ownedPlotScenes.clear();
|
||||
ownedCursorScenes.clear();
|
||||
doneCursorScenes.clear();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue