mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
feat(app): construct the globe engine with the viewer
The scene builds the page's Cesium engine right after the viewer (src/app/globeEngine.js), names the startup Google tileset to it once loaded, returns it beside the viewer and destroys it before the viewer. The debug handle exposes it as window.__godsEyeView.engine. Nothing consumes it yet; render holds taken before the governor is installed apply at install. qa-globe-contract now runs on the app's engine (self-built only when the app has none); a throwaway engine checks creation and destroy leave the scene untouched.
This commit is contained in:
1 parent
ab2368f3f3
commit
a5c98c6b07
7 files changed
+141
-2
No files matched your search
@@ -736,6 +736,8 @@
|
|||||||
"src/globe/cesium/createCesiumEngine.test.mjs",
|
"src/globe/cesium/createCesiumEngine.test.mjs",
|
||||||
"src/globe/cesium/testing/fakeViewer.mjs",
|
"src/globe/cesium/testing/fakeViewer.mjs",
|
||||||
"scripts/qa-globe-contract.mjs",
|
"scripts/qa-globe-contract.mjs",
|
||||||
|
"src/app/globeEngine.js",
|
||||||
|
"src/app/globeEngine.test.mjs",
|
||||||
"src/sources/reference.js",
|
"src/sources/reference.js",
|
||||||
"src/sources/reference.test.mjs",
|
"src/sources/reference.test.mjs",
|
||||||
"src/search/nominatim.js",
|
"src/search/nominatim.js",
|
||||||
|
|||||||
@@ -1737,6 +1737,20 @@
|
|||||||
"src/app/layers/traffic.js",
|
"src/app/layers/traffic.js",
|
||||||
"src/app/operations.js",
|
"src/app/operations.js",
|
||||||
"src/app/scene.js",
|
"src/app/scene.js",
|
||||||
|
"src/app/globeEngine.js",
|
||||||
|
"src/globe/contract.js",
|
||||||
|
"src/globe/cesium/createCesiumEngine.js",
|
||||||
|
"src/globe/cesium/camera.js",
|
||||||
|
"src/globe/cesium/convert.js",
|
||||||
|
"src/globe/cesium/credits.js",
|
||||||
|
"src/globe/cesium/events.js",
|
||||||
|
"src/globe/cesium/frames.js",
|
||||||
|
"src/globe/cesium/input.js",
|
||||||
|
"src/globe/cesium/maps.js",
|
||||||
|
"src/globe/cesium/picker.js",
|
||||||
|
"src/globe/cesium/projector.js",
|
||||||
|
"src/globe/cesium/render.js",
|
||||||
|
"src/globe/cesium/surface.js",
|
||||||
"src/sources/sourceSlot.js",
|
"src/sources/sourceSlot.js",
|
||||||
"src/app/sources.js",
|
"src/app/sources.js",
|
||||||
"src/app/startupChrome.js",
|
"src/app/startupChrome.js",
|
||||||
|
|||||||
@@ -83,6 +83,32 @@ test('startup shares one promise and teardown keeps data alive for controls', as
|
|||||||
await assert.rejects(app.start(), /destroyed/);
|
await assert.rejects(app.start(), /destroyed/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('later components receive the scene engine beside its viewer', async () => {
|
||||||
|
const viewer = { name: 'viewer' };
|
||||||
|
const engine = { kind: 'cesium' };
|
||||||
|
const seen = [];
|
||||||
|
const receive =
|
||||||
|
(phase) =>
|
||||||
|
({ scene }) => {
|
||||||
|
seen.push([phase, scene.viewer, scene.engine]);
|
||||||
|
return { name: phase };
|
||||||
|
};
|
||||||
|
const { app } = fixture({
|
||||||
|
createScene: () => ({ viewer, engine }),
|
||||||
|
createControls: receive('Controls'),
|
||||||
|
createData: receive('Data'),
|
||||||
|
createTools: receive('Tools'),
|
||||||
|
});
|
||||||
|
const components = await app.start();
|
||||||
|
assert.equal(components.scene.engine, engine);
|
||||||
|
assert.deepEqual(seen, [
|
||||||
|
['Controls', viewer, engine],
|
||||||
|
['Data', viewer, engine],
|
||||||
|
['Tools', viewer, engine],
|
||||||
|
]);
|
||||||
|
await app.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
for (const phase of phases) {
|
for (const phase of phases) {
|
||||||
test(`failure during ${phase} releases partially acquired resources`, async () => {
|
test(`failure during ${phase} releases partially acquired resources`, async () => {
|
||||||
const { app, events } = fixture({
|
const { app, events } = fixture({
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { createCesiumEngine } from '../globe/cesium/createCesiumEngine.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the page's one globe engine on the application viewer and tie its
|
||||||
|
* teardown to the scene lifetime. Call after the viewer's own cleanup is
|
||||||
|
* deferred, so the engine is destroyed first. Render holds taken before the
|
||||||
|
* render governor is installed apply when it is (src/renderGovernor.js).
|
||||||
|
* @param {Cesium.Viewer} viewer
|
||||||
|
* @param {{
|
||||||
|
* tilesetRef: { current: object | null } | (() => object | null) | null,
|
||||||
|
* defer: (dispose: () => void) => void,
|
||||||
|
* createEngine?: typeof createCesiumEngine,
|
||||||
|
* }} options
|
||||||
|
* @returns {import('../globe/contract.js').GlobeEngine}
|
||||||
|
*/
|
||||||
|
export function createSceneEngine(
|
||||||
|
viewer,
|
||||||
|
{ tilesetRef, defer, createEngine = createCesiumEngine },
|
||||||
|
) {
|
||||||
|
const engine = createEngine(viewer, { tilesetRef });
|
||||||
|
defer(() => engine.destroy());
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { createSceneEngine } from './globeEngine.js';
|
||||||
|
import { createCesiumEngine } from '../globe/cesium/createCesiumEngine.js';
|
||||||
|
|
||||||
|
const ROOT = path.resolve(
|
||||||
|
path.dirname(fileURLToPath(import.meta.url)),
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
);
|
||||||
|
const read = (rel) => fs.readFileSync(path.join(ROOT, rel), 'utf8');
|
||||||
|
|
||||||
|
test('the scene engine is built on the viewer and destroyed by the scene lifetime', () => {
|
||||||
|
const viewer = { name: 'viewer' };
|
||||||
|
const tilesetRef = { current: null };
|
||||||
|
const cleanups = [];
|
||||||
|
const calls = [];
|
||||||
|
const engine = createSceneEngine(viewer, {
|
||||||
|
tilesetRef,
|
||||||
|
defer: (dispose) => cleanups.push(dispose),
|
||||||
|
createEngine(target, options) {
|
||||||
|
calls.push([target, options]);
|
||||||
|
return { kind: 'cesium', destroy: () => calls.push('destroy') };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
assert.equal(engine.kind, 'cesium');
|
||||||
|
assert.equal(calls.length, 1);
|
||||||
|
assert.equal(calls[0][0], viewer);
|
||||||
|
assert.equal(calls[0][1].tilesetRef, tilesetRef);
|
||||||
|
assert.equal(cleanups.length, 1, 'engine teardown is deferred at once');
|
||||||
|
cleanups[0]();
|
||||||
|
assert.equal(calls.at(-1), 'destroy');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the scene engine defaults to the Cesium engine factory', () => {
|
||||||
|
assert.throws(
|
||||||
|
() => createSceneEngine({}, { tilesetRef: null, defer: () => {} }),
|
||||||
|
/createCesiumEngine needs a viewer/,
|
||||||
|
);
|
||||||
|
assert.equal(typeof createCesiumEngine, 'function');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the scene constructs the engine with the viewer and returns it beside the viewer', () => {
|
||||||
|
const scene = read('src/app/scene.js');
|
||||||
|
const viewerTeardown = scene.indexOf('uninstallRenderGovernor(viewer);');
|
||||||
|
const engineAt = scene.indexOf('createSceneEngine(viewer, {');
|
||||||
|
assert.ok(viewerTeardown >= 0 && engineAt >= 0);
|
||||||
|
// Cleanups run last-in first-out: the engine goes before the viewer.
|
||||||
|
assert.ok(engineAt > viewerTeardown, 'engine teardown must precede viewer');
|
||||||
|
assert.match(scene, /tilesetRef\.current = tileset;/);
|
||||||
|
assert.match(
|
||||||
|
scene,
|
||||||
|
/return \{ viewer, engine, tileset, mapStackController, operations \};/,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the debug handle exposes the engine beside the viewer', () => {
|
||||||
|
const tools = read('src/app/tools.js');
|
||||||
|
assert.match(
|
||||||
|
tools,
|
||||||
|
/const \{ viewer, engine, tileset, mapStackController, operations \} = scene;/,
|
||||||
|
);
|
||||||
|
assert.match(tools, /window\.__godsEyeView = \{\n\s+viewer,\n\s+engine,/);
|
||||||
|
});
|
||||||
+7
-1
@@ -14,6 +14,7 @@ import {
|
|||||||
governorRequestRender,
|
governorRequestRender,
|
||||||
} from '../renderGovernor.js';
|
} from '../renderGovernor.js';
|
||||||
import { describeError } from './errors.js';
|
import { describeError } from './errors.js';
|
||||||
|
import { createSceneEngine } from './globeEngine.js';
|
||||||
|
|
||||||
/** Construct the application globe using the caller's local configuration. */
|
/** Construct the application globe using the caller's local configuration. */
|
||||||
export async function createApplicationScene({
|
export async function createApplicationScene({
|
||||||
@@ -56,6 +57,10 @@ export async function createApplicationScene({
|
|||||||
uninstallRenderGovernor(viewer);
|
uninstallRenderGovernor(viewer);
|
||||||
if (!viewer.isDestroyed()) viewer.destroy();
|
if (!viewer.isDestroyed()) viewer.destroy();
|
||||||
});
|
});
|
||||||
|
// The globe engine over this viewer; Google's startup tileset is named
|
||||||
|
// once it loads.
|
||||||
|
const tilesetRef = { current: null };
|
||||||
|
const engine = createSceneEngine(viewer, { tilesetRef, defer });
|
||||||
defer(installTrackpadPinchZoom(viewer));
|
defer(installTrackpadPinchZoom(viewer));
|
||||||
registerDataCredits(viewer, credits);
|
registerDataCredits(viewer, credits);
|
||||||
configureCreditKeyboardAccess(document);
|
configureCreditKeyboardAccess(document);
|
||||||
@@ -69,6 +74,7 @@ export async function createApplicationScene({
|
|||||||
cesiumToken,
|
cesiumToken,
|
||||||
});
|
});
|
||||||
const tileset = photoreal.tileset;
|
const tileset = photoreal.tileset;
|
||||||
|
tilesetRef.current = tileset;
|
||||||
// A provider can finish after cancellation; retain ownership of its result.
|
// A provider can finish after cancellation; retain ownership of its result.
|
||||||
defer(() => {
|
defer(() => {
|
||||||
if (tileset && !tileset.isDestroyed()) {
|
if (tileset && !tileset.isDestroyed()) {
|
||||||
@@ -121,5 +127,5 @@ export async function createApplicationScene({
|
|||||||
});
|
});
|
||||||
|
|
||||||
signal.throwIfAborted();
|
signal.throwIfAborted();
|
||||||
return { viewer, tileset, mapStackController, operations };
|
return { viewer, engine, tileset, mapStackController, operations };
|
||||||
}
|
}
|
||||||
+2
-1
@@ -28,7 +28,7 @@ export function createApplicationTools({
|
|||||||
signal,
|
signal,
|
||||||
defer,
|
defer,
|
||||||
}) {
|
}) {
|
||||||
const { viewer, tileset, mapStackController, operations } = scene;
|
const { viewer, engine, tileset, mapStackController, operations } = scene;
|
||||||
const { styleManager, weatherEffects, cockpitCloudEffects } = controls;
|
const { styleManager, weatherEffects, cockpitCloudEffects } = controls;
|
||||||
const { dataManager } = data;
|
const { dataManager } = data;
|
||||||
const sceneDirector = new SceneDirector(viewer, styleManager, dataManager, {
|
const sceneDirector = new SceneDirector(viewer, styleManager, dataManager, {
|
||||||
@@ -144,6 +144,7 @@ export function createApplicationTools({
|
|||||||
|
|
||||||
window.__godsEyeView = {
|
window.__godsEyeView = {
|
||||||
viewer,
|
viewer,
|
||||||
|
engine,
|
||||||
styleManager,
|
styleManager,
|
||||||
tileset,
|
tileset,
|
||||||
dataManager,
|
dataManager,
|
||||||
|
|||||||
Reference in new issue
Block a user