mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
- Meaning fixes: playback connects to the broadcaster when it starts, browser
keys are restricted at the provider, clearing data layers switches them off
(ausschalten, not löschen), cockpit inputs are Quellen, Electronic is
Elektronische Musik, AIS types name vessels (SEGLER, LOTSENBOOT,
SANITÄTSDIENST, SCHNELLSCHIFF), contiguous US excludes Alaska/Hawaii,
wind and weather readings are model values, nearest frames are nearest in
time, cloud imagery and the brightness filter say what they are.
- NHC/CPHC advisories are Lageberichte, not Warnungen; cyclone strings use
Wirbelsturm throughout. Glossary updated.
- Grammar: " oder " keeps its space as a join separator, "{x} konnte nicht
sauber beendet werden", Then: no longer doubles Dann, scene save reasons
continue the sentence in lower case, neutral "Warten auf".
- Spacing between numbers and units in the remaining nine readouts.
- Reroute tooltip: German mode nouns keep their capital (Auto, Fahrrad).
- Built-in Nepal shot titles localize in every language: the legacy scene
upgrade rebuilds its base shots without sourcePackId, so the pack's own
shots in the scene now vouch for them.
- Layout: the layer status column no longer shrinks under a wide chip, and
a mixed-case compound (ALPR-Kameras) may break at its hyphen; only capital
codes (ADS-B, RTL-SDR) stay unbroken. WIRD AKTIVIERT overlapped the name.
The Bhote step meta line may take a third line.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
1107 lines
27 KiB
JavaScript
1107 lines
27 KiB
JavaScript
/**
|
|
* Scene recipes optimized for short social clips.
|
|
* Each recipe is deterministic so repeated runs produce similar footage.
|
|
*/
|
|
|
|
import { expandNepalEvidencePack } from './nepalEvidencePack.js';
|
|
import { msg, t } from '../i18n/index.js';
|
|
|
|
const BHOTE_KOSHI_NEPAL_BASE_RECIPE = Object.freeze({
|
|
id: 'bhote-koshi-nepal-evidence-pack',
|
|
version: 12,
|
|
title: msg('Bhote Koshi Evidence Sequence'),
|
|
style: 'normal',
|
|
ui: { hidePanels: false, hudMode: 'full', safeFrame: '16:9' },
|
|
layers: {
|
|
'bhote-koshi-locator': false,
|
|
},
|
|
releaseLayerIds: ['bhote-koshi-2026', 'bhote-koshi-locator'],
|
|
post: {
|
|
bloom: 0,
|
|
sharpen: true,
|
|
detectionMode: 'OFF',
|
|
mapStack: 'photoreal',
|
|
},
|
|
legacySceneBootstrap: {
|
|
targetSceneTitle: 'Nepal Flood Incident',
|
|
fromShotTitles: ['Shot 1', 'Shot 2', 'Shot 3'],
|
|
cameraPath: [
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Global Incident Context',
|
|
lat: 27.5962,
|
|
lon: 78.5718,
|
|
alt: 15628002,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0.9,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'nepal-context' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Nepal-Focused Globe Rotation',
|
|
lat: 28.7248,
|
|
lon: 81.3226,
|
|
alt: 2068966,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0.9,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'nepal-context' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Regional Approach',
|
|
lat: 27.0354,
|
|
lon: 84.6287,
|
|
alt: 809543,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0.9,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'bhote-koshi-regional' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Nearby Cities',
|
|
lat: 27.891,
|
|
lon: 85.428,
|
|
alt: 225734,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0.9,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'bhote-koshi-city-context' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Incident Corridor',
|
|
lat: 28.0529,
|
|
lon: 85.2189,
|
|
alt: 126931,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 11,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'bhote-koshi-incident-places' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Flood Path',
|
|
lat: 28.1691,
|
|
lon: 85.352,
|
|
alt: 60328,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0.9,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'bhote-koshi-flood-path' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Corridor Overview',
|
|
lat: 27.8986,
|
|
lon: 84.8723,
|
|
alt: 53247,
|
|
heading: 60,
|
|
pitch: -48,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0.9,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'bhote-koshi-path-overview' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Upper Valley',
|
|
lat: 28.4187,
|
|
lon: 85.4341,
|
|
alt: 7977,
|
|
heading: 155,
|
|
pitch: -28,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 8,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'bhote-koshi-trigger-record' },
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
requiredShotTitles: [
|
|
'Global Incident Context',
|
|
'Nepal-Focused Globe Rotation',
|
|
'Bhote Koshi Regional Approach',
|
|
'Bhote Koshi Nearby Cities',
|
|
'Bhote Koshi Incident Corridor',
|
|
'Bhote Koshi Flood Path',
|
|
'Bhote Koshi Corridor Overview',
|
|
'Bhote Koshi Upper Valley',
|
|
'Upper Valley Collapse',
|
|
'Debris-Dammed Lake',
|
|
'Nepal-China Border Gate',
|
|
'Timure Evidence Cluster',
|
|
'Syabru Besi Passage',
|
|
'Bidur / Trishuli Consequence',
|
|
],
|
|
requiredSourcePackBeatIds: [
|
|
'immediate-collapse-viewpoint',
|
|
'debris-dammed-lake',
|
|
'gyirong-border-gate',
|
|
'timure-cluster',
|
|
'syabru-besi',
|
|
'bidur-trishuli-bridge',
|
|
],
|
|
requiredSourcePackLayerId: 'bhote-koshi-2026',
|
|
photorealSurfaceBeatIds: [
|
|
'gyirong-border-gate',
|
|
'timure-cluster',
|
|
'syabru-besi',
|
|
'bidur-trishuli-bridge',
|
|
],
|
|
runtimeControlsByBeat: {
|
|
'immediate-collapse-viewpoint': { evidenceMediaAutoplay: true },
|
|
'debris-dammed-lake': { evidenceMediaAutoplay: true },
|
|
'gyirong-border-gate': {
|
|
imageryComparison: true,
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 2.5,
|
|
deferEvidenceUntilCameraSettled: true,
|
|
evidenceRevealDurationSec: 2.5,
|
|
},
|
|
'timure-cluster': {
|
|
imageryComparison: true,
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 4,
|
|
},
|
|
'syabru-besi': {
|
|
imageryComparison: true,
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 4,
|
|
},
|
|
'bidur-trishuli-bridge': {
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 4,
|
|
},
|
|
},
|
|
shotPatches: [
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Global Incident Context',
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'immediate-collapse-viewpoint',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Nepal-Focused Globe Rotation',
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'debris-dammed-lake',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Regional Approach',
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'gyirong-border-gate',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Nearby Cities',
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'timure-cluster',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Incident Corridor',
|
|
camera: {
|
|
lat: 28.0529,
|
|
lon: 85.2189,
|
|
alt: 126931,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
},
|
|
holdSec: 11,
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'syabru-besi',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Flood Path',
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'bidur-trishuli-bridge',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Corridor Overview',
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'gyirong-border-gate',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Upper Valley',
|
|
holdSec: 8,
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'immediate-collapse-viewpoint',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'bhote-koshi-trigger-record',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Upper Valley Collapse',
|
|
camera: {
|
|
lat: 28.417,
|
|
lon: 85.3937,
|
|
alt: 9850,
|
|
heading: 146,
|
|
pitch: -34,
|
|
roll: 0,
|
|
},
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'immediate-collapse-viewpoint',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: { evidenceMediaAutoplay: true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Nepal-China Border Gate',
|
|
visual: { mapStack: 'photoreal' },
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Timure Evidence Cluster',
|
|
visual: { mapStack: 'photoreal' },
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Syabru Besi Passage',
|
|
visual: { mapStack: 'photoreal' },
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bidur / Trishuli Consequence',
|
|
visual: { mapStack: 'photoreal' },
|
|
},
|
|
],
|
|
cameraPath: [
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Upper Valley Collapse',
|
|
lat: 28.417,
|
|
lon: 85.3937,
|
|
alt: 9850,
|
|
heading: 146,
|
|
pitch: -34,
|
|
roll: 0,
|
|
duration: 4.2,
|
|
hold: 1.4,
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'immediate-collapse-viewpoint',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: { evidenceMediaAutoplay: true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Debris-Dammed Lake',
|
|
lat: 28.392914,
|
|
lon: 85.570921,
|
|
alt: 10744,
|
|
heading: 232,
|
|
pitch: -37,
|
|
roll: 0,
|
|
duration: 4.2,
|
|
hold: 1.4,
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'debris-dammed-lake',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: { evidenceMediaAutoplay: true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Nepal-China Border Gate',
|
|
mapStack: 'photoreal',
|
|
lat: 28.316789,
|
|
lon: 85.399734,
|
|
alt: 5900,
|
|
heading: 208,
|
|
pitch: -42,
|
|
roll: 0,
|
|
duration: 4.2,
|
|
hold: 2.5,
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'gyirong-border-gate',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: {
|
|
imageryComparison: true,
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 2.5,
|
|
deferEvidenceUntilCameraSettled: true,
|
|
evidenceRevealDurationSec: 2.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Timure Evidence Cluster',
|
|
mapStack: 'photoreal',
|
|
lat: 28.285968,
|
|
lon: 85.379136,
|
|
alt: 5032,
|
|
heading: 198,
|
|
pitch: -40,
|
|
roll: 0,
|
|
duration: 4.2,
|
|
hold: 4,
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'timure-cluster',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: {
|
|
imageryComparison: true,
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 4,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Syabru Besi Passage',
|
|
mapStack: 'photoreal',
|
|
lat: 28.210306,
|
|
lon: 85.361527,
|
|
alt: 5893,
|
|
heading: 205,
|
|
pitch: -38,
|
|
roll: 0,
|
|
duration: 4.2,
|
|
hold: 4,
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'syabru-besi',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: {
|
|
imageryComparison: true,
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 4,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bidur / Trishuli Consequence',
|
|
mapStack: 'photoreal',
|
|
lat: 27.981737,
|
|
lon: 85.196273,
|
|
alt: 7422,
|
|
heading: 215,
|
|
pitch: -40,
|
|
roll: 0,
|
|
duration: 4.2,
|
|
hold: 4,
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'bidur-trishuli-bridge',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: {
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 4,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
const BHOTE_KOSHI_NEPAL_APPEND_RECIPE = BHOTE_KOSHI_NEPAL_BASE_RECIPE
|
|
? expandNepalEvidencePack(BHOTE_KOSHI_NEPAL_BASE_RECIPE)
|
|
: null;
|
|
|
|
// Fresh browser origins have no legacy three-shot Nepal project for the
|
|
// append recipe to upgrade. Seed that exact legacy shape so SceneDirector can
|
|
// run the same idempotent bootstrap used by existing browser-saved projects.
|
|
const BHOTE_KOSHI_NEPAL_BOOTSTRAP_RECIPE = BHOTE_KOSHI_NEPAL_APPEND_RECIPE
|
|
? Object.freeze({
|
|
id: 'bhote-koshi-nepal-scene',
|
|
title: msg('Nepal Flood Incident'),
|
|
durationSec: 15,
|
|
style: BHOTE_KOSHI_NEPAL_APPEND_RECIPE.style,
|
|
ui: BHOTE_KOSHI_NEPAL_APPEND_RECIPE.ui,
|
|
layers: BHOTE_KOSHI_NEPAL_APPEND_RECIPE.layers,
|
|
releaseLayerIds: BHOTE_KOSHI_NEPAL_APPEND_RECIPE.releaseLayerIds,
|
|
post: BHOTE_KOSHI_NEPAL_APPEND_RECIPE.post,
|
|
installAlongsideSceneId: 'bhote-koshi-flood',
|
|
installAlongsideFallbackSceneId: 'flights-radar',
|
|
cameraPath:
|
|
BHOTE_KOSHI_NEPAL_APPEND_RECIPE.legacySceneBootstrap.cameraPath
|
|
.slice(0, 3)
|
|
.map((keyframe, index) => ({
|
|
...keyframe,
|
|
// i18n-ignore: legacy shot titles the bootstrap matches verbatim
|
|
title: `Shot ${index + 1}`,
|
|
})),
|
|
})
|
|
: null;
|
|
|
|
const EVENT_RECIPES = [BHOTE_KOSHI_NEPAL_BOOTSTRAP_RECIPE].filter(Boolean);
|
|
|
|
const PUBLIC_SCENE_RECIPES = [
|
|
{
|
|
id: 'flights-radar',
|
|
title: msg('Global Flights Radar'),
|
|
durationSec: 30,
|
|
style: 'retro',
|
|
ui: { hidePanels: true, hudMode: 'minimal', safeFrame: '16:9' },
|
|
layers: {
|
|
flights: true,
|
|
satellites: false,
|
|
earthquakes: false,
|
|
traffic: false,
|
|
},
|
|
post: {
|
|
bloom: 62,
|
|
sharpen: true,
|
|
detectionMode: 'OFF',
|
|
},
|
|
cameraPath: [
|
|
{
|
|
lat: 20.0,
|
|
lon: -30.0,
|
|
alt: 19000000,
|
|
heading: 25,
|
|
pitch: -65,
|
|
roll: 0,
|
|
duration: 6,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 46.0,
|
|
lon: 2.0,
|
|
alt: 8000000,
|
|
heading: 40,
|
|
pitch: -52,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 35.0,
|
|
lon: 139.0,
|
|
alt: 4200000,
|
|
heading: 22,
|
|
pitch: -46,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 37.6,
|
|
lon: -122.4,
|
|
alt: 1700000,
|
|
heading: 8,
|
|
pitch: -40,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 0.0,
|
|
lon: -20.0,
|
|
alt: 12000000,
|
|
heading: -10,
|
|
pitch: -70,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'orbital-watch',
|
|
title: msg('Orbital Watch'),
|
|
durationSec: 32,
|
|
style: 'surveillance',
|
|
ui: { hidePanels: true, hudMode: 'full', safeFrame: '16:9' },
|
|
layers: {
|
|
flights: false,
|
|
satellites: true,
|
|
earthquakes: false,
|
|
traffic: false,
|
|
},
|
|
post: {
|
|
bloom: 58,
|
|
sharpen: false,
|
|
detectionMode: 'SPARSE',
|
|
styleParams: {
|
|
surveillance: {
|
|
gain: 0.62,
|
|
bloom: 0.38,
|
|
scanlineStr: 0.9,
|
|
pixelation: 2.1,
|
|
},
|
|
},
|
|
},
|
|
cameraPath: [
|
|
{
|
|
lat: 28.0,
|
|
lon: -82.0,
|
|
alt: 22000000,
|
|
heading: 0,
|
|
pitch: -82,
|
|
roll: 0,
|
|
duration: 7,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 10.0,
|
|
lon: 20.0,
|
|
alt: 12000000,
|
|
heading: 18,
|
|
pitch: -74,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 35.7,
|
|
lon: 139.7,
|
|
alt: 6500000,
|
|
heading: 24,
|
|
pitch: -64,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 48.8,
|
|
lon: 2.3,
|
|
alt: 3000000,
|
|
heading: 32,
|
|
pitch: -58,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 30.0,
|
|
lon: -25.0,
|
|
alt: 15000000,
|
|
heading: -20,
|
|
pitch: -78,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'thermal-threats',
|
|
title: msg('Thermal Threat Board'),
|
|
durationSec: 26,
|
|
style: 'thermal',
|
|
ui: { hidePanels: true, hudMode: 'full', safeFrame: '16:9' },
|
|
layers: {
|
|
flights: false,
|
|
satellites: false,
|
|
earthquakes: true,
|
|
traffic: false,
|
|
},
|
|
post: {
|
|
bloom: 72,
|
|
sharpen: true,
|
|
detectionMode: 'OFF',
|
|
styleParams: {
|
|
thermal: {
|
|
sensitivity: 0.84,
|
|
bloom: 0.78,
|
|
mode: 0.0,
|
|
pixelation: 1.9,
|
|
},
|
|
},
|
|
},
|
|
cameraPath: [
|
|
{
|
|
lat: 37.0,
|
|
lon: -121.0,
|
|
alt: 5200000,
|
|
heading: 20,
|
|
pitch: -62,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 35.7,
|
|
lon: 140.0,
|
|
alt: 3100000,
|
|
heading: 5,
|
|
pitch: -55,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: -36.8,
|
|
lon: 174.7,
|
|
alt: 2600000,
|
|
heading: -12,
|
|
pitch: -50,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 38.0,
|
|
lon: -10.0,
|
|
alt: 9000000,
|
|
heading: 12,
|
|
pitch: -70,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'city-overload',
|
|
title: msg('City Overload'),
|
|
durationSec: 30,
|
|
style: 'surveillance',
|
|
ui: { hidePanels: true, hudMode: 'minimal', safeFrame: '9:16' },
|
|
layers: {
|
|
flights: true,
|
|
satellites: true,
|
|
earthquakes: false,
|
|
traffic: true,
|
|
},
|
|
post: {
|
|
bloom: 65,
|
|
sharpen: true,
|
|
detectionMode: 'PANOPTIC',
|
|
styleParams: {
|
|
surveillance: {
|
|
gain: 0.68,
|
|
bloom: 0.42,
|
|
scanlineStr: 1.0,
|
|
pixelation: 2.4,
|
|
},
|
|
},
|
|
},
|
|
cameraPath: [
|
|
{
|
|
lat: 40.73,
|
|
lon: -74.0,
|
|
alt: 1500000,
|
|
heading: 25,
|
|
pitch: -44,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 40.73,
|
|
lon: -74.0,
|
|
alt: 550000,
|
|
heading: 58,
|
|
pitch: -36,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 40.73,
|
|
lon: -74.0,
|
|
alt: 260000,
|
|
heading: 102,
|
|
pitch: -33,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 34.05,
|
|
lon: -118.24,
|
|
alt: 1200000,
|
|
heading: 45,
|
|
pitch: -42,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 51.5,
|
|
lon: -0.12,
|
|
alt: 1200000,
|
|
heading: 22,
|
|
pitch: -44,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 0,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'omniscience-pullback',
|
|
title: msg('Omniscience Pullback'),
|
|
durationSec: 36,
|
|
style: 'retro',
|
|
ui: { hidePanels: true, hudMode: 'full', safeFrame: '16:9' },
|
|
layers: {
|
|
flights: true,
|
|
satellites: true,
|
|
earthquakes: true,
|
|
traffic: true,
|
|
},
|
|
post: {
|
|
bloom: 68,
|
|
sharpen: true,
|
|
detectionMode: 'SPARSE',
|
|
styleParams: {
|
|
retro: {
|
|
pixelation: 4.4,
|
|
distortion: 0.42,
|
|
instability: 0.58,
|
|
},
|
|
},
|
|
},
|
|
cameraPath: [
|
|
{
|
|
lat: 35.68,
|
|
lon: 139.76,
|
|
alt: 280000,
|
|
heading: 30,
|
|
pitch: -26,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 35.68,
|
|
lon: 139.76,
|
|
alt: 900000,
|
|
heading: 26,
|
|
pitch: -38,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 35.68,
|
|
lon: 139.76,
|
|
alt: 3800000,
|
|
heading: 18,
|
|
pitch: -52,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 20.0,
|
|
lon: 110.0,
|
|
alt: 9500000,
|
|
heading: 8,
|
|
pitch: -66,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 5.0,
|
|
lon: 30.0,
|
|
alt: 18000000,
|
|
heading: -8,
|
|
pitch: -78,
|
|
roll: 0,
|
|
duration: 6,
|
|
hold: 0,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
/** Build the recipe list without mutating stored user-authored projects. */
|
|
function createSceneRecipes({ localDemoRecipes = EVENT_RECIPES } = {}) {
|
|
return [...localDemoRecipes, ...PUBLIC_SCENE_RECIPES];
|
|
}
|
|
|
|
export const SCENE_RECIPES = createSceneRecipes();
|
|
|
|
export const SCENE_APPEND_RECIPES = BHOTE_KOSHI_NEPAL_APPEND_RECIPE
|
|
? [BHOTE_KOSHI_NEPAL_APPEND_RECIPE]
|
|
: [];
|
|
|
|
function getSceneRecipeById(id) {
|
|
return SCENE_RECIPES.find((recipe) => recipe.id === id) || null;
|
|
}
|
|
|
|
export function getSceneAppendRecipeById(id) {
|
|
return SCENE_APPEND_RECIPES.find((recipe) => recipe.id === id) || null;
|
|
}
|
|
|
|
// Built-in shot titles stay English in recipes and saved projects (upgrades
|
|
// and bindings match them verbatim). These are their display names; a title
|
|
// the user typed shows as typed. Place names inside them stay Latin.
|
|
const BUILTIN_SHOT_TITLES = new Set([
|
|
msg('Global Incident Context'),
|
|
msg('Nepal-Focused Globe Rotation'),
|
|
msg('Bhote Koshi Regional Approach'),
|
|
msg('Bhote Koshi Nearby Cities'),
|
|
msg('Bhote Koshi Incident Corridor'),
|
|
msg('Bhote Koshi Flood Path'),
|
|
msg('Bhote Koshi Corridor Overview'),
|
|
msg('Bhote Koshi Upper Valley'),
|
|
msg('Upper Valley Collapse'),
|
|
msg('Debris-Dammed Lake'),
|
|
msg('Nepal-China Border Gate'),
|
|
msg('Timure Evidence Cluster'),
|
|
msg('Syabru Besi Passage'),
|
|
msg('Bidur / Trishuli Consequence'),
|
|
msg('Second landslide'),
|
|
msg('Mailung / Upper Trishuli bridge'),
|
|
msg('Dandagaun viewpoint restaurant'),
|
|
msg('Devighat / Taadi Khola Bridge'),
|
|
msg('Final view'),
|
|
]);
|
|
|
|
/**
|
|
* Display title for a scene: the localized recipe name while the stored title
|
|
* still matches the built-in verbatim, the stored title otherwise.
|
|
* @param {{id?: string, title?: string}} scene
|
|
* @returns {string}
|
|
*/
|
|
export function displaySceneTitle(scene) {
|
|
const builtin = SCENE_RECIPES.find((recipe) => recipe.id === scene?.id);
|
|
if (builtin?.title && scene.title === builtin.title) return t(builtin.title);
|
|
const title = scene?.title || '';
|
|
// Only the generated "Scene N" localizes; an operator-typed "Scene 007"
|
|
// keeps its digits, so the number stays a string (no Number() round trip).
|
|
const numbered = /^Scene ([1-9]\d*)$/.exec(title);
|
|
return numbered ? t('Scene {n}', { n: numbered[1] }) : title;
|
|
}
|
|
|
|
/**
|
|
* Display title for a shot: built-in titles and the numbered "Shot N"
|
|
* fallback localize; any other title shows as stored.
|
|
*
|
|
* With `shot`, a built-in title localizes only for a recipe shot
|
|
* (`sourcePackId`), so a typed title that matches a built-in one stays as
|
|
* typed. Without `shot`, the title alone decides.
|
|
* @param {string} title
|
|
* @param {{sourcePackId?: string}} [shot]
|
|
* @param {{id?: string}} [scene] The shot's scene, for projects saved before
|
|
* shots recorded their source pack.
|
|
* @returns {string}
|
|
*/
|
|
export function displayShotTitle(title, shot = undefined, scene = undefined) {
|
|
const text = typeof title === 'string' ? title : '';
|
|
const fromRecipe = shot
|
|
? Boolean(shot.sourcePackId) ||
|
|
recipeDeclaresShotTitle(scene?.id, text) ||
|
|
shotPackDeclaresBaseTitle(scene, text)
|
|
: true;
|
|
if (fromRecipe && BUILTIN_SHOT_TITLES.has(text)) return t(text);
|
|
const numbered = /^Shot ([1-9]\d*)$/.exec(text);
|
|
return numbered ? t('Shot {n}', { n: numbered[1] }) : text;
|
|
}
|
|
|
|
/**
|
|
* Whether a shot pack that upgraded this scene declares the title for one of
|
|
* its legacy base shots. The upgrade rebuilds those shots without a
|
|
* sourcePackId (see SceneDirector._bootstrapLegacyShotPacks), so the pack's
|
|
* own shots in the scene vouch for them.
|
|
* @param {{shots?: Array<{sourcePackId?: string}>}} [scene]
|
|
* @param {string} title
|
|
* @returns {boolean}
|
|
*/
|
|
function shotPackDeclaresBaseTitle(scene, title) {
|
|
const shots = Array.isArray(scene?.shots) ? scene.shots : [];
|
|
return SCENE_APPEND_RECIPES.some(
|
|
(pack) =>
|
|
pack.legacySceneBootstrap?.cameraPath?.some(
|
|
(keyframe) => keyframe?.title === title,
|
|
) && shots.some((item) => item?.sourcePackId === pack.id),
|
|
);
|
|
}
|
|
|
|
/** Whether the built-in recipe behind a scene declares a shot with this title. */
|
|
function recipeDeclaresShotTitle(sceneId, title) {
|
|
const recipe = sceneId ? getSceneRecipeById(sceneId) : null;
|
|
return Boolean(
|
|
recipe?.cameraPath?.some((keyframe) => keyframe?.title === title),
|
|
);
|
|
}
|