mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
Cut duplicated and dead code across the engines, the layers and the QA scripts
- src/geo drops the ported debug argument checks and the tests that bitExact already covers; package boundaries accept a directory entry, so src/geo/ is listed once per group. - Cesium no longer carries movers and lines nobody draws with: they are optional engine capabilities. The fake engine and the Cesium camera use the shared cameraMath; unused contract tables, clock.ticked, the 'finest' surface detail and Navara click statistics are gone. - ALPR keeps one overlay core and one presentation model for both engines, bikeshare one station bookkeeping, the Navara ground effects one definition helper; image atlases, view centres, mover banks and colours are shared. - The legacy switch route, the clock offset and rate controls and other unused exports are removed. - The acceptance runs the Navara draw, geo import and scope checks; scenarios use the harness switch and page helpers; the main chunk check keeps the size budget only and runs once in CI.
This commit is contained in:
1 parent
c502404e1d
commit
7abf74518f
272 files changed
+2618
-8003
No files matched your search
@@ -59,7 +59,8 @@ jobs:
|
|||||||
- name: Build production bundle
|
- name: Build production bundle
|
||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
- name: Main chunk (no three or Navara in Cesium mode; size budget when recorded)
|
- name: Main chunk size budget
|
||||||
|
if: matrix.node == '24.22.2'
|
||||||
run: npm run check:main-chunk
|
run: npm run check:main-chunk
|
||||||
|
|
||||||
street-level-browser:
|
street-level-browser:
|
||||||
@@ -90,9 +91,6 @@ jobs:
|
|||||||
MAPILLARY_CLIENT_TOKEN: 'MLY|0|qa-fixture'
|
MAPILLARY_CLIENT_TOKEN: 'MLY|0|qa-fixture'
|
||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
- name: Main chunk (no three or Navara in Cesium mode; size budget when recorded)
|
|
||||||
run: npm run check:main-chunk
|
|
||||||
|
|
||||||
- name: Start the preview server
|
- name: Start the preview server
|
||||||
env:
|
env:
|
||||||
MAPILLARY_CLIENT_TOKEN: 'MLY|0|qa-fixture'
|
MAPILLARY_CLIENT_TOKEN: 'MLY|0|qa-fixture'
|
||||||
@@ -154,6 +152,3 @@ jobs:
|
|||||||
|
|
||||||
- name: Build production bundle
|
- name: Build production bundle
|
||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
- name: Main chunk (no three or Navara in Cesium mode; size budget when recorded)
|
|
||||||
run: npm run check:main-chunk
|
|
||||||
@@ -52,14 +52,13 @@ resolved dependency inventory remains in `package-lock.json`.
|
|||||||
are adapted from `@cesium/engine/Source/Core` (`Math.js`, `Ellipsoid.js`,
|
are adapted from `@cesium/engine/Source/Core` (`Math.js`, `Ellipsoid.js`,
|
||||||
`scaleToGeodeticSurface.js`, `Cartographic.js`, `Cartesian2.js`,
|
`scaleToGeodeticSurface.js`, `Cartographic.js`, `Cartesian2.js`,
|
||||||
`Cartesian3.js`, `Matrix3.js`, `Matrix4.js`, `Quaternion.js`,
|
`Cartesian3.js`, `Matrix3.js`, `Matrix4.js`, `Quaternion.js`,
|
||||||
`Transforms.js`, `Check.js`, `DeveloperError.js`) and say so in their first
|
`Transforms.js`) and say so in their first
|
||||||
line. The changes: the classes' static functions became module functions,
|
line. The changes: the classes' static functions became module functions,
|
||||||
vectors, geodetic positions and quaternions became plain-data instances of
|
vectors, geodetic positions and quaternions became plain-data instances of
|
||||||
module-private constructors (own enumerable fields only, no methods),
|
module-private constructors (own enumerable fields only, no methods),
|
||||||
matrices plain arrays, and the ellipsoid a frozen object; the debug
|
matrices plain arrays, and the ellipsoid a frozen object; the debug
|
||||||
argument checks were ported with Cesium's conditions and messages and, like
|
argument checks were left out, as in Cesium's release build; only the subset
|
||||||
Cesium's, are active in development and tests and stripped from production
|
gev uses was kept.
|
||||||
builds; only the subset gev uses was kept.
|
|
||||||
- Author: Copyright 2011-2024 CesiumJS Contributors
|
- Author: Copyright 2011-2024 CesiumJS Contributors
|
||||||
- Source: <https://github.com/CesiumGS/cesium>
|
- Source: <https://github.com/CesiumGS/cesium>
|
||||||
- License: Apache License 2.0
|
- License: Apache License 2.0
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
// initial chunks are every entry chunk and what it imports statically;
|
// initial chunks are every entry chunk and what it imports statically;
|
||||||
// navaraMode.js reaches Navara only through import(), so three and
|
// navaraMode.js reaches Navara only through import(), so three and
|
||||||
// @navaramap/* belong to lazy chunks. This checks the bundle's module ids
|
// @navaramap/* belong to lazy chunks. This checks the bundle's module ids
|
||||||
// (what Rollup put in each chunk), and fails the build otherwise;
|
// (what Rollup put in each chunk), and fails the build otherwise.
|
||||||
// scripts/check-main-chunk.mjs checks the built files' text as well.
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module ids that only the Navara engine may load, when no package list is
|
* Module ids that only the Navara engine may load, when no package list is
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ See [application construction](APPLICATION.md) and the
|
|||||||
2. `scripts/check-package-boundaries.mjs` builds every declared export without
|
2. `scripts/check-package-boundaries.mjs` builds every declared export without
|
||||||
app Vite configuration or environment files. `scripts/package-boundaries.json`
|
app Vite configuration or environment files. `scripts/package-boundaries.json`
|
||||||
assigns each export exactly once and lists its owned modules and external
|
assigns each export exactly once and lists its owned modules and external
|
||||||
dependencies. Unused imports still count. Node exports have only a `node`
|
dependencies; a module entry ending in `/` (such as `src/geo/`) owns every
|
||||||
|
file under that directory. Unused imports still count. Node exports have only a `node`
|
||||||
condition; browser groups cannot use build-only dependency exceptions.
|
condition; browser groups cannot use build-only dependency exceptions.
|
||||||
|
|
||||||
Portable source graphs cannot reach application/rendering, Node, Cesium or
|
Portable source graphs cannot reach application/rendering, Node, Cesium or
|
||||||
|
|||||||
+7
-7
@@ -61,21 +61,21 @@
|
|||||||
"@jtarrio/webrtlsdr": "^3.0.6",
|
"@jtarrio/webrtlsdr": "^3.0.6",
|
||||||
"@mapbox/vector-tile": "^3.0.0",
|
"@mapbox/vector-tile": "^3.0.0",
|
||||||
"@meri-imperiumi/eccodes-wasm": "^2.48.2",
|
"@meri-imperiumi/eccodes-wasm": "^2.48.2",
|
||||||
|
"@navaramap/three": "0.2.1",
|
||||||
|
"@navaramap/three-default-plugin": "0.2.1",
|
||||||
"cesium": "^1.124.0",
|
"cesium": "^1.124.0",
|
||||||
"egm96-universal": "^1.1.1",
|
"egm96-universal": "^1.1.1",
|
||||||
"hls.js": "^1.7.3",
|
"hls.js": "^1.7.3",
|
||||||
|
"kdbush": "4.0.2",
|
||||||
"mapillary-js": "^4.1.2",
|
"mapillary-js": "^4.1.2",
|
||||||
"mgrs": "^2.1.0",
|
"mgrs": "^2.1.0",
|
||||||
"pbf": "^5.1.2",
|
"pbf": "^5.1.2",
|
||||||
"pinia": "^2.3.1",
|
"pinia": "^2.3.1",
|
||||||
"satellite.js": "^6.0.2",
|
|
||||||
"vue": "^3.5.44",
|
|
||||||
"vue-i18n": "^11.4.15",
|
|
||||||
"@navaramap/three": "0.2.1",
|
|
||||||
"@navaramap/three-default-plugin": "0.2.1",
|
|
||||||
"kdbush": "4.0.2",
|
|
||||||
"postprocessing": "6.39.4",
|
"postprocessing": "6.39.4",
|
||||||
"three": "0.185.1"
|
"satellite.js": "^6.0.2",
|
||||||
|
"three": "0.185.1",
|
||||||
|
"vue": "^3.5.44",
|
||||||
|
"vue-i18n": "^11.4.15"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@happy-dom/global-registrator": "^20.14.6",
|
"@happy-dom/global-registrator": "^20.14.6",
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ const globeEngine = (file) => /^src\/globe\/(?:cesium|navara)\//.test(file);
|
|||||||
const cesiumPackage = /^(?:cesium(?:\/|$)|@cesium\/)/;
|
const cesiumPackage = /^(?:cesium(?:\/|$)|@cesium\/)/;
|
||||||
// Navara renders through three.js and its postprocessing peer.
|
// Navara renders through three.js and its postprocessing peer.
|
||||||
const navaraPackage = /^(?:(?:three|postprocessing)(?:\/|$)|@navaramap\/)/;
|
const navaraPackage = /^(?:(?:three|postprocessing)(?:\/|$)|@navaramap\/)/;
|
||||||
// Phase 3: the layers moved onto layer renderers (the *a tasks) keep each
|
// Layers built on layer renderers keep each engine in their renderers:
|
||||||
// engine in their renderers: cesium only in render/cesium*.js, three and
|
// cesium only in render/cesium*.js, three and Navara only in
|
||||||
// Navara only in render/navara*.js. The list grows with each moved layer.
|
// render/navara*.js. The list grows with each layer moved onto renderers.
|
||||||
const RENDERED_LAYER_DIRS = [
|
const RENDERED_LAYER_DIRS = [
|
||||||
'src/layers/alpr/',
|
'src/layers/alpr/',
|
||||||
'src/layers/airQuality/',
|
'src/layers/airQuality/',
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
/**
|
/**
|
||||||
* The main-chunk gate of the Cesium-mode bundle (run after `npm run build`):
|
* The size budget of the Cesium-mode main chunk (run after `npm run build`):
|
||||||
|
* gzip-9 of the entry chunk the built page loads (from the Vite manifest, or
|
||||||
|
* the module script dist/index.html loads) may exceed the baseline in
|
||||||
|
* scripts/main-chunk-baseline.json by at most its allowance. What the initial
|
||||||
|
* chunks may hold is the build's own check (build/initialChunkGuard.js).
|
||||||
*
|
*
|
||||||
* 1. Composition: the main chunk (the Vite manifest's entry, or the module
|
* Prints the size. `--json` prints the whole result; `--dist=DIR` reads
|
||||||
* script dist/index.html loads) and every chunk it imports statically carry
|
|
||||||
* no three or Navara code (text markers; the build itself also fails on
|
|
||||||
* three/@navaramap module ids in initial chunks, build/initialChunkGuard.js).
|
|
||||||
* 2. Budget, when scripts/main-chunk-baseline.json exists: gzip-9 of the
|
|
||||||
* largest dist/assets/index-*.js may exceed the recorded baseline by at
|
|
||||||
* most its allowance (the phase-4 branch's check; same exports,
|
|
||||||
* checkMainChunk and measureMainChunk, so the two merge into this script).
|
|
||||||
*
|
|
||||||
* Prints the sizes. `--json` prints the whole result; `--dist=DIR` reads
|
|
||||||
* another build.
|
* another build.
|
||||||
*
|
*
|
||||||
* npm run build && npm run check:main-chunk
|
* npm run build && npm run check:main-chunk
|
||||||
@@ -20,12 +15,7 @@ import fs from 'node:fs';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { gzipSync } from 'node:zlib';
|
import { gzipSync } from 'node:zlib';
|
||||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||||
import {
|
import { entryFromHtml, entryFromManifest } from '../src/tooling/mainChunk.js';
|
||||||
entryFromHtml,
|
|
||||||
entryFromManifest,
|
|
||||||
forbiddenMarkersIn,
|
|
||||||
initialChunks,
|
|
||||||
} from '../src/tooling/mainChunk.js';
|
|
||||||
|
|
||||||
const ROOT = fileURLToPath(new URL('..', import.meta.url));
|
const ROOT = fileURLToPath(new URL('..', import.meta.url));
|
||||||
|
|
||||||
@@ -44,40 +34,16 @@ export function checkMainChunk(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* gzip-9 size of the main chunk, with its name: the entry the built page
|
* gzip-9 size of the main chunk, with its name: the entry the built page
|
||||||
* loads (`entry`, or found from dist/index.html or the Vite manifest), else
|
* loads (`entry`, else found from the Vite manifest or dist/index.html).
|
||||||
* (phase 4's rule, for a dist without a page) the largest
|
|
||||||
* dist/assets/index-*.js.
|
|
||||||
*/
|
*/
|
||||||
export function measureMainChunk(distDir, { entry = null } = {}) {
|
export function measureMainChunk(distDir, { entry = null } = {}) {
|
||||||
let file = entry;
|
const file = entry ?? findEntry(distDir).entry;
|
||||||
if (!file) {
|
|
||||||
try {
|
|
||||||
file = findEntry(distDir).entry;
|
|
||||||
} catch {
|
|
||||||
file = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (file) {
|
|
||||||
const bytes = fs.readFileSync(path.join(distDir, file));
|
const bytes = fs.readFileSync(path.join(distDir, file));
|
||||||
return {
|
return {
|
||||||
name: path.basename(file),
|
name: path.basename(file),
|
||||||
gzipBytes: gzipSync(bytes, { level: 9 }).length,
|
gzipBytes: gzipSync(bytes, { level: 9 }).length,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const assets = path.join(distDir, 'assets');
|
|
||||||
const candidates = fs
|
|
||||||
.readdirSync(assets)
|
|
||||||
.filter((name) => /^index-.*\.js$/.test(name))
|
|
||||||
.map((name) => ({ name, bytes: fs.readFileSync(path.join(assets, name)) }))
|
|
||||||
.sort((a, b) => b.bytes.length - a.bytes.length);
|
|
||||||
if (!candidates.length)
|
|
||||||
throw new Error('no dist/assets/index-*.js: run npm run build first');
|
|
||||||
const [main] = candidates;
|
|
||||||
return {
|
|
||||||
name: main.name,
|
|
||||||
gzipBytes: gzipSync(main.bytes, { level: 9 }).length,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The entry chunk (relative to dist) and how it was found. */
|
/** The entry chunk (relative to dist) and how it was found. */
|
||||||
function findEntry(dist) {
|
function findEntry(dist) {
|
||||||
@@ -96,44 +62,15 @@ function findEntry(dist) {
|
|||||||
return { entry, via: 'index.html' };
|
return { entry, via: 'index.html' };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Composition of the initial chunks: sizes and forbidden markers. */
|
|
||||||
export function checkComposition(dist) {
|
|
||||||
const read = (file) => {
|
|
||||||
const absolute = path.join(dist, file);
|
|
||||||
return fs.existsSync(absolute) ? fs.readFileSync(absolute, 'utf8') : null;
|
|
||||||
};
|
|
||||||
const { entry, via } = findEntry(dist);
|
|
||||||
const chunks = initialChunks(entry, read).map((file) => {
|
|
||||||
const code = read(file);
|
|
||||||
if (code == null)
|
|
||||||
return { file, missing: true, bytes: 0, gzipBytes: 0, markers: [] };
|
|
||||||
return {
|
|
||||||
file,
|
|
||||||
bytes: Buffer.byteLength(code),
|
|
||||||
gzipBytes: gzipSync(code).length,
|
|
||||||
markers: forbiddenMarkersIn(code),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
entry,
|
|
||||||
via,
|
|
||||||
mainBytes: chunks[0].bytes,
|
|
||||||
mainGzipBytes: chunks[0].gzipBytes,
|
|
||||||
initialBytes: chunks.reduce((sum, chunk) => sum + chunk.bytes, 0),
|
|
||||||
chunks,
|
|
||||||
ok: chunks.every((chunk) => !chunk.missing && !chunk.markers.length),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
const option = (name) =>
|
const option = (name) =>
|
||||||
process.argv.find((arg) => arg.startsWith(`--${name}=`))?.split('=')[1];
|
process.argv.find((arg) => arg.startsWith(`--${name}=`))?.split('=')[1];
|
||||||
const dist = path.resolve(ROOT, option('dist') ?? 'dist');
|
const dist = path.resolve(ROOT, option('dist') ?? 'dist');
|
||||||
const composition = checkComposition(dist);
|
const { entry, via } = findEntry(dist);
|
||||||
const baselinePath = path.join(ROOT, 'scripts/main-chunk-baseline.json');
|
const baselinePath = path.join(ROOT, 'scripts/main-chunk-baseline.json');
|
||||||
let budget = null;
|
let budget = null;
|
||||||
if (fs.existsSync(baselinePath)) {
|
if (fs.existsSync(baselinePath)) {
|
||||||
const measured = measureMainChunk(dist, { entry: composition.entry });
|
const measured = measureMainChunk(dist, { entry });
|
||||||
budget = {
|
budget = {
|
||||||
...measured,
|
...measured,
|
||||||
...checkMainChunk(
|
...checkMainChunk(
|
||||||
@@ -142,28 +79,11 @@ function main() {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const ok = composition.ok && (budget?.ok ?? true);
|
const ok = budget?.ok ?? true;
|
||||||
if (process.argv.includes('--json')) {
|
if (process.argv.includes('--json')) {
|
||||||
console.log(JSON.stringify({ composition, budget, ok }, null, 2));
|
console.log(JSON.stringify({ entry, via, budget, ok }, null, 2));
|
||||||
} else {
|
} else {
|
||||||
const kb = (bytes) => `${(bytes / 1000).toFixed(2)} kB`;
|
console.log(`[check:main-chunk] entry ${entry} (via ${via})`);
|
||||||
console.log(
|
|
||||||
`[check:main-chunk] entry ${composition.entry} (via ${composition.via})`,
|
|
||||||
);
|
|
||||||
for (const chunk of composition.chunks)
|
|
||||||
console.log(
|
|
||||||
`[check:main-chunk] ${chunk.file}: ${kb(chunk.bytes)} (gzip ${kb(chunk.gzipBytes)})` +
|
|
||||||
(chunk.missing ? ' MISSING' : '') +
|
|
||||||
(chunk.markers.length
|
|
||||||
? ` FORBIDDEN: ${chunk.markers.join(', ')}`
|
|
||||||
: ''),
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`[check:main-chunk] main ${composition.mainBytes} bytes (gzip ${composition.mainGzipBytes}); initial chunks ${composition.initialBytes} bytes; ` +
|
|
||||||
(composition.ok
|
|
||||||
? 'no three or Navara'
|
|
||||||
: 'three or Navara in the initial chunks'),
|
|
||||||
);
|
|
||||||
console.log(
|
console.log(
|
||||||
budget
|
budget
|
||||||
? `[check:main-chunk] ${budget.name}: ${budget.message}`
|
? `[check:main-chunk] ${budget.name}: ${budget.message}`
|
||||||
|
|||||||
@@ -37,7 +37,11 @@ export async function checkPackageBoundaries(root) {
|
|||||||
) {
|
) {
|
||||||
throw new Error(`Invalid package boundary: ${name}`);
|
throw new Error(`Invalid package boundary: ${name}`);
|
||||||
}
|
}
|
||||||
|
// A module entry ending in "/" owns every file under that directory.
|
||||||
const allowed = new Set();
|
const allowed = new Set();
|
||||||
|
const allowedDirectories = [];
|
||||||
|
const owns = (id) =>
|
||||||
|
allowed.has(id) || allowedDirectories.some((dir) => id.startsWith(dir));
|
||||||
for (const module of group.modules) {
|
for (const module of group.modules) {
|
||||||
if (
|
if (
|
||||||
typeof module !== 'string' ||
|
typeof module !== 'string' ||
|
||||||
@@ -56,8 +60,12 @@ export async function checkPackageBoundaries(root) {
|
|||||||
) {
|
) {
|
||||||
throw new Error(`Boundary module escapes repository: ${name}`);
|
throw new Error(`Boundary module escapes repository: ${name}`);
|
||||||
}
|
}
|
||||||
|
if (module.endsWith('/')) {
|
||||||
|
allowedDirectories.push(`${normalizePath(resolved)}/`);
|
||||||
|
} else {
|
||||||
allowed.add(normalizePath(resolved));
|
allowed.add(normalizePath(resolved));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for (const external of group.external) {
|
for (const external of group.external) {
|
||||||
if (
|
if (
|
||||||
!Object.hasOwn(pkg.dependencies || {}, external) &&
|
!Object.hasOwn(pkg.dependencies || {}, external) &&
|
||||||
@@ -82,7 +90,7 @@ export async function checkPackageBoundaries(root) {
|
|||||||
if (
|
if (
|
||||||
typeof target !== 'string' ||
|
typeof target !== 'string' ||
|
||||||
!target.startsWith('./') ||
|
!target.startsWith('./') ||
|
||||||
!allowed.has(normalizePath(path.resolve(root, target)))
|
!owns(normalizePath(path.resolve(root, target)))
|
||||||
) {
|
) {
|
||||||
throw new Error(`Export must point to an owned module: ${key}`);
|
throw new Error(`Export must point to an owned module: ${key}`);
|
||||||
}
|
}
|
||||||
@@ -109,7 +117,7 @@ export async function checkPackageBoundaries(root) {
|
|||||||
// template blocks belong to the component file itself.
|
// template blocks belong to the component file itself.
|
||||||
if (info.id.startsWith('\0')) return;
|
if (info.id.startsWith('\0')) return;
|
||||||
const id = info.id.split('?')[0];
|
const id = info.id.split('?')[0];
|
||||||
if (!allowed.has(id)) {
|
if (!owns(id)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Package boundary ${name} imports an unowned module: ${path.relative(root, id)}`,
|
`Package boundary ${name} imports an unowned module: ${path.relative(root, id)}`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -589,6 +589,8 @@
|
|||||||
"src/layers/alpr/model.js",
|
"src/layers/alpr/model.js",
|
||||||
"src/layers/alpr/policy.js",
|
"src/layers/alpr/policy.js",
|
||||||
"src/layers/alpr/visuals.js",
|
"src/layers/alpr/visuals.js",
|
||||||
|
"src/layers/alpr/overlay.js",
|
||||||
|
"src/layers/alpr/presentation.js",
|
||||||
"src/layers/alpr/overlay.test.mjs",
|
"src/layers/alpr/overlay.test.mjs",
|
||||||
"src/layers/alpr/source.js",
|
"src/layers/alpr/source.js",
|
||||||
"src/layers/alpr/source.test.mjs",
|
"src/layers/alpr/source.test.mjs",
|
||||||
@@ -1217,6 +1219,7 @@
|
|||||||
"src/layers/aircraft/render/contract.js",
|
"src/layers/aircraft/render/contract.js",
|
||||||
"src/layers/shared/color.test.mjs",
|
"src/layers/shared/color.test.mjs",
|
||||||
"src/layers/shared/color.js",
|
"src/layers/shared/color.js",
|
||||||
|
"src/layers/shared/viewRectangle.js",
|
||||||
"src/layers/military/render/cesium.js",
|
"src/layers/military/render/cesium.js",
|
||||||
"src/layers/military/render/navara.js",
|
"src/layers/military/render/navara.js",
|
||||||
"src/layers/aircraft/qaDrawn.js",
|
"src/layers/aircraft/qaDrawn.js",
|
||||||
@@ -1225,13 +1228,18 @@
|
|||||||
"src/layers/vessels/render/cesium.js",
|
"src/layers/vessels/render/cesium.js",
|
||||||
"src/layers/vessels/render/contract.js",
|
"src/layers/vessels/render/contract.js",
|
||||||
"src/layers/vessels/renderer.test.mjs",
|
"src/layers/vessels/renderer.test.mjs",
|
||||||
|
"src/layers/vessels/testFixtures.mjs",
|
||||||
"src/layers/vessels/render/navara.js",
|
"src/layers/vessels/render/navara.js",
|
||||||
"src/layers/shared/render/engineView.js",
|
"src/layers/shared/render/engineView.js",
|
||||||
|
"src/layers/shared/render/navaraGroundEffect.js",
|
||||||
|
"src/layers/shared/render/navaraMoverBank.js",
|
||||||
|
"src/layers/shared/render/imageAtlas.js",
|
||||||
"src/layers/installations/render/cesium/lifecycle.js",
|
"src/layers/installations/render/cesium/lifecycle.js",
|
||||||
"src/layers/installations/render/cesium/rendering.js",
|
"src/layers/installations/render/cesium/rendering.js",
|
||||||
"src/layers/installations/render/cesium/selection.js",
|
"src/layers/installations/render/cesium/selection.js",
|
||||||
"src/layers/bikeshare/render/cesium/lifecycle.js",
|
"src/layers/bikeshare/render/cesium/lifecycle.js",
|
||||||
"src/layers/bikeshare/presentation.js",
|
"src/layers/bikeshare/presentation.js",
|
||||||
|
"src/layers/bikeshare/stationPoints.js",
|
||||||
"src/layers/bikeshare/presentation.test.mjs",
|
"src/layers/bikeshare/presentation.test.mjs",
|
||||||
"src/layers/bikeshare/selectionModel.js",
|
"src/layers/bikeshare/selectionModel.js",
|
||||||
"src/layers/bikeshare/render/cesium.js",
|
"src/layers/bikeshare/render/cesium.js",
|
||||||
@@ -1329,8 +1337,8 @@
|
|||||||
"src/globe/cesium/render.js",
|
"src/globe/cesium/render.js",
|
||||||
"src/globe/cesium/surface.js",
|
"src/globe/cesium/surface.js",
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/globe/navara/cameraMath.js",
|
"src/globe/cameraMath.js",
|
||||||
"src/globe/navara/cameraMath.test.mjs",
|
"src/globe/cameraMath.test.mjs",
|
||||||
"src/globe/navara/cameraModel.js",
|
"src/globe/navara/cameraModel.js",
|
||||||
"src/globe/navara/cameraModel.test.mjs",
|
"src/globe/navara/cameraModel.test.mjs",
|
||||||
"src/globe/navara/createNavaraEngine.js",
|
"src/globe/navara/createNavaraEngine.js",
|
||||||
@@ -1364,14 +1372,12 @@
|
|||||||
"src/tooling/mainChunkComposition.test.mjs",
|
"src/tooling/mainChunkComposition.test.mjs",
|
||||||
"src/tooling/initialChunkGuard.test.mjs",
|
"src/tooling/initialChunkGuard.test.mjs",
|
||||||
"src/globe/movers.test.mjs",
|
"src/globe/movers.test.mjs",
|
||||||
"src/globe/cesium/movers.test.mjs",
|
|
||||||
"src/globe/navara/movers/pack.test.mjs",
|
"src/globe/navara/movers/pack.test.mjs",
|
||||||
"src/globe/navara/movers/pickIds.test.mjs",
|
"src/globe/navara/movers/pickIds.test.mjs",
|
||||||
"src/globe/navara/movers/moverMesh.test.mjs",
|
"src/globe/navara/movers/moverMesh.test.mjs",
|
||||||
"src/globe/navara/movers/modelMover.test.mjs",
|
"src/globe/navara/movers/modelMover.test.mjs",
|
||||||
"src/bench/syntheticFlights.test.mjs",
|
"src/bench/syntheticFlights.test.mjs",
|
||||||
"src/globe/lines.test.mjs",
|
"src/globe/lines.test.mjs",
|
||||||
"src/globe/cesium/lines.test.mjs",
|
|
||||||
"src/globe/navara/lines/lineMesh.test.mjs",
|
"src/globe/navara/lines/lineMesh.test.mjs",
|
||||||
"src/globe/pickDelivery.test.mjs",
|
"src/globe/pickDelivery.test.mjs",
|
||||||
"src/globe/navara/picking.test.mjs",
|
"src/globe/navara/picking.test.mjs",
|
||||||
|
|||||||
+47
-534
@@ -897,24 +897,7 @@
|
|||||||
"src/i18n/locales/zh-CN.json",
|
"src/i18n/locales/zh-CN.json",
|
||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/ui/unitLabels.js",
|
"src/ui/unitLabels.js",
|
||||||
"src/geo/index.js",
|
"src/geo/"
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js"
|
|
||||||
],
|
],
|
||||||
"external": ["cesium"]
|
"external": ["cesium"]
|
||||||
},
|
},
|
||||||
@@ -1176,24 +1159,7 @@
|
|||||||
"src/globe/cesium/engineFromViewer.js",
|
"src/globe/cesium/engineFromViewer.js",
|
||||||
"src/cameraAngles.js",
|
"src/cameraAngles.js",
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/app/layers/directory.js",
|
"src/app/layers/directory.js",
|
||||||
"src/globe/follow.js",
|
"src/globe/follow.js",
|
||||||
"src/ui/kit/UiSegmented.vue"
|
"src/ui/kit/UiSegmented.vue"
|
||||||
@@ -1333,24 +1299,7 @@
|
|||||||
"src/ui/i18n.js",
|
"src/ui/i18n.js",
|
||||||
"src/globe/cesium/engineFromViewer.js",
|
"src/globe/cesium/engineFromViewer.js",
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/geo/index.js",
|
"src/geo/"
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js"
|
|
||||||
],
|
],
|
||||||
"external": ["cesium", "vue", "pinia", "vue-i18n"]
|
"external": ["cesium", "vue", "pinia", "vue-i18n"]
|
||||||
},
|
},
|
||||||
@@ -1432,24 +1381,7 @@
|
|||||||
"src/ui/unitLabels.js",
|
"src/ui/unitLabels.js",
|
||||||
"src/data/render/cesiumProjection.js",
|
"src/data/render/cesiumProjection.js",
|
||||||
"src/data/render/cesiumConstants.js",
|
"src/data/render/cesiumConstants.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/time/clock.js",
|
"src/time/clock.js",
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/globe/cesium/engineFromViewer.js",
|
"src/globe/cesium/engineFromViewer.js",
|
||||||
@@ -1516,24 +1448,7 @@
|
|||||||
"src/data/render/cesiumProjection.js",
|
"src/data/render/cesiumProjection.js",
|
||||||
"src/data/render/cesiumConstants.js",
|
"src/data/render/cesiumConstants.js",
|
||||||
"src/layers/shared/color.js",
|
"src/layers/shared/color.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/time/clock.js",
|
"src/time/clock.js",
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/globe/cesium/engineFromViewer.js",
|
"src/globe/cesium/engineFromViewer.js",
|
||||||
@@ -1571,24 +1486,7 @@
|
|||||||
"src/data/inputOwnership.js",
|
"src/data/inputOwnership.js",
|
||||||
"src/data/render/cesiumProjection.js",
|
"src/data/render/cesiumProjection.js",
|
||||||
"src/data/render/cesiumConstants.js",
|
"src/data/render/cesiumConstants.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/globe/cesium/engineFromViewer.js"
|
"src/globe/cesium/engineFromViewer.js"
|
||||||
],
|
],
|
||||||
@@ -1635,24 +1533,7 @@
|
|||||||
"src/layers/vessels/icons.js",
|
"src/layers/vessels/icons.js",
|
||||||
"src/layers/shared/color.js",
|
"src/layers/shared/color.js",
|
||||||
"src/data/render/cesiumProjection.js",
|
"src/data/render/cesiumProjection.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/globe/cesium/engineFromViewer.js"
|
"src/globe/cesium/engineFromViewer.js"
|
||||||
],
|
],
|
||||||
@@ -1665,24 +1546,7 @@
|
|||||||
"src/layers/earthquakes/model.js",
|
"src/layers/earthquakes/model.js",
|
||||||
"src/layers/earthquakes/source.js",
|
"src/layers/earthquakes/source.js",
|
||||||
"src/layers/earthquakes/records.js",
|
"src/layers/earthquakes/records.js",
|
||||||
"src/geo/boundingSphere.js",
|
"src/geo/",
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/index.js",
|
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/layers/shared/color.js"
|
"src/layers/shared/color.js"
|
||||||
],
|
],
|
||||||
"external": []
|
"external": []
|
||||||
@@ -1751,24 +1615,7 @@
|
|||||||
"src/i18n/locales/uk.json",
|
"src/i18n/locales/uk.json",
|
||||||
"src/i18n/locales/zh-CN.json",
|
"src/i18n/locales/zh-CN.json",
|
||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/layers/firms/rectangle.js",
|
"src/layers/firms/rectangle.js",
|
||||||
"src/layers/shared/color.js"
|
"src/layers/shared/color.js"
|
||||||
],
|
],
|
||||||
@@ -1810,24 +1657,7 @@
|
|||||||
"src/i18n/locales/zh-CN.json",
|
"src/i18n/locales/zh-CN.json",
|
||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/ui/unitLabels.js",
|
"src/ui/unitLabels.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/time/clock.js",
|
"src/time/clock.js",
|
||||||
"src/layers/shared/color.js"
|
"src/layers/shared/color.js"
|
||||||
],
|
],
|
||||||
@@ -1872,24 +1702,7 @@
|
|||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/layers/shared/color.js",
|
"src/layers/shared/color.js",
|
||||||
"src/layers/launches/render/cesium.js",
|
"src/layers/launches/render/cesium.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/time/clock.js",
|
"src/time/clock.js",
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/globe/cesium/engineFromViewer.js"
|
"src/globe/cesium/engineFromViewer.js"
|
||||||
@@ -1946,24 +1759,7 @@
|
|||||||
"src/layers/installations/render/cesium/selection.js",
|
"src/layers/installations/render/cesium/selection.js",
|
||||||
"src/layers/shared/color.js",
|
"src/layers/shared/color.js",
|
||||||
"src/layers/installations/render/cesium/namedMarkers.js",
|
"src/layers/installations/render/cesium/namedMarkers.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/globe/cesium/engineFromViewer.js"
|
"src/globe/cesium/engineFromViewer.js"
|
||||||
],
|
],
|
||||||
@@ -2015,24 +1811,7 @@
|
|||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/ui/unitLabels.js",
|
"src/ui/unitLabels.js",
|
||||||
"src/data/render/cesiumProjection.js",
|
"src/data/render/cesiumProjection.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/globe/cesium/engineFromViewer.js"
|
"src/globe/cesium/engineFromViewer.js"
|
||||||
],
|
],
|
||||||
@@ -2094,24 +1873,7 @@
|
|||||||
"src/i18n/locales/uk.json",
|
"src/i18n/locales/uk.json",
|
||||||
"src/i18n/locales/zh-CN.json",
|
"src/i18n/locales/zh-CN.json",
|
||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/globe/cesium/engineFromViewer.js"
|
"src/globe/cesium/engineFromViewer.js"
|
||||||
],
|
],
|
||||||
@@ -2151,24 +1913,7 @@
|
|||||||
"src/layers/bikeshare/selectionModel.js",
|
"src/layers/bikeshare/selectionModel.js",
|
||||||
"src/layers/bikeshare/presentation.js",
|
"src/layers/bikeshare/presentation.js",
|
||||||
"src/layers/shared/color.js",
|
"src/layers/shared/color.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/globe/cesium/engineFromViewer.js"
|
"src/globe/cesium/engineFromViewer.js"
|
||||||
],
|
],
|
||||||
@@ -2227,24 +1972,7 @@
|
|||||||
"src/i18n/locales/zh-CN.json",
|
"src/i18n/locales/zh-CN.json",
|
||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/layers/shared/color.js",
|
"src/layers/shared/color.js",
|
||||||
"src/geo/index.js",
|
"src/geo/"
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js"
|
|
||||||
],
|
],
|
||||||
"external": ["hls.js"]
|
"external": ["hls.js"]
|
||||||
},
|
},
|
||||||
@@ -2318,24 +2046,7 @@
|
|||||||
"src/i18n/locales/zh-CN.json",
|
"src/i18n/locales/zh-CN.json",
|
||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/layers/streetLevel/providers/mapillary/policy.js",
|
"src/layers/streetLevel/providers/mapillary/policy.js",
|
||||||
"src/geo/index.js",
|
"src/geo/"
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js"
|
|
||||||
],
|
],
|
||||||
"external": ["cesium", "pbf", "@mapbox/vector-tile", "mapillary-js"]
|
"external": ["cesium", "pbf", "@mapbox/vector-tile", "mapillary-js"]
|
||||||
},
|
},
|
||||||
@@ -2371,24 +2082,7 @@
|
|||||||
"src/i18n/locales/uk.json",
|
"src/i18n/locales/uk.json",
|
||||||
"src/i18n/locales/zh-CN.json",
|
"src/i18n/locales/zh-CN.json",
|
||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/geo/index.js",
|
"src/geo/"
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js"
|
|
||||||
],
|
],
|
||||||
"external": ["@mapbox/vector-tile", "pbf"]
|
"external": ["@mapbox/vector-tile", "pbf"]
|
||||||
},
|
},
|
||||||
@@ -2437,24 +2131,7 @@
|
|||||||
"src/i18n/locales/uk.json",
|
"src/i18n/locales/uk.json",
|
||||||
"src/i18n/locales/zh-CN.json",
|
"src/i18n/locales/zh-CN.json",
|
||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/geo/index.js",
|
"src/geo/"
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js"
|
|
||||||
],
|
],
|
||||||
"external": []
|
"external": []
|
||||||
},
|
},
|
||||||
@@ -2486,24 +2163,7 @@
|
|||||||
"src/i18n/locales/uk.json",
|
"src/i18n/locales/uk.json",
|
||||||
"src/i18n/locales/zh-CN.json",
|
"src/i18n/locales/zh-CN.json",
|
||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/geo/index.js",
|
"src/geo/"
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js"
|
|
||||||
],
|
],
|
||||||
"external": ["cesium"]
|
"external": ["cesium"]
|
||||||
},
|
},
|
||||||
@@ -3692,7 +3352,7 @@
|
|||||||
"src/globe/cesium/render.js",
|
"src/globe/cesium/render.js",
|
||||||
"src/globe/cesium/surface.js",
|
"src/globe/cesium/surface.js",
|
||||||
"src/globe/cesium/engineFromViewer.js",
|
"src/globe/cesium/engineFromViewer.js",
|
||||||
"src/globe/navara/cameraMath.js",
|
"src/globe/cameraMath.js",
|
||||||
"src/globe/navara/cameraModel.js",
|
"src/globe/navara/cameraModel.js",
|
||||||
"src/globe/navara/createNavaraEngine.js",
|
"src/globe/navara/createNavaraEngine.js",
|
||||||
"src/globe/navara/credits.js",
|
"src/globe/navara/credits.js",
|
||||||
@@ -3722,6 +3382,8 @@
|
|||||||
"src/data/render/cesiumTrailRenderer.js",
|
"src/data/render/cesiumTrailRenderer.js",
|
||||||
"src/layers/alpr/render/cesiumPresentation.js",
|
"src/layers/alpr/render/cesiumPresentation.js",
|
||||||
"src/layers/alpr/render/cesiumOverlay.js",
|
"src/layers/alpr/render/cesiumOverlay.js",
|
||||||
|
"src/layers/alpr/overlay.js",
|
||||||
|
"src/layers/alpr/presentation.js",
|
||||||
"src/layers/alpr/render/cesium.js",
|
"src/layers/alpr/render/cesium.js",
|
||||||
"src/layers/bikeshare/selectionModel.js",
|
"src/layers/bikeshare/selectionModel.js",
|
||||||
"src/layers/bikeshare/presentation.js",
|
"src/layers/bikeshare/presentation.js",
|
||||||
@@ -3742,35 +3404,16 @@
|
|||||||
"src/layers/localAdsb/qaDrawn.js",
|
"src/layers/localAdsb/qaDrawn.js",
|
||||||
"src/ui/imageryBoxNavara.js",
|
"src/ui/imageryBoxNavara.js",
|
||||||
"src/layers/installations/render/cesium/namedMarkers.js",
|
"src/layers/installations/render/cesium/namedMarkers.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/time/clock.js",
|
"src/time/clock.js",
|
||||||
"src/globe/navara/overlay.js",
|
"src/globe/navara/overlay.js",
|
||||||
"src/globe/navara/frameDescriptor.js",
|
"src/globe/navara/frameDescriptor.js",
|
||||||
"src/globe/cesium/clock.js",
|
"src/globe/cesium/clock.js",
|
||||||
"src/globe/pickDelivery.js",
|
"src/globe/pickDelivery.js",
|
||||||
"src/globe/navara/clock.js",
|
"src/globe/navara/clock.js",
|
||||||
"src/globe/cesium/lines.js",
|
|
||||||
"src/globe/follow.js",
|
"src/globe/follow.js",
|
||||||
"src/globe/lines.js",
|
"src/globe/lines.js",
|
||||||
"src/globe/navara/movers/pickIds.js",
|
"src/globe/navara/movers/pickIds.js",
|
||||||
"src/globe/cesium/movers.js",
|
|
||||||
"src/globe/movers.js",
|
"src/globe/movers.js",
|
||||||
"src/globe/cesium/follow.js",
|
"src/globe/cesium/follow.js",
|
||||||
"src/globe/cpuPicks.js",
|
"src/globe/cpuPicks.js",
|
||||||
@@ -3847,6 +3490,8 @@
|
|||||||
"src/layers/cyclones/render/navara.js",
|
"src/layers/cyclones/render/navara.js",
|
||||||
"src/layers/shared/render/groundRing.js",
|
"src/layers/shared/render/groundRing.js",
|
||||||
"src/layers/bikeshare/render/navara.js",
|
"src/layers/bikeshare/render/navara.js",
|
||||||
|
"src/layers/bikeshare/stationPoints.js",
|
||||||
|
"src/layers/shared/viewRectangle.js",
|
||||||
"src/layers/installations/render/navaraGeometry.js",
|
"src/layers/installations/render/navaraGeometry.js",
|
||||||
"src/layers/installations/render/navaraOutlineBatch.js",
|
"src/layers/installations/render/navaraOutlineBatch.js",
|
||||||
"src/layers/installations/render/navaraStencilBatch.js",
|
"src/layers/installations/render/navaraStencilBatch.js",
|
||||||
@@ -3857,6 +3502,7 @@
|
|||||||
"src/layers/radio/render/navara.js",
|
"src/layers/radio/render/navara.js",
|
||||||
"src/layers/transit/render/navara.js",
|
"src/layers/transit/render/navara.js",
|
||||||
"src/layers/transit/render/navaraSprites.js",
|
"src/layers/transit/render/navaraSprites.js",
|
||||||
|
"src/layers/shared/render/navaraMoverBank.js",
|
||||||
"src/layers/transit/render/navaraView.js",
|
"src/layers/transit/render/navaraView.js",
|
||||||
"src/layers/transit/render/navaraTrail.js",
|
"src/layers/transit/render/navaraTrail.js",
|
||||||
"src/layers/recentImagery/render/navara.js",
|
"src/layers/recentImagery/render/navara.js",
|
||||||
@@ -3868,14 +3514,18 @@
|
|||||||
"src/layers/cctv/render/navaraLine.js",
|
"src/layers/cctv/render/navaraLine.js",
|
||||||
"src/layers/alpr/render/navara.js",
|
"src/layers/alpr/render/navara.js",
|
||||||
"src/layers/alpr/render/navaraOverlay.js",
|
"src/layers/alpr/render/navaraOverlay.js",
|
||||||
|
"src/layers/alpr/overlay.js",
|
||||||
|
"src/layers/alpr/presentation.js",
|
||||||
"src/layers/radio/screenClusters.js",
|
"src/layers/radio/screenClusters.js",
|
||||||
"src/layers/shared/render/navaraCellGeometry.js",
|
"src/layers/shared/render/navaraCellGeometry.js",
|
||||||
"src/layers/shared/render/navaraGroundCells.js",
|
"src/layers/shared/render/navaraGroundCells.js",
|
||||||
"src/layers/firms/render/navaraSprites.js",
|
"src/layers/firms/render/navaraSprites.js",
|
||||||
|
"src/layers/shared/render/imageAtlas.js",
|
||||||
"src/layers/firms/render/navaraHeat.js",
|
"src/layers/firms/render/navaraHeat.js",
|
||||||
"src/layers/shared/render/navaraGroundGeometry.js",
|
"src/layers/shared/render/navaraGroundGeometry.js",
|
||||||
"src/layers/shared/render/navaraGroundGrid.js",
|
"src/layers/shared/render/navaraGroundGrid.js",
|
||||||
"src/layers/shared/render/navaraGroundSurface.js",
|
"src/layers/shared/render/navaraGroundSurface.js",
|
||||||
|
"src/layers/shared/render/navaraGroundEffect.js",
|
||||||
"src/annotations/outlineRungs.js",
|
"src/annotations/outlineRungs.js",
|
||||||
"src/sources/openFreeMapOutlines.js",
|
"src/sources/openFreeMapOutlines.js",
|
||||||
"src/sources/nominatimOutlines.js",
|
"src/sources/nominatimOutlines.js",
|
||||||
@@ -4364,6 +4014,7 @@
|
|||||||
"src/layers/transit/state.js",
|
"src/layers/transit/state.js",
|
||||||
"src/layers/transit/testing.js",
|
"src/layers/transit/testing.js",
|
||||||
"src/layers/transit/viewport.js",
|
"src/layers/transit/viewport.js",
|
||||||
|
"src/layers/shared/viewRectangle.js",
|
||||||
"src/data/cameraSensitivity.js",
|
"src/data/cameraSensitivity.js",
|
||||||
"src/data/contactPlayback.js",
|
"src/data/contactPlayback.js",
|
||||||
"src/data/contactTrailRenderer.js",
|
"src/data/contactTrailRenderer.js",
|
||||||
@@ -4528,6 +4179,7 @@
|
|||||||
"src/globe/cesium/input.js",
|
"src/globe/cesium/input.js",
|
||||||
"src/globe/cesium/convert.js",
|
"src/globe/cesium/convert.js",
|
||||||
"src/globe/cesium/camera.js",
|
"src/globe/cesium/camera.js",
|
||||||
|
"src/globe/cameraMath.js",
|
||||||
"src/globe/cesium/frames.js",
|
"src/globe/cesium/frames.js",
|
||||||
"src/globe/cesium/picker.js",
|
"src/globe/cesium/picker.js",
|
||||||
"src/globe/cesium/events.js",
|
"src/globe/cesium/events.js",
|
||||||
@@ -4543,6 +4195,8 @@
|
|||||||
"src/data/render/cesiumTrailRenderer.js",
|
"src/data/render/cesiumTrailRenderer.js",
|
||||||
"src/layers/alpr/render/cesiumPresentation.js",
|
"src/layers/alpr/render/cesiumPresentation.js",
|
||||||
"src/layers/alpr/render/cesiumOverlay.js",
|
"src/layers/alpr/render/cesiumOverlay.js",
|
||||||
|
"src/layers/alpr/overlay.js",
|
||||||
|
"src/layers/alpr/presentation.js",
|
||||||
"src/layers/alpr/render/cesium.js",
|
"src/layers/alpr/render/cesium.js",
|
||||||
"src/layers/bikeshare/selectionModel.js",
|
"src/layers/bikeshare/selectionModel.js",
|
||||||
"src/layers/bikeshare/presentation.js",
|
"src/layers/bikeshare/presentation.js",
|
||||||
@@ -4550,6 +4204,7 @@
|
|||||||
"src/layers/bikeshare/render/cesium/viewport.js",
|
"src/layers/bikeshare/render/cesium/viewport.js",
|
||||||
"src/layers/bikeshare/render/cesium/lifecycle.js",
|
"src/layers/bikeshare/render/cesium/lifecycle.js",
|
||||||
"src/layers/bikeshare/render/cesium/rendering.js",
|
"src/layers/bikeshare/render/cesium/rendering.js",
|
||||||
|
"src/layers/bikeshare/stationPoints.js",
|
||||||
"src/layers/bikeshare/render/cesium/selection.js",
|
"src/layers/bikeshare/render/cesium/selection.js",
|
||||||
"src/layers/installations/render/cesium/lifecycle.js",
|
"src/layers/installations/render/cesium/lifecycle.js",
|
||||||
"src/layers/installations/render/cesium.js",
|
"src/layers/installations/render/cesium.js",
|
||||||
@@ -4561,24 +4216,7 @@
|
|||||||
"src/data/render/cesiumContactTrailRenderer.js",
|
"src/data/render/cesiumContactTrailRenderer.js",
|
||||||
"src/layers/localAdsb/qaDrawn.js",
|
"src/layers/localAdsb/qaDrawn.js",
|
||||||
"src/layers/installations/render/cesium/namedMarkers.js",
|
"src/layers/installations/render/cesium/namedMarkers.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/time/clock.js",
|
"src/time/clock.js",
|
||||||
"src/globe/follow.js",
|
"src/globe/follow.js",
|
||||||
"src/globe/cesium/follow.js",
|
"src/globe/cesium/follow.js",
|
||||||
@@ -4687,24 +4325,7 @@
|
|||||||
"src/globe/cesium/engineFromViewer.js",
|
"src/globe/cesium/engineFromViewer.js",
|
||||||
"src/globe/cesium/credits.js",
|
"src/globe/cesium/credits.js",
|
||||||
"src/globe/contract.js",
|
"src/globe/contract.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/globe/surfaceDetail.js",
|
"src/globe/surfaceDetail.js",
|
||||||
"src/sources/openFreeMap.js",
|
"src/sources/openFreeMap.js",
|
||||||
"src/sources/vectorTiles.js",
|
"src/sources/vectorTiles.js",
|
||||||
@@ -4985,24 +4606,7 @@
|
|||||||
"src/i18n/locales/zh-CN.json",
|
"src/i18n/locales/zh-CN.json",
|
||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/ui/unitLabels.js",
|
"src/ui/unitLabels.js",
|
||||||
"src/geo/julianDate.js",
|
"src/geo/"
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/index.js"
|
|
||||||
],
|
],
|
||||||
"external": ["cesium"]
|
"external": ["cesium"]
|
||||||
},
|
},
|
||||||
@@ -5315,6 +4919,7 @@
|
|||||||
"src/layers/transit/testing.js",
|
"src/layers/transit/testing.js",
|
||||||
"src/layers/transit/trails.js",
|
"src/layers/transit/trails.js",
|
||||||
"src/layers/transit/viewport.js",
|
"src/layers/transit/viewport.js",
|
||||||
|
"src/layers/shared/viewRectangle.js",
|
||||||
"src/overlays/worldOverlay.js",
|
"src/overlays/worldOverlay.js",
|
||||||
"src/overlays/worldOverlayDraw.js",
|
"src/overlays/worldOverlayDraw.js",
|
||||||
"src/overlays/worldOverlayTokens.js",
|
"src/overlays/worldOverlayTokens.js",
|
||||||
@@ -5349,6 +4954,7 @@
|
|||||||
"src/globe/cesium/picker.js",
|
"src/globe/cesium/picker.js",
|
||||||
"src/globe/cesium/frames.js",
|
"src/globe/cesium/frames.js",
|
||||||
"src/globe/cesium/camera.js",
|
"src/globe/cesium/camera.js",
|
||||||
|
"src/globe/cameraMath.js",
|
||||||
"src/globe/cesium/convert.js",
|
"src/globe/cesium/convert.js",
|
||||||
"src/globe/cesium/input.js",
|
"src/globe/cesium/input.js",
|
||||||
"src/maps/google3d.js",
|
"src/maps/google3d.js",
|
||||||
@@ -5373,24 +4979,7 @@
|
|||||||
"src/data/render/cesiumProjection.js",
|
"src/data/render/cesiumProjection.js",
|
||||||
"src/layers/shared/color.js",
|
"src/layers/shared/color.js",
|
||||||
"src/layers/transit/render/cesium.js",
|
"src/layers/transit/render/cesium.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/time/clock.js",
|
"src/time/clock.js",
|
||||||
"src/globe/surfaceDetail.js",
|
"src/globe/surfaceDetail.js",
|
||||||
"src/celestialRing/render/cesium.js"
|
"src/celestialRing/render/cesium.js"
|
||||||
@@ -5418,6 +5007,7 @@
|
|||||||
"src/layers/transit/state.js",
|
"src/layers/transit/state.js",
|
||||||
"src/layers/transit/testing.js",
|
"src/layers/transit/testing.js",
|
||||||
"src/layers/transit/viewport.js",
|
"src/layers/transit/viewport.js",
|
||||||
|
"src/layers/shared/viewRectangle.js",
|
||||||
"src/data/contactPlayback.js",
|
"src/data/contactPlayback.js",
|
||||||
"src/data/contactTrailRenderer.js",
|
"src/data/contactTrailRenderer.js",
|
||||||
"src/data/transitProxy.js",
|
"src/data/transitProxy.js",
|
||||||
@@ -5445,24 +5035,7 @@
|
|||||||
"src/i18n/locales/zh-CN.json",
|
"src/i18n/locales/zh-CN.json",
|
||||||
"src/i18n/locales/zh-Hant.json",
|
"src/i18n/locales/zh-Hant.json",
|
||||||
"src/layers/shared/color.js",
|
"src/layers/shared/color.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/globe/contract.js"
|
"src/globe/contract.js"
|
||||||
],
|
],
|
||||||
"external": ["cesium", "pbf"]
|
"external": ["cesium", "pbf"]
|
||||||
@@ -5506,9 +5079,6 @@
|
|||||||
"exports": ["./layers/perimeters"],
|
"exports": ["./layers/perimeters"],
|
||||||
"modules": [
|
"modules": [
|
||||||
"src/geo/cartesian3.js",
|
"src/geo/cartesian3.js",
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/geo/math.js",
|
"src/geo/math.js",
|
||||||
"src/layers/perimeters/index.js",
|
"src/layers/perimeters/index.js",
|
||||||
"src/layers/perimeters/render/cesium.js",
|
"src/layers/perimeters/render/cesium.js",
|
||||||
@@ -5611,9 +5181,6 @@
|
|||||||
"src/layers/ghostMaps/kml.js",
|
"src/layers/ghostMaps/kml.js",
|
||||||
"src/layers/ghostMaps/source.js",
|
"src/layers/ghostMaps/source.js",
|
||||||
"src/geo/cartesian3.js",
|
"src/geo/cartesian3.js",
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/geo/math.js",
|
"src/geo/math.js",
|
||||||
"src/sources/httpBody.js",
|
"src/sources/httpBody.js",
|
||||||
"src/data/geoFileImport.js",
|
"src/data/geoFileImport.js",
|
||||||
@@ -5663,24 +5230,7 @@
|
|||||||
"exports": ["./layers/drone-airspace"],
|
"exports": ["./layers/drone-airspace"],
|
||||||
"modules": [
|
"modules": [
|
||||||
"src/layers/droneAirspace/index.js",
|
"src/layers/droneAirspace/index.js",
|
||||||
"src/geo/index.js",
|
"src/geo/",
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/boundingSphere.js",
|
|
||||||
"src/geo/transforms.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/layers/shared/render/cesiumZones.js",
|
"src/layers/shared/render/cesiumZones.js",
|
||||||
"src/sources/httpBody.js",
|
"src/sources/httpBody.js",
|
||||||
"src/layers/droneAirspace/source.js",
|
"src/layers/droneAirspace/source.js",
|
||||||
@@ -5769,24 +5319,7 @@
|
|||||||
"src/tools/queries/panelRequest.js",
|
"src/tools/queries/panelRequest.js",
|
||||||
"src/geoDistance.js",
|
"src/geoDistance.js",
|
||||||
"src/visualStylePolicy.js",
|
"src/visualStylePolicy.js",
|
||||||
"src/geo/boundingSphere.js",
|
"src/geo/"
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/index.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/transforms.js"
|
|
||||||
],
|
],
|
||||||
"external": ["satellite.js"]
|
"external": ["satellite.js"]
|
||||||
},
|
},
|
||||||
@@ -5898,9 +5431,6 @@
|
|||||||
"exports": ["./layers/air-quality"],
|
"exports": ["./layers/air-quality"],
|
||||||
"modules": [
|
"modules": [
|
||||||
"src/geo/cartesian3.js",
|
"src/geo/cartesian3.js",
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/geo/math.js",
|
"src/geo/math.js",
|
||||||
"src/layers/airQuality/index.js",
|
"src/layers/airQuality/index.js",
|
||||||
"src/layers/airQuality/render/cesium.js",
|
"src/layers/airQuality/render/cesium.js",
|
||||||
@@ -5946,24 +5476,7 @@
|
|||||||
"src/loadingFeedback.js",
|
"src/loadingFeedback.js",
|
||||||
"src/data/installationFeedback.js",
|
"src/data/installationFeedback.js",
|
||||||
"src/i18n/marker.js",
|
"src/i18n/marker.js",
|
||||||
"src/geo/boundingSphere.js",
|
"src/geo/"
|
||||||
"src/geo/cartesian2.js",
|
|
||||||
"src/geo/cartesian3.js",
|
|
||||||
"src/geo/cartographic.js",
|
|
||||||
"src/geo/check.js",
|
|
||||||
"src/geo/debug.js",
|
|
||||||
"src/geo/developerError.js",
|
|
||||||
"src/geo/ellipsoid.js",
|
|
||||||
"src/geo/ellipsoidGeodesic.js",
|
|
||||||
"src/geo/index.js",
|
|
||||||
"src/geo/julianDate.js",
|
|
||||||
"src/geo/math.js",
|
|
||||||
"src/geo/matrix3.js",
|
|
||||||
"src/geo/matrix4.js",
|
|
||||||
"src/geo/quaternion.js",
|
|
||||||
"src/geo/scaleToGeodeticSurface.js",
|
|
||||||
"src/geo/simon1994.js",
|
|
||||||
"src/geo/transforms.js"
|
|
||||||
],
|
],
|
||||||
"external": ["cesium"]
|
"external": ["cesium"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,42 +83,28 @@ try {
|
|||||||
// Every name the checkout's .env files define, plus key-like names in this
|
// Every name the checkout's .env files define, plus key-like names in this
|
||||||
// environment: all blank for the build and the preview.
|
// environment: all blank for the build and the preview.
|
||||||
const KEY_LIKE = /(_KEY|_TOKEN|_SECRET|_CLIENT_ID|PASSWORD)$/;
|
const KEY_LIKE = /(_KEY|_TOKEN|_SECRET|_CLIENT_ID|PASSWORD)$/;
|
||||||
const dotenvNames = () => {
|
/** The [name, value] pairs of the checkout's .env and .env.local, in order. */
|
||||||
const names = new Set();
|
const dotenv = () =>
|
||||||
for (const file of ['.env', '.env.local'])
|
['.env', '.env.local']
|
||||||
if (existsSync(join(root, file)))
|
.filter((file) => existsSync(join(root, file)))
|
||||||
for (const line of readFileSync(join(root, file), 'utf8').split('\n')) {
|
.flatMap((file) => readFileSync(join(root, file), 'utf8').split('\n'))
|
||||||
const name = line.match(/^\s*([A-Z0-9_]+)\s*=/)?.[1];
|
.map((line) => line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)/))
|
||||||
if (name) names.add(name);
|
.filter(Boolean)
|
||||||
}
|
.map(([, name, raw]) => [name, raw.trim().replace(/^["']|["']$/g, '')]);
|
||||||
return [...names].sort();
|
const dotenvNames = () => [...new Set(dotenv().map(([name]) => name))].sort();
|
||||||
};
|
const envNames = () =>
|
||||||
const envNames = () => {
|
[
|
||||||
const names = new Set();
|
...new Set([
|
||||||
for (const file of ['.env', '.env.local'])
|
...dotenvNames(),
|
||||||
if (existsSync(join(root, file)))
|
...Object.keys(process.env).filter((name) => KEY_LIKE.test(name)),
|
||||||
for (const line of readFileSync(join(root, file), 'utf8').split('\n')) {
|
]),
|
||||||
const name = line.match(/^\s*([A-Z0-9_]+)\s*=/)?.[1];
|
].sort();
|
||||||
if (name) names.add(name);
|
// Credentials only (model names and URLs are settings the code may also
|
||||||
}
|
// carry as defaults).
|
||||||
for (const name of Object.keys(process.env))
|
const envValues = () =>
|
||||||
if (KEY_LIKE.test(name)) names.add(name);
|
dotenv()
|
||||||
return [...names].sort();
|
.filter(([name, value]) => KEY_LIKE.test(name) && value.length >= 8)
|
||||||
};
|
.map(([, value]) => value);
|
||||||
const envValues = () => {
|
|
||||||
const values = [];
|
|
||||||
for (const file of ['.env', '.env.local'])
|
|
||||||
if (existsSync(join(root, file)))
|
|
||||||
for (const line of readFileSync(join(root, file), 'utf8').split('\n')) {
|
|
||||||
const [, name, raw] = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)$/) ?? [];
|
|
||||||
const value = raw?.trim().replace(/^["']|["']$/g, '');
|
|
||||||
// Credentials only (model names and URLs are settings the code may
|
|
||||||
// also carry as defaults).
|
|
||||||
if (KEY_LIKE.test(name ?? '') && value && value.length >= 8)
|
|
||||||
values.push(value);
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
};
|
|
||||||
const scrubbed = () => ({
|
const scrubbed = () => ({
|
||||||
...process.env,
|
...process.env,
|
||||||
...Object.fromEntries(envNames().map((name) => [name, ''])),
|
...Object.fromEntries(envNames().map((name) => [name, ''])),
|
||||||
@@ -368,9 +354,9 @@ const runs = [
|
|||||||
['rocket-launches'],
|
['rocket-launches'],
|
||||||
['--missions'],
|
['--missions'],
|
||||||
],
|
],
|
||||||
// Stage 3's own checks (one application per page, voice across a switch).
|
// One application per page, and voice across a switch: these block paid
|
||||||
// They block paid routes themselves but do not run under the strict
|
// routes themselves but do not run under the strict harness, so they add
|
||||||
// harness, so they add no assertion manifest and no network ledger.
|
// no assertion manifest and no network ledger.
|
||||||
['app-once', 'app-once', [], [], 600, true],
|
['app-once', 'app-once', [], [], 600, true],
|
||||||
['voice-engine-switch', 'voice-engine-switch', [], [], 300, true],
|
['voice-engine-switch', 'voice-engine-switch', [], [], 300, true],
|
||||||
['strict-rapid-switch', 'strict-rapid-switch', []],
|
['strict-rapid-switch', 'strict-rapid-switch', []],
|
||||||
@@ -379,14 +365,18 @@ const runs = [
|
|||||||
['strict-failure-matrix', 'strict-failure-matrix', [], [], 600],
|
['strict-failure-matrix', 'strict-failure-matrix', [], [], 600],
|
||||||
['strict-cold-chunks', 'strict-cold-chunks', []],
|
['strict-cold-chunks', 'strict-cold-chunks', []],
|
||||||
['strict-prod-smoke', 'strict-prod-smoke', []],
|
['strict-prod-smoke', 'strict-prod-smoke', []],
|
||||||
// Stage 6: the app's visual styles, bloom, contact sonar, cockpit clouds
|
// The app's visual styles, bloom, contact sonar, cockpit clouds, and the
|
||||||
// and annotations drawn on Navara (annotations is a tool, not a catalog
|
// annotations, Draw, Import and the scope mask with the celestial ring on
|
||||||
// layer: TOOL_IDS).
|
// Navara (those are tools and display overlays, not catalog layers:
|
||||||
|
// TOOL_IDS).
|
||||||
['navara-visual-styles', 'navara-visual-styles', []],
|
['navara-visual-styles', 'navara-visual-styles', []],
|
||||||
['navara-bloom', 'navara-bloom', []],
|
['navara-bloom', 'navara-bloom', []],
|
||||||
['navara-sonar', 'navara-sonar', []],
|
['navara-sonar', 'navara-sonar', []],
|
||||||
['navara-cockpit-clouds', 'navara-cockpit-clouds', []],
|
['navara-cockpit-clouds', 'navara-cockpit-clouds', []],
|
||||||
['navara-annotations', 'navara-annotations', ['annotations']],
|
['navara-annotations', 'navara-annotations', ['annotations']],
|
||||||
|
['navara-draw', 'navara-draw', ['draw']],
|
||||||
|
['navara-geo-import', 'navara-geo-import', ['geo-import']],
|
||||||
|
['navara-scope', 'navara-scope', ['scope-mask', 'celestial-ring']],
|
||||||
['strict-soak', 'strict-soak', [], soakRuns, 1200],
|
['strict-soak', 'strict-soak', [], soakRuns, 1200],
|
||||||
[
|
[
|
||||||
'strict-soak--long',
|
'strict-soak--long',
|
||||||
@@ -398,7 +388,13 @@ const runs = [
|
|||||||
];
|
];
|
||||||
// Ids a scenario covers that are tools, not catalog layers: their
|
// Ids a scenario covers that are tools, not catalog layers: their
|
||||||
// assertions are recorded, but the catalog comparison leaves them out.
|
// assertions are recorded, but the catalog comparison leaves them out.
|
||||||
const TOOL_IDS = new Set(['annotations']);
|
const TOOL_IDS = new Set([
|
||||||
|
'annotations',
|
||||||
|
'draw',
|
||||||
|
'geo-import',
|
||||||
|
'scope-mask',
|
||||||
|
'celestial-ring',
|
||||||
|
]);
|
||||||
const scenarioIds = [
|
const scenarioIds = [
|
||||||
...new Set(
|
...new Set(
|
||||||
runs.flatMap(([, , ids]) => ids).filter((id) => !TOOL_IDS.has(id)),
|
runs.flatMap(([, , ids]) => ids).filter((id) => !TOOL_IDS.has(id)),
|
||||||
@@ -414,9 +410,7 @@ const liveCatalog = {};
|
|||||||
layers: [],
|
layers: [],
|
||||||
name: 'catalog',
|
name: 'catalog',
|
||||||
});
|
});
|
||||||
const browser = await strict.launch({
|
const browser = await strict.launch();
|
||||||
args: ['--use-angle=metal', '--enable-gpu'],
|
|
||||||
});
|
|
||||||
try {
|
try {
|
||||||
for (const engine of ENGINES) {
|
for (const engine of ENGINES) {
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
@@ -497,17 +491,7 @@ for (const [label, name, layers, extra = [], seconds = 300, external] of runs) {
|
|||||||
const script = 'scripts/qa-' + name + '.mjs';
|
const script = 'scripts/qa-' + name + '.mjs';
|
||||||
const evidence = join(output, 'evidence', label + '.json');
|
const evidence = join(output, 'evidence', label + '.json');
|
||||||
rmSync(evidence, { force: true });
|
rmSync(evidence, { force: true });
|
||||||
const args =
|
const args = [base.origin, ...extra];
|
||||||
name === 'w2-imagery'
|
|
||||||
? [
|
|
||||||
'--url',
|
|
||||||
base.origin + '/?engine=navara',
|
|
||||||
'--report',
|
|
||||||
join(output, 'evidence', 'imagery-report.json'),
|
|
||||||
'--switch',
|
|
||||||
'--preview',
|
|
||||||
]
|
|
||||||
: [base.origin, ...extra];
|
|
||||||
if (external) args.splice(0, 1, base.origin + '/');
|
if (external) args.splice(0, 1, base.origin + '/');
|
||||||
else args.push(`--layers=${layers.join(',')}`, `--evidence=${evidence}`);
|
else args.push(`--layers=${layers.join(',')}`, `--evidence=${evidence}`);
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
|||||||
+1
-18
@@ -221,7 +221,7 @@ try {
|
|||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
report.firstRoundTrip = await listenerCounts(page, { bySource: true });
|
report.firstRoundTrip = await listenerCounts(page, { bySource: true });
|
||||||
}
|
}
|
||||||
// Both engines draw the session's marks and files (Navara since Stage 6).
|
// Both engines draw the session's marks and files (Navara included).
|
||||||
await page.waitForFunction(
|
await page.waitForFunction(
|
||||||
() => window.__gevGeoImport.diagnostics().drawn === 1,
|
() => window.__gevGeoImport.diagnostics().drawn === 1,
|
||||||
{ timeout: 30_000 },
|
{ timeout: 30_000 },
|
||||||
@@ -412,23 +412,6 @@ try {
|
|||||||
report.errors = errors;
|
report.errors = errors;
|
||||||
check(errors.length === 0, 'page errors', errors);
|
check(errors.length === 0, 'page errors', errors);
|
||||||
|
|
||||||
// The legacy route (?switch=legacy) still builds an application per
|
|
||||||
// engine, for one release.
|
|
||||||
const legacy = await openSessionPage(
|
|
||||||
browser,
|
|
||||||
sessionAddress(base, { engine: 'cesium', extra: { switch: 'legacy' } }),
|
|
||||||
);
|
|
||||||
await waitReady(legacy.page, 'cesium');
|
|
||||||
await switchEngine(legacy.page, 'navara');
|
|
||||||
await switchEngine(legacy.page, 'cesium');
|
|
||||||
report.legacy = await readSession(legacy.page);
|
|
||||||
check(
|
|
||||||
report.legacy.counters.applications === 3,
|
|
||||||
'the legacy route builds an application per engine',
|
|
||||||
report.legacy.counters,
|
|
||||||
);
|
|
||||||
check(legacy.errors.length === 0, 'legacy page errors', legacy.errors);
|
|
||||||
await legacy.page.close();
|
|
||||||
console.log(JSON.stringify(report, null, 2));
|
console.log(JSON.stringify(report, null, 2));
|
||||||
if (!reportOnly) console.log('PASS application created once; session kept');
|
if (!reportOnly) console.log('PASS application created once; session kept');
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export function isExpectedPreviewSetupError(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// W1 acceptance shares one strict runtime verdict across its main page and
|
// The runtime evidence gate shares one strict runtime verdict across its main page and
|
||||||
// every share-link page. Response diagnostics never retain query credentials.
|
// every share-link page. Response diagnostics never retain query credentials.
|
||||||
export function watchW1Page(page, report, { name, preview = false, baseUrl }) {
|
export function watchW1Page(page, report, { name, preview = false, baseUrl }) {
|
||||||
const errors = (report.browserErrors ??= []);
|
const errors = (report.browserErrors ??= []);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const { stdout: video } = await promisify(execFile)(
|
|||||||
{ encoding: 'buffer', maxBuffer: 16000000 },
|
{ encoding: 'buffer', maxBuffer: 16000000 },
|
||||||
);
|
);
|
||||||
const strict = createStrict({ origin: url.origin, layers: ['cctv'] });
|
const strict = createStrict({ origin: url.origin, layers: ['cctv'] });
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const p = await b.newPage();
|
||||||
const errors = [];
|
const errors = [];
|
||||||
p.on('pageerror', (e) => {
|
p.on('pageerror', (e) => {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
*
|
*
|
||||||
* This legacy harness is the geometry, lifecycle and real-input oracle.
|
* This legacy harness is the geometry, lifecycle and real-input oracle.
|
||||||
* Migration runs use headless Metal and strict runtime error observation.
|
* Migration runs use headless Metal and strict runtime error observation.
|
||||||
* Screenshots remain visual-review evidence here; the W2 companion must
|
* Screenshots remain visual-review evidence here; a separate pixel gate must
|
||||||
* independently prove actual marker, image-plane and video pixels.
|
* independently prove actual marker, image-plane and video pixels.
|
||||||
*
|
*
|
||||||
* No `cesium` package import for in-page math: the app does not expose a
|
* No `cesium` package import for in-page math: the app does not expose a
|
||||||
@@ -2245,7 +2245,7 @@ async function main() {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Screenshots supplement this state/geometry oracle. Positive rendered
|
// Screenshots supplement this state/geometry oracle. Positive rendered
|
||||||
// media pixels are a separate mandatory W2 companion gate.
|
// media pixels are a separate mandatory pixel gate.
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
const shot = path.join(SHOTS_DIR, 'cctv-v2-active.png');
|
const shot = path.join(SHOTS_DIR, 'cctv-v2-active.png');
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { fromDegrees } from '../src/geo/cartesian3.js';
|
import { fromDegrees } from '../src/geo/cartesian3.js';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
base.search = '?engine=navara&welcome=0&look=cinematic';
|
base.search = '?engine=navara&welcome=0&look=cinematic';
|
||||||
@@ -10,14 +15,8 @@ const strict = createStrict({
|
|||||||
origin: base.origin,
|
origin: base.origin,
|
||||||
layers: ['military-awareness', 'military-installations'],
|
layers: ['military-awareness', 'military-installations'],
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b);
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
await p.evaluateOnNewDocument(() => {
|
await p.evaluateOnNewDocument(() => {
|
||||||
const fetch = window.fetch.bind(window);
|
const fetch = window.fetch.bind(window);
|
||||||
window.fetch = (input, options) => {
|
window.fetch = (input, options) => {
|
||||||
@@ -89,21 +88,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(p, 'navara');
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
await p.evaluate(async () => {
|
await p.evaluate(async () => {
|
||||||
const { dataManager, styleManager, tileset } = window.__godsEyeView;
|
const { dataManager, styleManager, tileset } = window.__godsEyeView;
|
||||||
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
||||||
@@ -235,8 +220,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
baseline,
|
baseline,
|
||||||
);
|
);
|
||||||
for (const kind of ['cesium', 'navara']) {
|
for (const kind of ['cesium', 'navara']) {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
const current = await sample();
|
const current = await sample();
|
||||||
console.log('handoff', kind, JSON.stringify(current));
|
console.log('handoff', kind, JSON.stringify(current));
|
||||||
assert.deepEqual(current, {
|
assert.deepEqual(current, {
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
base.search = '?engine=navara&welcome=0&look=cinematic';
|
base.search = '?engine=navara&welcome=0&look=cinematic';
|
||||||
base.hash =
|
base.hash =
|
||||||
'v=2&lat=55.3241&lon=10.7995&alt=25000&heading=0&pitch=-90&roll=0&map=sentinel2&l=';
|
'v=2&lat=55.3241&lon=10.7995&alt=25000&heading=0&pitch=-90&roll=0&map=sentinel2&l=';
|
||||||
const strict = createStrict({ origin: base.origin, layers: ['directions'] });
|
const strict = createStrict({ origin: base.origin, layers: ['directions'] });
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b);
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
let routeReads = 0,
|
let routeReads = 0,
|
||||||
release;
|
release;
|
||||||
await p.setRequestInterception(true);
|
await p.setRequestInterception(true);
|
||||||
@@ -55,21 +54,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(p, 'navara');
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
await p.evaluate(async () => {
|
await p.evaluate(async () => {
|
||||||
const { tileset, dataManager } = window.__godsEyeView;
|
const { tileset, dataManager } = window.__godsEyeView;
|
||||||
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
||||||
@@ -91,8 +76,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
'directions',
|
'directions',
|
||||||
'A and B set by clicks on the globe; one route request',
|
'A and B set by clicks on the globe; one route request',
|
||||||
);
|
);
|
||||||
await strict.pick(p, 'cesium');
|
await strict.switchTo(p, 'cesium');
|
||||||
await ready('cesium');
|
|
||||||
release();
|
release();
|
||||||
await p.waitForFunction(() => window.qaRoute.getStats().count === 3);
|
await p.waitForFunction(() => window.qaRoute.getStats().count === 3);
|
||||||
await p.waitForFunction(() => {
|
await p.waitForFunction(() => {
|
||||||
@@ -105,8 +89,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
'the route asked for on Navara arrives on Cesium; a step is selectable',
|
'the route asked for on Navara arrives on Cesium; a step is selectable',
|
||||||
);
|
);
|
||||||
const transfer = async (kind) => {
|
const transfer = async (kind) => {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await p.evaluate(
|
await p.evaluate(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ const strict = createStrict({
|
|||||||
layers: [],
|
layers: [],
|
||||||
fixtures: ['https://earthquake.usgs.gov/*'],
|
fixtures: ['https://earthquake.usgs.gov/*'],
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const p = await b.newPage();
|
||||||
await p.setViewport({ width: 1280, height: 900 });
|
await p.setViewport({ width: 1280, height: 900 });
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ base.search = '?welcome=0&look=cinematic';
|
|||||||
base.hash =
|
base.hash =
|
||||||
'v=2&lat=42.3554&lon=-71.0605&alt=6000&heading=0&pitch=-90&map=osm&l=e';
|
'v=2&lat=42.3554&lon=-71.0605&alt=6000&heading=0&pitch=-90&map=osm&l=e';
|
||||||
const strict = createStrict({ origin: base.origin, layers: ['earthquakes'] });
|
const strict = createStrict({ origin: base.origin, layers: ['earthquakes'] });
|
||||||
await strict.run(
|
await strict.run({}, async (browser) => {
|
||||||
{ args: ['--use-angle=metal', '--enable-gpu'] },
|
|
||||||
async (browser) => {
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
await page.evaluateOnNewDocument(() => {
|
await page.evaluateOnNewDocument(() => {
|
||||||
localStorage.setItem('gev:first-run-mission:v1', 'suppressed');
|
localStorage.setItem('gev:first-run-mission:v1', 'suppressed');
|
||||||
@@ -33,9 +31,7 @@ await strict.run(
|
|||||||
};
|
};
|
||||||
window.fetch = (url, options) => {
|
window.fetch = (url, options) => {
|
||||||
if (
|
if (
|
||||||
String(url).startsWith(
|
String(url).startsWith('https://earthquake.usgs.gov/earthquakes/feed/')
|
||||||
'https://earthquake.usgs.gov/earthquakes/feed/',
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
return Promise.resolve(
|
return Promise.resolve(
|
||||||
Response.json({
|
Response.json({
|
||||||
@@ -69,9 +65,7 @@ await strict.run(
|
|||||||
window.__godsEyeView?.engine?.kind === kind &&
|
window.__godsEyeView?.engine?.kind === kind &&
|
||||||
document.querySelector('#globe-engine')?.value === kind &&
|
document.querySelector('#globe-engine')?.value === kind &&
|
||||||
!document.querySelector('#globe-engine').disabled &&
|
!document.querySelector('#globe-engine').disabled &&
|
||||||
document
|
document.getElementById('loading-screen')?.classList.contains('hidden'),
|
||||||
.getElementById('loading-screen')
|
|
||||||
?.classList.contains('hidden'),
|
|
||||||
{ timeout: 90000 },
|
{ timeout: 90000 },
|
||||||
kind,
|
kind,
|
||||||
);
|
);
|
||||||
@@ -128,8 +122,7 @@ await strict.run(
|
|||||||
on,
|
on,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const stored = () =>
|
const stored = () => page.evaluate(() => localStorage.getItem('gev.engine'));
|
||||||
page.evaluate(() => localStorage.getItem('gev.engine'));
|
|
||||||
async function switchTo(kind) {
|
async function switchTo(kind) {
|
||||||
if (
|
if (
|
||||||
await page.$eval(
|
await page.$eval(
|
||||||
@@ -164,9 +157,7 @@ await strict.run(
|
|||||||
});
|
});
|
||||||
await strict.pick(page, kind);
|
await strict.pick(page, kind);
|
||||||
await ready(kind);
|
await ready(kind);
|
||||||
assert.ok(
|
assert.ok(await page.evaluate(() => window.__switchDocument === document));
|
||||||
await page.evaluate(() => window.__switchDocument === document),
|
|
||||||
);
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
window.__switchObserver.disconnect();
|
window.__switchObserver.disconnect();
|
||||||
@@ -221,8 +212,7 @@ await strict.run(
|
|||||||
assert.ok(Math.abs(Number(hash.get('alt')) - 6000) < 30);
|
assert.ok(Math.abs(Number(hash.get('alt')) - 6000) < 30);
|
||||||
const view = await page.evaluate(() => {
|
const view = await page.evaluate(() => {
|
||||||
const { engine, mapStackController } = window.__godsEyeView;
|
const { engine, mapStackController } = window.__godsEyeView;
|
||||||
const { latitude, longitude, height } =
|
const { latitude, longitude, height } = engine.camera.pose().cartographic;
|
||||||
engine.camera.pose().cartographic;
|
|
||||||
return {
|
return {
|
||||||
latitude: latitude * (180 / Math.PI),
|
latitude: latitude * (180 / Math.PI),
|
||||||
longitude: longitude * (180 / Math.PI),
|
longitude: longitude * (180 / Math.PI),
|
||||||
@@ -345,11 +335,7 @@ await strict.run(
|
|||||||
'earthquakes',
|
'earthquakes',
|
||||||
'Forward restores the disabled layer on the same model',
|
'Forward restores the disabled layer on the same model',
|
||||||
);
|
);
|
||||||
assert.equal(
|
assert.equal(await settledAddress(), cesiumLayers, 'Forward keeps its entry');
|
||||||
await settledAddress(),
|
|
||||||
cesiumLayers,
|
|
||||||
'Forward keeps its entry',
|
|
||||||
);
|
|
||||||
await page.evaluate(() =>
|
await page.evaluate(() =>
|
||||||
window.__godsEyeView.dataManager.setEnabled('earthquakes', true, {
|
window.__godsEyeView.dataManager.setEnabled('earthquakes', true, {
|
||||||
origin: 'user',
|
origin: 'user',
|
||||||
@@ -384,8 +370,7 @@ await strict.run(
|
|||||||
});
|
});
|
||||||
const session = () =>
|
const session = () =>
|
||||||
page.evaluate(() => {
|
page.evaluate(() => {
|
||||||
const { dataManager, styleManager, sceneDirector } =
|
const { dataManager, styleManager, sceneDirector } = window.__godsEyeView;
|
||||||
window.__godsEyeView;
|
|
||||||
const { director, context, vessels } = window.qaSession;
|
const { director, context, vessels } = window.qaSession;
|
||||||
return {
|
return {
|
||||||
director: director === sceneDirector && !director._destroyed,
|
director: director === sceneDirector && !director._destroyed,
|
||||||
@@ -480,5 +465,4 @@ await strict.run(
|
|||||||
console.log(
|
console.log(
|
||||||
'PASS Cesium default, both switches, same document, warm engine identity, parked canvas, no startup splash, share pose/map/layers, Back/Forward, remembered engine, blocked preference storage',
|
'PASS Cesium default, both switches, same document, warm engine identity, parked canvas, no startup splash, share pose/map/layers, Back/Forward, remembered engine, blocked preference storage',
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
@@ -67,7 +67,7 @@ const getOpt = (name, dflt) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const BASE_URL = getOpt('--url', 'http://localhost:4173');
|
const BASE_URL = getOpt('--url', 'http://localhost:4173');
|
||||||
// --engine=cesium|navara (phase 3): Navara reads the layer through the QA
|
// --engine=cesium|navara: Navara reads the layer through the QA
|
||||||
// probe (window.__godsEyeView.layers.drawn) and moves the engine camera.
|
// probe (window.__godsEyeView.layers.drawn) and moves the engine camera.
|
||||||
const ENGINE = (argv.find((arg) => arg.startsWith('--engine=')) ?? '--engine=cesium').slice(9);
|
const ENGINE = (argv.find((arg) => arg.startsWith('--engine=')) ?? '--engine=cesium').slice(9);
|
||||||
const APP_URL =
|
const APP_URL =
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const strict = createStrict({
|
|||||||
// installAlprFixture answers the camera tiles.
|
// installAlprFixture answers the camera tiles.
|
||||||
fixtures: ['https://tiles.dontgetflocked.com/*'],
|
fixtures: ['https://tiles.dontgetflocked.com/*'],
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const p = await b.newPage();
|
||||||
await p.setViewport({ width: 1280, height: 900 });
|
await p.setViewport({ width: 1280, height: 900 });
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import fs from 'node:fs';
|
|||||||
// QA_BASE_URL matches the sibling harnesses (qa-height-datum / qa-cctv-v2) so a
|
// QA_BASE_URL matches the sibling harnesses (qa-height-datum / qa-cctv-v2) so a
|
||||||
// secondary checkout can verify against its own dev server instead of the default :4173.
|
// secondary checkout can verify against its own dev server instead of the default :4173.
|
||||||
const BASE_URL = process.env.QA_BASE_URL || 'http://localhost:4173';
|
const BASE_URL = process.env.QA_BASE_URL || 'http://localhost:4173';
|
||||||
// --engine=cesium|navara (phase 3): on Navara the camera, the mesh probes and
|
// --engine=cesium|navara: on Navara the camera, the mesh probes and
|
||||||
// the sprite census go through the engine and the QA probe.
|
// the sprite census go through the engine and the QA probe.
|
||||||
const ENGINE = (process.argv.find((arg) => arg.startsWith('--engine=')) ?? '--engine=cesium').slice(9);
|
const ENGINE = (process.argv.find((arg) => arg.startsWith('--engine=')) ?? '--engine=cesium').slice(9);
|
||||||
const APP_URL =
|
const APP_URL =
|
||||||
@@ -125,7 +125,7 @@ const report = await page.evaluate(async () => {
|
|||||||
drawnBy: modelAt ? 'model' : 'sprite',
|
drawnBy: modelAt ? 'model' : 'sprite',
|
||||||
meshM: meshH != null ? +meshH.toFixed(1) : null,
|
meshM: meshH != null ? +meshH.toFixed(1) : null,
|
||||||
aboveMeshM: meshH != null ? +(c.height - meshH).toFixed(1) : null,
|
aboveMeshM: meshH != null ? +(c.height - meshH).toFixed(1) : null,
|
||||||
// Why a contact sits where it does (10b review 6): the probe's own
|
// Why a contact sits where it does: the probe's own
|
||||||
// answer (source, tile size) and the coarse DEM floor (the prior).
|
// answer (source, tile size) and the coarse DEM floor (the prior).
|
||||||
probe: answer ? { source: answer.source, tileSizeM: answer.tileSizeM ?? null } : null,
|
probe: answer ? { source: answer.source, tileSizeM: answer.tileSizeM ?? null } : null,
|
||||||
demFloorM: cachedGroundFloor(c.latitude * 180 / Math.PI, c.longitude * 180 / Math.PI) ?? null,
|
demFloorM: cachedGroundFloor(c.latitude * 180 / Math.PI, c.longitude * 180 / Math.PI) ?? null,
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
import { fromDegrees } from '../src/geo/cartesian3.js';
|
import { fromDegrees } from '../src/geo/cartesian3.js';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
base.search = '?engine=navara&welcome=0&look=cinematic';
|
base.search = '?engine=navara&welcome=0&look=cinematic';
|
||||||
base.hash =
|
base.hash =
|
||||||
'v=2&lat=43.6500&lon=-79.3800&alt=46000&heading=0&pitch=-90&roll=0&map=sentinel2&l=';
|
'v=2&lat=43.6500&lon=-79.3800&alt=46000&heading=0&pitch=-90&roll=0&map=sentinel2&l=';
|
||||||
const strict = createStrict({ origin: base.origin, layers: ['s2-ghostmaps'] });
|
const strict = createStrict({ origin: base.origin, layers: ['s2-ghostmaps'] });
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b);
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
const file = await readFile(
|
const file = await readFile(
|
||||||
new URL('./fixtures/ghostmaps-scenes.kmz', import.meta.url),
|
new URL('./fixtures/ghostmaps-scenes.kmz', import.meta.url),
|
||||||
);
|
);
|
||||||
@@ -63,21 +62,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
[...file],
|
[...file],
|
||||||
);
|
);
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(p, 'navara');
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
await p.evaluate(async () => {
|
await p.evaluate(async () => {
|
||||||
const { dataManager } = window.__godsEyeView;
|
const { dataManager } = window.__godsEyeView;
|
||||||
await dataManager.setEnabled('s2-ghostmaps', true, { origin: 'user' });
|
await dataManager.setEnabled('s2-ghostmaps', true, { origin: 'user' });
|
||||||
@@ -111,8 +96,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
await p.evaluate(() => {
|
await p.evaluate(() => {
|
||||||
window.ghostStats.titles = [];
|
window.ghostStats.titles = [];
|
||||||
});
|
});
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
await p.waitForFunction(() =>
|
await p.waitForFunction(() =>
|
||||||
window.ghostStats.titles.some((v) => /QA zone/i.test(v)),
|
window.ghostStats.titles.some((v) => /QA zone/i.test(v)),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { fromDegrees } from '../src/geo/cartesian3.js';
|
import { fromDegrees } from '../src/geo/cartesian3.js';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
base.search = '?engine=navara&welcome=0&look=cinematic';
|
base.search = '?engine=navara&welcome=0&look=cinematic';
|
||||||
@@ -16,14 +21,8 @@ const strict = createStrict({
|
|||||||
'fire-perimeters',
|
'fire-perimeters',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b);
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
await p.evaluateOnNewDocument(() => {
|
await p.evaluateOnNewDocument(() => {
|
||||||
const fetch = window.fetch.bind(window);
|
const fetch = window.fetch.bind(window);
|
||||||
window.fetch = (input, o) => {
|
window.fetch = (input, o) => {
|
||||||
@@ -122,20 +121,6 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const capture = async (visible, layerId = 'air-quality') => {
|
const capture = async (visible, layerId = 'air-quality') => {
|
||||||
await p.evaluate(
|
await p.evaluate(
|
||||||
async (show, id) => {
|
async (show, id) => {
|
||||||
@@ -230,7 +215,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
}
|
}
|
||||||
return on;
|
return on;
|
||||||
};
|
};
|
||||||
await ready('navara');
|
await waitForEngine(p, 'navara');
|
||||||
await p.evaluate(() => {
|
await p.evaluate(() => {
|
||||||
const d = window.__godsEyeView;
|
const d = window.__godsEyeView;
|
||||||
if (d.tileset || window.__GOOGLE_MAPS_API_KEY__)
|
if (d.tileset || window.__GOOGLE_MAPS_API_KEY__)
|
||||||
@@ -240,8 +225,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
});
|
});
|
||||||
for (const kind of ['navara', 'cesium', 'navara']) {
|
for (const kind of ['navara', 'cesium', 'navara']) {
|
||||||
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
const same = await p.evaluate(
|
const same = await p.evaluate(
|
||||||
() =>
|
() =>
|
||||||
window.__godsEyeView.dataManager.layers.get('air-quality').module ===
|
window.__godsEyeView.dataManager.layers.get('air-quality').module ===
|
||||||
@@ -357,8 +341,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
};
|
};
|
||||||
for (const kind of ['navara', 'cesium', 'navara']) {
|
for (const kind of ['navara', 'cesium', 'navara']) {
|
||||||
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
await card(true);
|
await card(true);
|
||||||
await strict.check(
|
await strict.check(
|
||||||
p,
|
p,
|
||||||
@@ -478,8 +461,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
let histories = await p.evaluate(() => window.qaVesselHistories);
|
let histories = await p.evaluate(() => window.qaVesselHistories);
|
||||||
for (const kind of ['navara', 'cesium', 'navara']) {
|
for (const kind of ['navara', 'cesium', 'navara']) {
|
||||||
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
}
|
}
|
||||||
const sample = await p.evaluate(() => {
|
const sample = await p.evaluate(() => {
|
||||||
const entry =
|
const entry =
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { fromDegrees } from '../src/geo/cartesian3.js';
|
import { fromDegrees } from '../src/geo/cartesian3.js';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
base.search = '?engine=navara&welcome=0&look=cinematic';
|
base.search = '?engine=navara&welcome=0&look=cinematic';
|
||||||
@@ -26,14 +31,8 @@ const strict = createStrict({
|
|||||||
origin: base.origin,
|
origin: base.origin,
|
||||||
layers: ['local-dams', 'local-datacenters'],
|
layers: ['local-dams', 'local-datacenters'],
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b);
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
const reads = { datacenters: 0, dams: 0 };
|
const reads = { datacenters: 0, dams: 0 };
|
||||||
p.on('request', (req) => {
|
p.on('request', (req) => {
|
||||||
const path = new URL(req.url()).pathname;
|
const path = new URL(req.url()).pathname;
|
||||||
@@ -52,21 +51,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(p, 'navara');
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
const ids = ['local-datacenters', 'local-dams'];
|
const ids = ['local-datacenters', 'local-dams'];
|
||||||
const dataset = await p.evaluate(async (ids) => {
|
const dataset = await p.evaluate(async (ids) => {
|
||||||
const { dataManager, tileset } = window.__godsEyeView;
|
const { dataManager, tileset } = window.__godsEyeView;
|
||||||
@@ -184,8 +169,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
await p.evaluate(() => {
|
await p.evaluate(() => {
|
||||||
window.qaSelections = [];
|
window.qaSelections = [];
|
||||||
});
|
});
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
await p.waitForFunction(
|
await p.waitForFunction(
|
||||||
() => window.qaSelections.includes('local-datacenters:342664739'),
|
() => window.qaSelections.includes('local-datacenters:342664739'),
|
||||||
{ timeout: 15000 },
|
{ timeout: 15000 },
|
||||||
|
|||||||
@@ -314,8 +314,7 @@ async function main() {
|
|||||||
(ids) => ids.filter((id) => !window.__godsEyeView.layers.available(id)),
|
(ids) => ids.filter((id) => !window.__godsEyeView.layers.available(id)),
|
||||||
fieldLayers,
|
fieldLayers,
|
||||||
);
|
);
|
||||||
// An engine with flights but not the other field layers (Navara, W1 so
|
// An engine with flights but not the other field layers: the same 12,000 / 5,200 observations, all as aircraft, so the
|
||||||
// far): the same 12,000 / 5,200 observations, all as aircraft, so the
|
|
||||||
// detection lane is measured on the layers the engine has.
|
// detection lane is measured on the layers the engine has.
|
||||||
let fieldCounts = FIELD_COUNTS;
|
let fieldCounts = FIELD_COUNTS;
|
||||||
let normalCounts = NORMAL_COUNTS;
|
let normalCounts = NORMAL_COUNTS;
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ url.hash =
|
|||||||
// left.
|
// left.
|
||||||
const HIDDEN_PICKER = 'the Globe Engine picker is hidden in cockpit mode';
|
const HIDDEN_PICKER = 'the Globe Engine picker is hidden in cockpit mode';
|
||||||
const strict = createStrict({ origin: url.origin, layers: [layerId] });
|
const strict = createStrict({ origin: url.origin, layers: [layerId] });
|
||||||
await strict.run(
|
await strict.run({}, async (browser) => {
|
||||||
{ args: ['--use-angle=metal', '--enable-gpu'] },
|
|
||||||
async (browser) => {
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
const errors = [];
|
const errors = [];
|
||||||
page.on('pageerror', (e) => errors.push(e.message));
|
page.on('pageerror', (e) => errors.push(e.message));
|
||||||
@@ -270,10 +268,7 @@ await strict.run(
|
|||||||
stamp: entry.module.getStats().lastUpdate,
|
stamp: entry.module.getStats().lastUpdate,
|
||||||
};
|
};
|
||||||
const deadline = Date.now() + 2.5 * every + 5000;
|
const deadline = Date.now() + 2.5 * every + 5000;
|
||||||
while (
|
while (window.__handoff.ticks - start.ticks < 2 && Date.now() < deadline)
|
||||||
window.__handoff.ticks - start.ticks < 2 &&
|
|
||||||
Date.now() < deadline
|
|
||||||
)
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 250));
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
||||||
// Let the last tick's request land.
|
// Let the last tick's request land.
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||||
@@ -297,8 +292,7 @@ await strict.run(
|
|||||||
polls,
|
polls,
|
||||||
);
|
);
|
||||||
await page.waitForFunction(
|
await page.waitForFunction(
|
||||||
() =>
|
() => Math.abs(window.__godsEyeView.engine.camera.height() - 9700) < 1000,
|
||||||
Math.abs(window.__godsEyeView.engine.camera.height() - 9700) < 1000,
|
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
`PASS ${kind} cockpit, aircraft, HUD, manager and timer (${result.sourcePolls} polls since entry)`,
|
`PASS ${kind} cockpit, aircraft, HUD, manager and timer (${result.sourcePolls} polls since entry)`,
|
||||||
@@ -372,10 +366,7 @@ await strict.run(
|
|||||||
// retry starts Cesium cold on the same session without a reload.
|
// retry starts Cesium cold on the same session without a reload.
|
||||||
const lengthBeforeDoubleFailure = await page.evaluate(() => {
|
const lengthBeforeDoubleFailure = await page.evaluate(() => {
|
||||||
window.__failResume = true;
|
window.__failResume = true;
|
||||||
for (const engine of [
|
for (const engine of [window.__godsEyeView.engine, window.__parkedCesium]) {
|
||||||
window.__godsEyeView.engine,
|
|
||||||
window.__parkedCesium,
|
|
||||||
]) {
|
|
||||||
const request = engine.render.request;
|
const request = engine.render.request;
|
||||||
engine.render.request = function (reason, ...args) {
|
engine.render.request = function (reason, ...args) {
|
||||||
if (reason === 'engine-resume' && window.__failResume) {
|
if (reason === 'engine-resume' && window.__failResume) {
|
||||||
@@ -422,8 +413,7 @@ await strict.run(
|
|||||||
.querySelector('#loading-screen')
|
.querySelector('#loading-screen')
|
||||||
.classList.contains('hidden') &&
|
.classList.contains('hidden') &&
|
||||||
/QA parked renderer failure/.test(
|
/QA parked renderer failure/.test(
|
||||||
document.querySelector('#loading-screen .loader-status')
|
document.querySelector('#loading-screen .loader-status').textContent,
|
||||||
.textContent,
|
|
||||||
),
|
),
|
||||||
lengthBeforeDoubleFailure,
|
lengthBeforeDoubleFailure,
|
||||||
),
|
),
|
||||||
@@ -524,5 +514,4 @@ await strict.run(
|
|||||||
after,
|
after,
|
||||||
);
|
);
|
||||||
assert.deepEqual(errors, []);
|
assert.deepEqual(errors, []);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
@@ -1,19 +1,18 @@
|
|||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
base.search = '?engine=navara&welcome=0&look=cinematic';
|
base.search = '?engine=navara&welcome=0&look=cinematic';
|
||||||
base.hash =
|
base.hash =
|
||||||
'v=2&lat=41.5340&lon=-90.5070&alt=20000&heading=0&pitch=-90&roll=0&map=sentinel2&l=';
|
'v=2&lat=41.5340&lon=-90.5070&alt=20000&heading=0&pitch=-90&roll=0&map=sentinel2&l=';
|
||||||
const strict = createStrict({ origin: base.origin, layers: ['local-adsb'] });
|
const strict = createStrict({ origin: base.origin, layers: ['local-adsb'] });
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b);
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
await p.evaluateOnNewDocument(() => {
|
await p.evaluateOnNewDocument(() => {
|
||||||
const raw = window.fetch;
|
const raw = window.fetch;
|
||||||
window.fetch = function (input, ...args) {
|
window.fetch = function (input, ...args) {
|
||||||
@@ -53,21 +52,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(p, 'navara');
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
await p.evaluate(async () => {
|
await p.evaluate(async () => {
|
||||||
const { dataManager, tileset } = window.__godsEyeView;
|
const { dataManager, tileset } = window.__godsEyeView;
|
||||||
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
||||||
@@ -107,8 +92,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
await p.evaluate(() => {
|
await p.evaluate(() => {
|
||||||
window.qaSelections = [];
|
window.qaSelections = [];
|
||||||
});
|
});
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
await p.waitForFunction(() =>
|
await p.waitForFunction(() =>
|
||||||
window.qaSelections.includes('local-adsb:abc123'),
|
window.qaSelections.includes('local-adsb:abc123'),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,7 +9,11 @@
|
|||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const LAT = 37.7749;
|
const LAT = 37.7749;
|
||||||
const LON = -122.4194;
|
const LON = -122.4194;
|
||||||
@@ -31,8 +35,6 @@ const RING = [
|
|||||||
await strict.run(
|
await strict.run(
|
||||||
{
|
{
|
||||||
args: [
|
args: [
|
||||||
'--use-angle=metal',
|
|
||||||
'--enable-gpu',
|
|
||||||
'--disable-background-timer-throttling',
|
'--disable-background-timer-throttling',
|
||||||
'--disable-renderer-backgrounding',
|
'--disable-renderer-backgrounding',
|
||||||
],
|
],
|
||||||
@@ -43,21 +45,7 @@ await strict.run(
|
|||||||
const errors = [];
|
const errors = [];
|
||||||
page.on('pageerror', (error) => errors.push(error.message));
|
page.on('pageerror', (error) => errors.push(error.message));
|
||||||
await page.goto(base.href);
|
await page.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(page, 'navara');
|
||||||
await page.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 120000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await page.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await page.evaluate(() =>
|
await page.evaluate(() =>
|
||||||
Boolean(window.__godsEyeView.tileset || window.__GOOGLE_MAPS_API_KEY__),
|
Boolean(window.__godsEyeView.tileset || window.__GOOGLE_MAPS_API_KEY__),
|
||||||
@@ -172,8 +160,7 @@ await strict.run(
|
|||||||
if (
|
if (
|
||||||
(await page.evaluate(() => window.__godsEyeView.engine.kind)) !== kind
|
(await page.evaluate(() => window.__godsEyeView.engine.kind)) !== kind
|
||||||
) {
|
) {
|
||||||
await strict.pick(page, kind);
|
await strict.switchTo(page, kind);
|
||||||
await ready(kind);
|
|
||||||
}
|
}
|
||||||
await wait(2500);
|
await wait(2500);
|
||||||
const state = await page.evaluate(() => ({
|
const state = await page.evaluate(() => ({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The app's Bloom on Navara (Stage 6 B2): the Display bloom glows the
|
// The app's Bloom on Navara: the Display bloom glows the
|
||||||
// Navara frame (brighter, more at a higher slider), it sits after the
|
// Navara frame (brighter, more at a higher slider), it sits after the
|
||||||
// overlays and before the visual styles, Cesium's dead zone draws nothing,
|
// overlays and before the visual styles, Cesium's dead zone draws nothing,
|
||||||
// turning it off restores the frame exactly, and on Cesium the scene's own
|
// turning it off restores the frame exactly, and on Cesium the scene's own
|
||||||
@@ -53,9 +53,7 @@ const setBloom = (page, enabled, intensityPct) =>
|
|||||||
intensityPct,
|
intensityPct,
|
||||||
);
|
);
|
||||||
|
|
||||||
await strict.run(
|
await strict.run({}, async (browser) => {
|
||||||
{ args: ['--use-angle=metal', '--enable-gpu'] },
|
|
||||||
async (browser) => {
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
const errors = [];
|
const errors = [];
|
||||||
page.on('pageerror', (error) => errors.push(error.message));
|
page.on('pageerror', (error) => errors.push(error.message));
|
||||||
@@ -201,5 +199,4 @@ await strict.run(
|
|||||||
path.join(outDir, 'bloom.json'),
|
path.join(outDir, 'bloom.json'),
|
||||||
JSON.stringify(report, null, 2),
|
JSON.stringify(report, null, 2),
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// The cockpit weather clouds on Navara (Stage 6 C3): in cockpit mode with
|
// The cockpit weather clouds on Navara: in cockpit mode with
|
||||||
// the cockpit weather on, Navara's own volumetric clouds take their cover
|
// the cockpit weather on, Navara's own volumetric clouds take their cover
|
||||||
// from the weather under the camera (clear: none; overcast: a heavy deck),
|
// from the weather under the camera (clear: none; overcast: a heavy deck),
|
||||||
// with no second canvas or WebGL context; leaving the cockpit gives the look
|
// with no second canvas or WebGL context; leaving the cockpit gives the look
|
||||||
@@ -101,9 +101,7 @@ const coverage = (page) =>
|
|||||||
() => window.__godsEyeView.engine.render.cloudCover?.().coverage,
|
() => window.__godsEyeView.engine.render.cloudCover?.().coverage,
|
||||||
);
|
);
|
||||||
|
|
||||||
await strict.run(
|
await strict.run({}, async (browser) => {
|
||||||
{ args: ['--use-angle=metal', '--enable-gpu'] },
|
|
||||||
async (browser) => {
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
const errors = [];
|
const errors = [];
|
||||||
page.on('pageerror', (error) => errors.push(error.message));
|
page.on('pageerror', (error) => errors.push(error.message));
|
||||||
@@ -160,8 +158,7 @@ await strict.run(
|
|||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
const { engine } = window.__godsEyeView;
|
const { engine } = window.__godsEyeView;
|
||||||
const place = engine.camera.pose().cartographic;
|
const place = engine.camera.pose().cartographic;
|
||||||
const east =
|
const east = place.longitude + 30000 / (6378137 * Math.cos(place.latitude));
|
||||||
place.longitude + 30000 / (6378137 * Math.cos(place.latitude));
|
|
||||||
const { sin, cos } = Math;
|
const { sin, cos } = Math;
|
||||||
// WGS84 Earth-fixed position of (east, latitude, height).
|
// WGS84 Earth-fixed position of (east, latitude, height).
|
||||||
const a = 6378137;
|
const a = 6378137;
|
||||||
@@ -221,8 +218,7 @@ await strict.run(
|
|||||||
await page.waitForFunction(
|
await page.waitForFunction(
|
||||||
() =>
|
() =>
|
||||||
Number(
|
Number(
|
||||||
document.getElementById('cockpit-cloud-effects')?.dataset
|
document.getElementById('cockpit-cloud-effects')?.dataset.cloudStrength,
|
||||||
.cloudStrength,
|
|
||||||
) > 0.3,
|
) > 0.3,
|
||||||
{ timeout: 15000, polling: 100 },
|
{ timeout: 15000, polling: 100 },
|
||||||
);
|
);
|
||||||
@@ -248,5 +244,4 @@ await strict.run(
|
|||||||
path.join(outDir, 'cockpit-clouds.json'),
|
path.join(outDir, 'cockpit-clouds.json'),
|
||||||
JSON.stringify(report, null, 2),
|
JSON.stringify(report, null, 2),
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
@@ -32,8 +32,6 @@ const AREA = [
|
|||||||
await strict.run(
|
await strict.run(
|
||||||
{
|
{
|
||||||
args: [
|
args: [
|
||||||
'--use-angle=metal',
|
|
||||||
'--enable-gpu',
|
|
||||||
'--disable-background-timer-throttling',
|
'--disable-background-timer-throttling',
|
||||||
'--disable-renderer-backgrounding',
|
'--disable-renderer-backgrounding',
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -136,8 +136,6 @@ const FILES = [
|
|||||||
await strict.run(
|
await strict.run(
|
||||||
{
|
{
|
||||||
args: [
|
args: [
|
||||||
'--use-angle=metal',
|
|
||||||
'--enable-gpu',
|
|
||||||
'--disable-background-timer-throttling',
|
'--disable-background-timer-throttling',
|
||||||
'--disable-renderer-backgrounding',
|
'--disable-renderer-backgrounding',
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ import assert from 'node:assert/strict';
|
|||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import { fromDegrees } from '../src/geo/cartesian3.js';
|
import { fromDegrees } from '../src/geo/cartesian3.js';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
base.search = '?engine=navara&welcome=0';
|
base.search = '?engine=navara&welcome=0';
|
||||||
@@ -31,8 +35,6 @@ const EDGE = { x: 1250, y: 560, width: 26, height: 90 };
|
|||||||
await strict.run(
|
await strict.run(
|
||||||
{
|
{
|
||||||
args: [
|
args: [
|
||||||
'--use-angle=metal',
|
|
||||||
'--enable-gpu',
|
|
||||||
'--disable-background-timer-throttling',
|
'--disable-background-timer-throttling',
|
||||||
'--disable-renderer-backgrounding',
|
'--disable-renderer-backgrounding',
|
||||||
],
|
],
|
||||||
@@ -43,21 +45,7 @@ await strict.run(
|
|||||||
const errors = [];
|
const errors = [];
|
||||||
page.on('pageerror', (error) => errors.push(error.message));
|
page.on('pageerror', (error) => errors.push(error.message));
|
||||||
await page.goto(base.href);
|
await page.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(page, 'navara');
|
||||||
await page.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 120000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await page.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await page.evaluate(() =>
|
await page.evaluate(() =>
|
||||||
Boolean(window.__godsEyeView.tileset || window.__GOOGLE_MAPS_API_KEY__),
|
Boolean(window.__godsEyeView.tileset || window.__GOOGLE_MAPS_API_KEY__),
|
||||||
@@ -92,8 +80,7 @@ await strict.run(
|
|||||||
if (
|
if (
|
||||||
(await page.evaluate(() => window.__godsEyeView.engine.kind)) !== kind
|
(await page.evaluate(() => window.__godsEyeView.engine.kind)) !== kind
|
||||||
) {
|
) {
|
||||||
await strict.pick(page, kind);
|
await strict.switchTo(page, kind);
|
||||||
await ready(kind);
|
|
||||||
}
|
}
|
||||||
await wait(2500);
|
await wait(2500);
|
||||||
const mask = await page.evaluate(() => {
|
const mask = await page.evaluate(() => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The Cyber HUD sonar on Navara (Stage 6 C2): with the Cyber HUD on, the
|
// The Cyber HUD sonar on Navara: with the Cyber HUD on, the
|
||||||
// sweep lights the contacts it passes and dims the rest to the opacity
|
// sweep lights the contacts it passes and dims the rest to the opacity
|
||||||
// floor, by each contact's place on screen exactly as the CPU sampler of
|
// floor, by each contact's place on screen exactly as the CPU sampler of
|
||||||
// src/cyberSonar.js rates it; the sweep turns; with the sweep off every
|
// src/cyberSonar.js rates it; the sweep turns; with the sweep off every
|
||||||
@@ -98,9 +98,7 @@ const framesIn = (page, ms) =>
|
|||||||
const mean = (values) =>
|
const mean = (values) =>
|
||||||
values.reduce((sum, value) => sum + value, 0) / Math.max(1, values.length);
|
values.reduce((sum, value) => sum + value, 0) / Math.max(1, values.length);
|
||||||
|
|
||||||
await strict.run(
|
await strict.run({}, async (browser) => {
|
||||||
{ args: ['--use-angle=metal', '--enable-gpu'] },
|
|
||||||
async (browser) => {
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
const errors = [];
|
const errors = [];
|
||||||
page.on('pageerror', (error) => errors.push(error.message));
|
page.on('pageerror', (error) => errors.push(error.message));
|
||||||
@@ -265,8 +263,7 @@ await strict.run(
|
|||||||
{ timeout: 10000 },
|
{ timeout: 10000 },
|
||||||
);
|
);
|
||||||
const cesium = await page.evaluate(() => ({
|
const cesium = await page.evaluate(() => ({
|
||||||
setContactSonar:
|
setContactSonar: typeof window.__godsEyeView.engine.render.setContactSonar,
|
||||||
typeof window.__godsEyeView.engine.render.setContactSonar,
|
|
||||||
gpu: document.documentElement.dataset.cyberSonarGpu,
|
gpu: document.documentElement.dataset.cyberSonarGpu,
|
||||||
}));
|
}));
|
||||||
check(
|
check(
|
||||||
@@ -286,5 +283,4 @@ await strict.run(
|
|||||||
JSON.stringify(report, null, 2),
|
JSON.stringify(report, null, 2),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
);
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// The app's visual styles on Navara (Stage 6 B1): each of CRT, NVG, FLIR,
|
// The app's visual styles on Navara: each of CRT, NVG, FLIR,
|
||||||
// Anime and Noir filters the Navara frame (pixels and a style-specific
|
// Anime and Noir filters the Navara frame (pixels and a style-specific
|
||||||
// signature against Normal), switching styles crossfades (both styles drawn
|
// signature against Normal), switching styles crossfades (both styles drawn
|
||||||
// at once mid-way, the fade completes), Normal restores the frame, the
|
// at once mid-way, the fade completes), Normal restores the frame, the
|
||||||
@@ -45,9 +45,7 @@ const check = checker(report, assert);
|
|||||||
|
|
||||||
const save = (name, pixels) => saveCapture(outDir, name, pixels);
|
const save = (name, pixels) => saveCapture(outDir, name, pixels);
|
||||||
|
|
||||||
await strict.run(
|
await strict.run({}, async (browser) => {
|
||||||
{ args: ['--use-angle=metal', '--enable-gpu'] },
|
|
||||||
async (browser) => {
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
const errors = [];
|
const errors = [];
|
||||||
page.on('pageerror', (error) => errors.push(error.message));
|
page.on('pageerror', (error) => errors.push(error.message));
|
||||||
@@ -88,11 +86,7 @@ await strict.run(
|
|||||||
const normal = await capture(page);
|
const normal = await capture(page);
|
||||||
const normalAgain = await capture(page);
|
const normalAgain = await capture(page);
|
||||||
const noise = diff(normal, normalAgain);
|
const noise = diff(normal, normalAgain);
|
||||||
check(
|
check('Normal is a still frame to compare against', noise.mean < 0.5, noise);
|
||||||
'Normal is a still frame to compare against',
|
|
||||||
noise.mean < 0.5,
|
|
||||||
noise,
|
|
||||||
);
|
|
||||||
save('normal', normal);
|
save('normal', normal);
|
||||||
const normalStats = stats(normal);
|
const normalStats = stats(normal);
|
||||||
|
|
||||||
@@ -114,8 +108,7 @@ await strict.run(
|
|||||||
);
|
);
|
||||||
if (previous !== 'normal') {
|
if (previous !== 'normal') {
|
||||||
const both = fade.samples.filter(
|
const both = fade.samples.filter(
|
||||||
([out, next]) =>
|
([out, next]) => out > 0.05 && out < 0.95 && next > 0.05 && next < 0.95,
|
||||||
out > 0.05 && out < 0.95 && next > 0.05 && next < 0.95,
|
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
`${name}: ${previous} and ${name} draw together mid-crossfade`,
|
`${name}: ${previous} and ${name} draw together mid-crossfade`,
|
||||||
@@ -163,8 +156,7 @@ await strict.run(
|
|||||||
styled.saturation < normalStats.saturation &&
|
styled.saturation < normalStats.saturation &&
|
||||||
styled.vignette < normalStats.vignette * 0.85,
|
styled.vignette < normalStats.vignette * 0.85,
|
||||||
// Anime: cel bands, more saturated.
|
// Anime: cel bands, more saturated.
|
||||||
anime:
|
anime: styled.saturation > normalStats.saturation * 1.05 || shift > 0.05,
|
||||||
styled.saturation > normalStats.saturation * 1.05 || shift > 0.05,
|
|
||||||
// CRT: pixelated, dithered, scanlined.
|
// CRT: pixelated, dithered, scanlined.
|
||||||
retro: shift > 0.05 || change.mean > 12,
|
retro: shift > 0.05 || change.mean > 12,
|
||||||
}[name];
|
}[name];
|
||||||
@@ -307,11 +299,7 @@ await strict.run(
|
|||||||
cesiumState,
|
cesiumState,
|
||||||
);
|
);
|
||||||
const cesiumChange = diff(cesiumNormal, cesiumThermal);
|
const cesiumChange = diff(cesiumNormal, cesiumThermal);
|
||||||
check(
|
check('Cesium: FLIR changes its frame', cesiumChange.mean > 6, cesiumChange);
|
||||||
'Cesium: FLIR changes its frame',
|
|
||||||
cesiumChange.mean > 6,
|
|
||||||
cesiumChange,
|
|
||||||
);
|
|
||||||
await pickStyle(page, 'normal', 'thermal');
|
await pickStyle(page, 'normal', 'thermal');
|
||||||
await page.evaluate((sharpen) => {
|
await page.evaluate((sharpen) => {
|
||||||
const { styleManager } = window.__godsEyeView;
|
const { styleManager } = window.__godsEyeView;
|
||||||
@@ -319,11 +307,7 @@ await strict.run(
|
|||||||
styleManager.setSharpen(sharpen);
|
styleManager.setSharpen(sharpen);
|
||||||
}, display);
|
}, display);
|
||||||
const cesiumBack = diff(cesiumNormal, await capture(page));
|
const cesiumBack = diff(cesiumNormal, await capture(page));
|
||||||
check(
|
check('Cesium: Normal restores its frame', cesiumBack.mean < 1.5, cesiumBack);
|
||||||
'Cesium: Normal restores its frame',
|
|
||||||
cesiumBack.mean < 1.5,
|
|
||||||
cesiumBack,
|
|
||||||
);
|
|
||||||
|
|
||||||
check('no page errors', errors.length === 0, errors);
|
check('no page errors', errors.length === 0, errors);
|
||||||
if (outDir)
|
if (outDir)
|
||||||
@@ -331,5 +315,4 @@ await strict.run(
|
|||||||
path.join(outDir, 'visual-styles.json'),
|
path.join(outDir, 'visual-styles.json'),
|
||||||
JSON.stringify(report, null, 2),
|
JSON.stringify(report, null, 2),
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
base.search = '?engine=navara&welcome=0&look=cinematic';
|
base.search = '?engine=navara&welcome=0&look=cinematic';
|
||||||
@@ -54,18 +59,8 @@ const strict = createStrict({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b, { shaderErrors: true });
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
p.on('console', (msg) => {
|
|
||||||
if (msg.type() === 'error' && /Shader|WebGLProgram/i.test(msg.text()))
|
|
||||||
errors.push(msg.text());
|
|
||||||
});
|
|
||||||
const assets = [];
|
const assets = [];
|
||||||
p.on('request', (req) => {
|
p.on('request', (req) => {
|
||||||
const path = new URL(req.url()).pathname;
|
const path = new URL(req.url()).pathname;
|
||||||
@@ -77,21 +72,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
assets.push(path);
|
assets.push(path);
|
||||||
});
|
});
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(p, 'navara');
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
await p.evaluate(async () => {
|
await p.evaluate(async () => {
|
||||||
const { tileset, dataManager, sceneDirector } = window.__godsEyeView;
|
const { tileset, dataManager, sceneDirector } = window.__godsEyeView;
|
||||||
if (!sceneDirector?.listScenes().length)
|
if (!sceneDirector?.listScenes().length)
|
||||||
@@ -118,8 +99,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
};
|
};
|
||||||
for (const kind of ['navara', 'cesium', 'navara']) {
|
for (const kind of ['navara', 'cesium', 'navara']) {
|
||||||
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
}
|
}
|
||||||
const state = await p.evaluate(() => {
|
const state = await p.evaluate(() => {
|
||||||
const { dataManager } = window.__godsEyeView;
|
const { dataManager } = window.__godsEyeView;
|
||||||
@@ -184,8 +164,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
await p.waitForFunction(
|
await p.waitForFunction(
|
||||||
() => window.qaEvent.getPlaybackState().progress > 0.53,
|
() => window.qaEvent.getPlaybackState().progress > 0.53,
|
||||||
);
|
);
|
||||||
await strict.pick(p, 'cesium');
|
await strict.switchTo(p, 'cesium');
|
||||||
await ready('cesium');
|
|
||||||
const live = await p.evaluate(() => window.qaEvent.getPlaybackState());
|
const live = await p.evaluate(() => window.qaEvent.getPlaybackState());
|
||||||
await strict.check(
|
await strict.check(
|
||||||
p,
|
p,
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { fromDegrees } from '../src/geo/cartesian3.js';
|
import { fromDegrees } from '../src/geo/cartesian3.js';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
base.search = '?engine=navara&welcome=0&look=cinematic';
|
base.search = '?engine=navara&welcome=0&look=cinematic';
|
||||||
@@ -11,34 +16,10 @@ const strict = createStrict({
|
|||||||
origin: base.origin,
|
origin: base.origin,
|
||||||
layers: ['bhote-koshi-locator'],
|
layers: ['bhote-koshi-locator'],
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b, { shaderErrors: true });
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
p.on('console', (msg) => {
|
|
||||||
if (msg.type() === 'error' && /Shader|WebGLProgram/i.test(msg.text()))
|
|
||||||
errors.push(msg.text());
|
|
||||||
});
|
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(p, 'navara');
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
await p.evaluate(async () => {
|
await p.evaluate(async () => {
|
||||||
const { tileset, dataManager } = window.__godsEyeView;
|
const { tileset, dataManager } = window.__godsEyeView;
|
||||||
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
||||||
@@ -51,8 +32,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
});
|
});
|
||||||
for (const kind of ['navara', 'cesium', 'navara']) {
|
for (const kind of ['navara', 'cesium', 'navara']) {
|
||||||
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
}
|
}
|
||||||
const { same, stats, params, at } = await p.evaluate(
|
const { same, stats, params, at } = await p.evaluate(
|
||||||
(world) => {
|
(world) => {
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
// Drone airspace, OSM places, sea marks and ski pistes on both engines, over
|
// Drone airspace, OSM places, sea marks and ski pistes on both engines, over
|
||||||
// Navara → Cesium → Navara: each draws over the view centre, and the switch
|
// Navara → Cesium → Navara: each draws over the view centre, and the switch
|
||||||
@@ -57,14 +62,8 @@ const strict = createStrict({
|
|||||||
tileRoute('https://www.opensnowmap.org/*', 'ski-pistes'),
|
tileRoute('https://www.opensnowmap.org/*', 'ski-pistes'),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b);
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
await p.evaluateOnNewDocument((ring) => {
|
await p.evaluateOnNewDocument((ring) => {
|
||||||
const fetch = window.fetch.bind(window);
|
const fetch = window.fetch.bind(window);
|
||||||
window.qaZoneReads = 0;
|
window.qaZoneReads = 0;
|
||||||
@@ -101,20 +100,6 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
};
|
};
|
||||||
}, RING);
|
}, RING);
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const setEnabled = (id, on) =>
|
const setEnabled = (id, on) =>
|
||||||
p.evaluate(
|
p.evaluate(
|
||||||
(layerId, show) =>
|
(layerId, show) =>
|
||||||
@@ -177,7 +162,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
const greener = (on, off) =>
|
const greener = (on, off) =>
|
||||||
2 * on[1] - on[0] - on[2] > 2 * off[1] - off[0] - off[2] + 10;
|
2 * on[1] - on[0] - on[2] > 2 * off[1] - off[0] - off[2] + 10;
|
||||||
|
|
||||||
await ready('navara');
|
await waitForEngine(p, 'navara');
|
||||||
await p.evaluate(() => {
|
await p.evaluate(() => {
|
||||||
const { dataManager } = window.__godsEyeView;
|
const { dataManager } = window.__godsEyeView;
|
||||||
window.qaModels = Object.fromEntries(
|
window.qaModels = Object.fromEntries(
|
||||||
@@ -190,8 +175,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
for (const [leg, kind] of ['navara', 'cesium', 'navara'].entries()) {
|
for (const [leg, kind] of ['navara', 'cesium', 'navara'].entries()) {
|
||||||
const readsBeforeSwitch = await p.evaluate(() => window.qaZoneReads);
|
const readsBeforeSwitch = await p.evaluate(() => window.qaZoneReads);
|
||||||
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
const same = await p.evaluate(() =>
|
const same = await p.evaluate(() =>
|
||||||
Object.entries(window.qaModels).every(
|
Object.entries(window.qaModels).every(
|
||||||
([id, model]) =>
|
([id, model]) =>
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ const url = localPreview(process.argv[2]);
|
|||||||
url.search = '?engine=navara&welcome=0';
|
url.search = '?engine=navara&welcome=0';
|
||||||
url.hash = 'v=2&lat=30.27&lon=-97.74&alt=12000&pitch=-90&map=sentinel2&l=';
|
url.hash = 'v=2&lat=30.27&lon=-97.74&alt=12000&pitch=-90&map=sentinel2&l=';
|
||||||
const strict = createStrict({ origin: url.origin, layers: ['radio'] });
|
const strict = createStrict({ origin: url.origin, layers: ['radio'] });
|
||||||
await strict.run(
|
await strict.run({}, async (browser) => {
|
||||||
{ args: ['--use-angle=metal', '--enable-gpu'] },
|
|
||||||
async (browser) => {
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
const errors = [];
|
const errors = [];
|
||||||
page.on('pageerror', (e) => errors.push(e.message));
|
page.on('pageerror', (e) => errors.push(e.message));
|
||||||
@@ -87,5 +85,4 @@ await strict.run(
|
|||||||
console.log(
|
console.log(
|
||||||
'PASS one live radio player, one station directory and the same selection',
|
'PASS one live radio player, one station directory and the same selection',
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
@@ -14,7 +14,7 @@ const strict = createStrict({
|
|||||||
origin: url.origin,
|
origin: url.origin,
|
||||||
layers: [missions ? 'rocket-launches' : 'satellites'],
|
layers: [missions ? 'rocket-launches' : 'satellites'],
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const p = await b.newPage();
|
||||||
const errors = [];
|
const errors = [];
|
||||||
p.on('pageerror', (e) => {
|
p.on('pageerror', (e) => {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const getOpt = (name, dflt) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const BASE_URL = getOpt('--url', 'http://localhost:4173');
|
const BASE_URL = getOpt('--url', 'http://localhost:4173');
|
||||||
// --engine=cesium|navara (phase 3): without a viewer the layer is read through
|
// --engine=cesium|navara: without a viewer the layer is read through
|
||||||
// the QA probe (window.__godsEyeView.layers.drawn) and the military checks are
|
// the QA probe (window.__godsEyeView.layers.drawn) and the military checks are
|
||||||
// skipped where the engine has no military layer.
|
// skipped where the engine has no military layer.
|
||||||
const ENGINE = (argv.find((arg) => arg.startsWith('--engine=')) ?? '--engine=cesium').slice(9);
|
const ENGINE = (argv.find((arg) => arg.startsWith('--engine=')) ?? '--engine=cesium').slice(9);
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
import { serveFixtures, newFixture } from './qa-street-level.mjs';
|
import { serveFixtures, newFixture } from './qa-street-level.mjs';
|
||||||
import { KV_LINE, KV_SEQUENCE_ID } from './fixtures/street-level/kartaView.mjs';
|
import { KV_LINE, KV_SEQUENCE_ID } from './fixtures/street-level/kartaView.mjs';
|
||||||
import {
|
import {
|
||||||
@@ -28,9 +32,7 @@ const strict = createStrict({
|
|||||||
'https://panoramax.openstreetmap.fr/*',
|
'https://panoramax.openstreetmap.fr/*',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
await strict.run(
|
await strict.run({}, async (browser) => {
|
||||||
{ args: ['--use-angle=metal', '--enable-gpu'] },
|
|
||||||
async (browser) => {
|
|
||||||
const p = await browser.newPage();
|
const p = await browser.newPage();
|
||||||
await p.setViewport({ width: 1280, height: 900 });
|
await p.setViewport({ width: 1280, height: 900 });
|
||||||
const errors = [],
|
const errors = [],
|
||||||
@@ -53,21 +55,7 @@ await strict.run(
|
|||||||
photoRequests++;
|
photoRequests++;
|
||||||
});
|
});
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (k) => {
|
await waitForEngine(p, 'navara');
|
||||||
await p.waitForFunction(
|
|
||||||
(kind) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === kind &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
k,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
await p.evaluate(async (provider) => {
|
await p.evaluate(async (provider) => {
|
||||||
const { dataManager, tileset } = window.__godsEyeView;
|
const { dataManager, tileset } = window.__godsEyeView;
|
||||||
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
||||||
@@ -112,8 +100,7 @@ await strict.run(
|
|||||||
window.qaStreet.getUIState().street.open &&
|
window.qaStreet.getUIState().street.open &&
|
||||||
window.qaStreet.getUIState().sequence.images > 0 &&
|
window.qaStreet.getUIState().sequence.images > 0 &&
|
||||||
(document.querySelector('#sl-viewer img')?.naturalWidth > 0 ||
|
(document.querySelector('#sl-viewer img')?.naturalWidth > 0 ||
|
||||||
document.querySelector('#sl-viewer canvas:not([hidden])')?.width >
|
document.querySelector('#sl-viewer canvas:not([hidden])')?.width > 0),
|
||||||
0),
|
|
||||||
);
|
);
|
||||||
await strict.passed(
|
await strict.passed(
|
||||||
p,
|
p,
|
||||||
@@ -147,8 +134,7 @@ await strict.run(
|
|||||||
);
|
);
|
||||||
const reads = photoRequests;
|
const reads = photoRequests;
|
||||||
for (const kind of ['cesium', 'navara']) {
|
for (const kind of ['cesium', 'navara']) {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
// The new globe reloads coverage right after it attaches: compare once
|
// The new globe reloads coverage right after it attaches: compare once
|
||||||
// that load is over.
|
// that load is over.
|
||||||
await until(
|
await until(
|
||||||
@@ -195,8 +181,7 @@ await strict.run(
|
|||||||
// Click-picking on Cesium: close the viewer, switch, click the line again.
|
// Click-picking on Cesium: close the viewer, switch, click the line again.
|
||||||
await p.evaluate(() => window.qaStreet.closeViewer());
|
await p.evaluate(() => window.qaStreet.closeViewer());
|
||||||
await until(() => !window.qaStreet.getUIState().street.open);
|
await until(() => !window.qaStreet.getUIState().street.open);
|
||||||
await strict.pick(p, 'cesium');
|
await strict.switchTo(p, 'cesium');
|
||||||
await ready('cesium');
|
|
||||||
await until(
|
await until(
|
||||||
() =>
|
() =>
|
||||||
!window.qaStreet.getUIState().coverage.loading &&
|
!window.qaStreet.getUIState().coverage.loading &&
|
||||||
@@ -209,5 +194,4 @@ await strict.run(
|
|||||||
console.log(
|
console.log(
|
||||||
'PASS native line pick, same mounted photo and sequence across two switches',
|
'PASS native line pick, same mounted photo and sequence across two switches',
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
@@ -41,7 +41,7 @@ const navaraCode = async (url) => {
|
|||||||
};
|
};
|
||||||
const results = [];
|
const results = [];
|
||||||
strict.extra.contracts = results;
|
strict.extra.contracts = results;
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
for (const start of ['navara', 'cesium']) {
|
for (const start of ['navara', 'cesium']) {
|
||||||
const p = await b.newPage();
|
const p = await b.newPage();
|
||||||
await p.setViewport({ width: 1280, height: 800 });
|
await p.setViewport({ width: 1280, height: 800 });
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const other = (kind) => (kind === 'navara' ? 'cesium' : 'navara');
|
|||||||
const strict = createStrict({ origin: base.origin, layers: [] });
|
const strict = createStrict({ origin: base.origin, layers: [] });
|
||||||
const results = [];
|
const results = [];
|
||||||
strict.extra.contracts = results;
|
strict.extra.contracts = results;
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
let p = null;
|
let p = null;
|
||||||
const errors = [];
|
const errors = [];
|
||||||
// A fresh page on `from`; warm visits the other engine first.
|
// A fresh page on `from`; warm visits the other engine first.
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const other = (kind) => (kind === 'navara' ? 'cesium' : 'navara');
|
|||||||
const strict = createStrict({ origin: base.origin, layers: [] });
|
const strict = createStrict({ origin: base.origin, layers: [] });
|
||||||
const results = [];
|
const results = [];
|
||||||
strict.extra.contracts = results;
|
strict.extra.contracts = results;
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
// A fresh page on `start` with the session; `warm` visits the other
|
// A fresh page on `start` with the session; `warm` visits the other
|
||||||
// engine first so it is parked.
|
// engine first so it is parked.
|
||||||
const open = async (start, warm) => {
|
const open = async (start, warm) => {
|
||||||
|
|||||||
@@ -29,8 +29,21 @@ import {
|
|||||||
import { createMissLoader, tileCacheFile } from './qa/tileCache.mjs';
|
import { createMissLoader, tileCacheFile } from './qa/tileCache.mjs';
|
||||||
|
|
||||||
export const ENGINES = Object.freeze(['cesium', 'navara']);
|
export const ENGINES = Object.freeze(['cesium', 'navara']);
|
||||||
const RESERVED_PORTS = ['', '4173', '4185', '4186', '4187', '4188', '4189'];
|
/** Local ports that belong to the user's own servers: never a QA preview. */
|
||||||
const RESERVED = [...RESERVED_PORTS, '4194', '4196'];
|
export const RESERVED_PORTS = Object.freeze([
|
||||||
|
'',
|
||||||
|
'4173',
|
||||||
|
'4174',
|
||||||
|
'4185',
|
||||||
|
'4186',
|
||||||
|
'4187',
|
||||||
|
'4188',
|
||||||
|
'4189',
|
||||||
|
'4194',
|
||||||
|
'4196',
|
||||||
|
]);
|
||||||
|
/** Chrome flags every scenario's browser starts with (the GPU path). */
|
||||||
|
const CHROME_ARGS = Object.freeze(['--use-angle=metal', '--enable-gpu']);
|
||||||
|
|
||||||
/** The values of --layers=a,b and --evidence=FILE (null when absent). */
|
/** The values of --layers=a,b and --evidence=FILE (null when absent). */
|
||||||
export function strictArguments(argv = process.argv) {
|
export function strictArguments(argv = process.argv) {
|
||||||
@@ -48,7 +61,7 @@ export function localPreview(href) {
|
|||||||
const url = new URL(href);
|
const url = new URL(href);
|
||||||
if (
|
if (
|
||||||
!['localhost', '127.0.0.1'].includes(url.hostname) ||
|
!['localhost', '127.0.0.1'].includes(url.hostname) ||
|
||||||
RESERVED.includes(url.port)
|
RESERVED_PORTS.includes(url.port)
|
||||||
)
|
)
|
||||||
throw Error('Supply an unreserved local keyless preview URL');
|
throw Error('Supply an unreserved local keyless preview URL');
|
||||||
return url;
|
return url;
|
||||||
@@ -89,11 +102,18 @@ export async function installStrictNetwork(
|
|||||||
// workers' own requests are never held.
|
// workers' own requests are never held.
|
||||||
const intercept = async (session, type) => {
|
const intercept = async (session, type) => {
|
||||||
session.on('Fetch.requestPaused', async ({ requestId, request }) => {
|
session.on('Fetch.requestPaused', async ({ requestId, request }) => {
|
||||||
const fulfil = (status, headers, body) =>
|
// Every answer allows any origin; `noStore` keeps it out of the cache.
|
||||||
|
const fulfil = (status, contentType, body, { noStore = false } = {}) =>
|
||||||
session.send('Fetch.fulfillRequest', {
|
session.send('Fetch.fulfillRequest', {
|
||||||
requestId,
|
requestId,
|
||||||
responseCode: status,
|
responseCode: status,
|
||||||
responseHeaders: headers,
|
responseHeaders: [
|
||||||
|
...(contentType
|
||||||
|
? [{ name: 'Content-Type', value: contentType }]
|
||||||
|
: []),
|
||||||
|
...(noStore ? [{ name: 'Cache-Control', value: 'no-store' }] : []),
|
||||||
|
{ name: 'Access-Control-Allow-Origin', value: '*' },
|
||||||
|
],
|
||||||
...(body == null
|
...(body == null
|
||||||
? {}
|
? {}
|
||||||
: { body: Buffer.from(body).toString('base64') }),
|
: { body: Buffer.from(body).toString('base64') }),
|
||||||
@@ -106,12 +126,7 @@ export async function installStrictNetwork(
|
|||||||
const answer = await route.respond(request);
|
const answer = await route.respond(request);
|
||||||
return await fulfil(
|
return await fulfil(
|
||||||
answer.status ?? 200,
|
answer.status ?? 200,
|
||||||
[
|
answer.contentType,
|
||||||
...(answer.contentType
|
|
||||||
? [{ name: 'Content-Type', value: answer.contentType }]
|
|
||||||
: []),
|
|
||||||
{ name: 'Access-Control-Allow-Origin', value: '*' },
|
|
||||||
],
|
|
||||||
answer.body ?? '',
|
answer.body ?? '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -128,14 +143,9 @@ export async function installStrictNetwork(
|
|||||||
(ledger.probes[probe.pattern] ?? 0) + 1;
|
(ledger.probes[probe.pattern] ?? 0) + 1;
|
||||||
return await fulfil(
|
return await fulfil(
|
||||||
probe.status,
|
probe.status,
|
||||||
[
|
probe.contentType,
|
||||||
...(probe.contentType
|
|
||||||
? [{ name: 'Content-Type', value: probe.contentType }]
|
|
||||||
: []),
|
|
||||||
{ name: 'Cache-Control', value: 'no-store' },
|
|
||||||
{ name: 'Access-Control-Allow-Origin', value: '*' },
|
|
||||||
],
|
|
||||||
probe.body ?? null,
|
probe.body ?? null,
|
||||||
|
{ noStore: true },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const accept = Object.entries(request.headers ?? {}).find(
|
const accept = Object.entries(request.headers ?? {}).find(
|
||||||
@@ -172,16 +182,7 @@ export async function installStrictNetwork(
|
|||||||
const host = new URL(request.url).host;
|
const host = new URL(request.url).host;
|
||||||
tiles.fetchedByHost[host] = (tiles.fetchedByHost[host] ?? 0) + 1;
|
tiles.fetchedByHost[host] = (tiles.fetchedByHost[host] ?? 0) + 1;
|
||||||
}
|
}
|
||||||
await fulfil(
|
await fulfil(entry.status, entry.contentType, entry.body);
|
||||||
entry.status,
|
|
||||||
[
|
|
||||||
...(entry.contentType
|
|
||||||
? [{ name: 'Content-Type', value: entry.contentType }]
|
|
||||||
: []),
|
|
||||||
{ name: 'Access-Control-Allow-Origin', value: '*' },
|
|
||||||
],
|
|
||||||
entry.body,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (
|
if (
|
||||||
/Invalid InterceptionId|closed|detached/i.test(String(error?.message))
|
/Invalid InterceptionId|closed|detached/i.test(String(error?.message))
|
||||||
@@ -326,21 +327,15 @@ export async function installStrictNetwork(
|
|||||||
* each page gets an accessor in place of the global: reads before the script
|
* each page gets an accessor in place of the global: reads before the script
|
||||||
* has assigned it are logged with their stack in `ledger.cesiumBeforeLoad`
|
* has assigned it are logged with their stack in `ledger.cesiumBeforeLoad`
|
||||||
* and answer `undefined`, as production would.
|
* and answer `undefined`, as production would.
|
||||||
*
|
|
||||||
* GEV_QA_CESIUM_TRAP=record (a survey, not a check) answers such a read with
|
|
||||||
* the SDK instead, loaded synchronously on the first one, and logs every
|
|
||||||
* distinct read site until the page itself loads Cesium.js, so one run lists
|
|
||||||
* all the Navara paths that still need Cesium.
|
|
||||||
*/
|
*/
|
||||||
async function trapCesiumGlobal(session, ledger) {
|
async function trapCesiumGlobal(session, ledger) {
|
||||||
const binding = '__gevStrictCesiumRead';
|
const binding = '__gevStrictCesiumRead';
|
||||||
const record = process.env.GEV_QA_CESIUM_TRAP === 'record';
|
|
||||||
session.on('Runtime.bindingCalled', (event) => {
|
session.on('Runtime.bindingCalled', (event) => {
|
||||||
if (event.name !== binding) return;
|
if (event.name !== binding) return;
|
||||||
const stack = String(event.payload).slice(0, 1500);
|
const stack = String(event.payload).slice(0, 1500);
|
||||||
const known = ledger.cesiumBeforeLoad.find((read) => read.stack === stack);
|
const known = ledger.cesiumBeforeLoad.find((read) => read.stack === stack);
|
||||||
if (known) known.count++;
|
if (known) known.count++;
|
||||||
else if (ledger.cesiumBeforeLoad.length < (record ? 2000 : 50))
|
else if (ledger.cesiumBeforeLoad.length < 50)
|
||||||
ledger.cesiumBeforeLoad.push({ stack, count: 1 });
|
ledger.cesiumBeforeLoad.push({ stack, count: 1 });
|
||||||
});
|
});
|
||||||
await session.send('Runtime.enable');
|
await session.send('Runtime.enable');
|
||||||
@@ -349,10 +344,8 @@ async function trapCesiumGlobal(session, ledger) {
|
|||||||
await session.send('Page.addScriptToEvaluateOnNewDocument', {
|
await session.send('Page.addScriptToEvaluateOnNewDocument', {
|
||||||
source: `(() => {
|
source: `(() => {
|
||||||
if (window.top !== window) return;
|
if (window.top !== window) return;
|
||||||
const record = ${record};
|
|
||||||
const seen = new Set();
|
const seen = new Set();
|
||||||
let sdk;
|
let sdk;
|
||||||
let pageLoaded = false;
|
|
||||||
const report = () => {
|
const report = () => {
|
||||||
const limit = Error.stackTraceLimit;
|
const limit = Error.stackTraceLimit;
|
||||||
Error.stackTraceLimit = 12;
|
Error.stackTraceLimit = 12;
|
||||||
@@ -366,21 +359,8 @@ async function trapCesiumGlobal(session, ledger) {
|
|||||||
Object.defineProperty(window, 'Cesium', {
|
Object.defineProperty(window, 'Cesium', {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get() {
|
get() {
|
||||||
if (!record) {
|
|
||||||
if (sdk === undefined) report();
|
if (sdk === undefined) report();
|
||||||
return sdk;
|
return sdk;
|
||||||
}
|
|
||||||
if (!pageLoaded) {
|
|
||||||
pageLoaded = !!document.querySelector('script[src$="/Cesium.js"]');
|
|
||||||
if (!pageLoaded) report();
|
|
||||||
}
|
|
||||||
if (sdk === undefined) {
|
|
||||||
const request = new XMLHttpRequest();
|
|
||||||
request.open('GET', '/cesium/Cesium.js', false);
|
|
||||||
request.send();
|
|
||||||
(0, eval)(request.responseText);
|
|
||||||
}
|
|
||||||
return sdk;
|
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
sdk = value;
|
sdk = value;
|
||||||
@@ -434,30 +414,8 @@ export async function pickEngine(
|
|||||||
);
|
);
|
||||||
const opened = !(await expanded());
|
const opened = !(await expanded());
|
||||||
if (opened) {
|
if (opened) {
|
||||||
await page.click('#control-panel-toggle').catch(async (error) => {
|
await page.click('#control-panel-toggle').catch((error) => {
|
||||||
const state = await page.evaluate(() => {
|
throw Error(`cannot click the control panel toggle: ${error.message}`);
|
||||||
const el = document.querySelector('#control-panel-toggle');
|
|
||||||
const box = el?.getBoundingClientRect();
|
|
||||||
const style = el && getComputedStyle(el);
|
|
||||||
const hit =
|
|
||||||
box &&
|
|
||||||
document.elementFromPoint(
|
|
||||||
box.x + box.width / 2,
|
|
||||||
box.y + box.height / 2,
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
box: box && [box.x, box.y, box.width, box.height],
|
|
||||||
display: style?.display,
|
|
||||||
visibility: style?.visibility,
|
|
||||||
hidden: el?.closest('[hidden]')?.id ?? null,
|
|
||||||
hit: hit?.id || hit?.className || null,
|
|
||||||
body: document.body.className,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
throw Error(
|
|
||||||
`cannot click the control panel toggle (${error.message}): ` +
|
|
||||||
JSON.stringify(state),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
await page.waitForFunction(
|
await page.waitForFunction(
|
||||||
() =>
|
() =>
|
||||||
@@ -592,6 +550,27 @@ export async function waitForEngine(page, kind, { timeout = 120000 } = {}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A new 1024×768 page of `browser` whose uncaught errors (and, with
|
||||||
|
* `shaderErrors`, shader and WebGL program errors on the console) land in
|
||||||
|
* `errors`; each uncaught error is also logged without its query string.
|
||||||
|
*/
|
||||||
|
export async function openPage(browser, { shaderErrors = false } = {}) {
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.setViewport({ width: 1024, height: 768 });
|
||||||
|
const errors = [];
|
||||||
|
page.on('pageerror', (e) => {
|
||||||
|
errors.push(e.message);
|
||||||
|
console.log('error', e.message.split('?')[0]);
|
||||||
|
});
|
||||||
|
if (shaderErrors)
|
||||||
|
page.on('console', (msg) => {
|
||||||
|
if (msg.type() === 'error' && /Shader|WebGLProgram/i.test(msg.text()))
|
||||||
|
errors.push(msg.text());
|
||||||
|
});
|
||||||
|
return { page, errors };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The strict context of one scenario run.
|
* The strict context of one scenario run.
|
||||||
*
|
*
|
||||||
@@ -653,9 +632,16 @@ export function createStrict({
|
|||||||
ledger,
|
ledger,
|
||||||
extra,
|
extra,
|
||||||
timings,
|
timings,
|
||||||
/** Launch Chrome with the policy on every target. */
|
/**
|
||||||
async launch(options = {}) {
|
* Launch headless Chrome with the policy on every target; `args` adds
|
||||||
const browser = await puppeteer.launch({ headless: true, ...options });
|
* flags to CHROME_ARGS.
|
||||||
|
*/
|
||||||
|
async launch({ args = [], ...options } = {}) {
|
||||||
|
const browser = await puppeteer.launch({
|
||||||
|
headless: true,
|
||||||
|
...options,
|
||||||
|
args: [...CHROME_ARGS, ...args],
|
||||||
|
});
|
||||||
await installStrictNetwork(browser, {
|
await installStrictNetwork(browser, {
|
||||||
origin,
|
origin,
|
||||||
fixtures,
|
fixtures,
|
||||||
@@ -665,7 +651,7 @@ export function createStrict({
|
|||||||
});
|
});
|
||||||
return browser;
|
return browser;
|
||||||
},
|
},
|
||||||
/** Launch, run `body(browser)`, close, then finish(). */
|
/** Launch with `options`, run `body(browser)`, close, then finish(). */
|
||||||
async run(options, body) {
|
async run(options, body) {
|
||||||
const browser = await strict.launch(options);
|
const browser = await strict.launch(options);
|
||||||
let failed = null;
|
let failed = null;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
const strict = createStrict({ origin: base.origin, layers: [] });
|
const strict = createStrict({ origin: base.origin, layers: [] });
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const p = await b.newPage();
|
||||||
await p.setViewport({ width: 1280, height: 800 });
|
await p.setViewport({ width: 1280, height: 800 });
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const base = localPreview(process.argv[2]);
|
|||||||
const strict = createStrict({ origin: base.origin, layers: [] });
|
const strict = createStrict({ origin: base.origin, layers: [] });
|
||||||
const results = [];
|
const results = [];
|
||||||
strict.extra.contracts = results;
|
strict.extra.contracts = results;
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
for (const start of ENGINES) {
|
for (const start of ENGINES) {
|
||||||
const page = await b.newPage();
|
const page = await b.newPage();
|
||||||
await page.setViewport({ width: 1280, height: 800 });
|
await page.setViewport({ width: 1280, height: 800 });
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import {
|
|||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
const strict = createStrict({ origin: base.origin, layers: [] });
|
const strict = createStrict({ origin: base.origin, layers: [] });
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const p = await b.newPage();
|
||||||
await p.setViewport({ width: 1280, height: 800 });
|
await p.setViewport({ width: 1280, height: 800 });
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|||||||
@@ -156,9 +156,7 @@ strict.extra.soak = report;
|
|||||||
|
|
||||||
for (let run = 1; run <= runs; run++) {
|
for (let run = 1; run <= runs; run++) {
|
||||||
const start = run % 2 ? 'navara' : 'cesium';
|
const start = run % 2 ? 'navara' : 'cesium';
|
||||||
const browser = await strict.launch({
|
const browser = await strict.launch();
|
||||||
args: ['--use-angle=metal', '--enable-gpu'],
|
|
||||||
});
|
|
||||||
const row = { run, start, swaps: [], checkpoints: [], errors: [] };
|
const row = { run, start, swaps: [], checkpoints: [], errors: [] };
|
||||||
report.runs.push(row);
|
report.runs.push(row);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { fromDegrees } from '../src/geo/cartesian3.js';
|
import { fromDegrees } from '../src/geo/cartesian3.js';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
base.search = '?engine=navara&welcome=0&look=cinematic';
|
base.search = '?engine=navara&welcome=0&look=cinematic';
|
||||||
@@ -27,39 +32,15 @@ const strict = createStrict({
|
|||||||
origin: base.origin,
|
origin: base.origin,
|
||||||
layers: ['telegeography-submarine-cables'],
|
layers: ['telegeography-submarine-cables'],
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b, { shaderErrors: true });
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
const reads = [];
|
const reads = [];
|
||||||
p.on('request', (req) => {
|
p.on('request', (req) => {
|
||||||
const path = new URL(req.url()).pathname;
|
const path = new URL(req.url()).pathname;
|
||||||
if (/\/(cable-geo|landing-point-geo).*\.json$/.test(path)) reads.push(path);
|
if (/\/(cable-geo|landing-point-geo).*\.json$/.test(path)) reads.push(path);
|
||||||
});
|
});
|
||||||
p.on('console', (msg) => {
|
|
||||||
if (msg.type() === 'error' && /Shader|WebGLProgram/i.test(msg.text()))
|
|
||||||
errors.push(msg.text());
|
|
||||||
});
|
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(p, 'navara');
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
await p.evaluate(async () => {
|
await p.evaluate(async () => {
|
||||||
const { dataManager, tileset } = window.__godsEyeView;
|
const { dataManager, tileset } = window.__godsEyeView;
|
||||||
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
||||||
@@ -71,8 +52,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
});
|
});
|
||||||
for (const kind of ['navara', 'cesium', 'navara']) {
|
for (const kind of ['navara', 'cesium', 'navara']) {
|
||||||
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
}
|
}
|
||||||
await p.waitForFunction(
|
await p.waitForFunction(
|
||||||
() => window.qaCables.getStats().referenceLabelCount > 0,
|
() => window.qaCables.getStats().referenceLabelCount > 0,
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { trafficFixtureResponse } from './traffic-fixtures.mjs';
|
import { trafficFixtureResponse } from './traffic-fixtures.mjs';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
import { STRICT_TILE_HOSTS } from './qa-map-policy.mjs';
|
import { STRICT_TILE_HOSTS } from './qa-map-policy.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
@@ -22,14 +27,8 @@ const strict = createStrict({
|
|||||||
fixtures: ['https://tiles.maps.eox.at/*', 'https://tiles.openfreemap.org/*'],
|
fixtures: ['https://tiles.maps.eox.at/*', 'https://tiles.openfreemap.org/*'],
|
||||||
tileHosts: STRICT_TILE_HOSTS.filter((host) => host !== 'tiles.maps.eox.at'),
|
tileHosts: STRICT_TILE_HOSTS.filter((host) => host !== 'tiles.maps.eox.at'),
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b, { shaderErrors: true });
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
const keyless = process.argv.includes('--simulation');
|
const keyless = process.argv.includes('--simulation');
|
||||||
const reads = new Map();
|
const reads = new Map();
|
||||||
@@ -73,26 +72,8 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
}
|
}
|
||||||
return req.continue();
|
return req.continue();
|
||||||
});
|
});
|
||||||
p.on('console', (msg) => {
|
|
||||||
if (msg.type() === 'error' && /Shader|WebGLProgram/i.test(msg.text()))
|
|
||||||
errors.push(msg.text());
|
|
||||||
});
|
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(p, 'navara');
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
await p.evaluate(async () => {
|
await p.evaluate(async () => {
|
||||||
const { tileset, dataManager } = window.__godsEyeView;
|
const { tileset, dataManager } = window.__godsEyeView;
|
||||||
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
||||||
@@ -199,8 +180,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
);
|
);
|
||||||
for (const kind of ['navara', 'cesium', 'navara']) {
|
for (const kind of ['navara', 'cesium', 'navara']) {
|
||||||
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
}
|
}
|
||||||
await waitTraffic();
|
await waitTraffic();
|
||||||
const check = await p.evaluate(() => {
|
const check = await p.evaluate(() => {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const strict = createStrict({
|
|||||||
'*://*.earthdata.nasa.gov/*',
|
'*://*.earthdata.nasa.gov/*',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const p = await b.newPage();
|
||||||
await p.setViewport({ width: 1280, height: 900 });
|
await p.setViewport({ width: 1280, height: 900 });
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const getOpt = (name, dflt) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const APP_URL = getOpt('--url', 'http://localhost:4173');
|
const APP_URL = getOpt('--url', 'http://localhost:4173');
|
||||||
// --engine cesium|navara (phase 3): frame, settle and read the renderer
|
// --engine cesium|navara: frame, settle and read the renderer
|
||||||
// through window.__godsEyeView.engine (either engine) instead of the Cesium
|
// through window.__godsEyeView.engine (either engine) instead of the Cesium
|
||||||
// viewer; the URL gets ?engine=. Without it, as before.
|
// viewer; the URL gets ?engine=. Without it, as before.
|
||||||
const ENGINE = getOpt('--engine', null);
|
const ENGINE = getOpt('--engine', null);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
* node scripts/qa-vessel-datum.mjs --ports rotterdam --url http://localhost:4173
|
* node scripts/qa-vessel-datum.mjs --ports rotterdam --url http://localhost:4173
|
||||||
* node scripts/qa-vessel-datum.mjs --engine navara --url 'http://localhost:PORT/?qaFeed=vessels:fixture'
|
* node scripts/qa-vessel-datum.mjs --engine navara --url 'http://localhost:PORT/?qaFeed=vessels:fixture'
|
||||||
*
|
*
|
||||||
* --engine cesium|navara (phase 3): the engine-neutral path on either engine,
|
* --engine cesium|navara: the engine-neutral path on either engine,
|
||||||
* through window.__godsEyeView.engine and the QA probe (layers.drawn: each
|
* through window.__godsEyeView.engine and the QA probe (layers.drawn: each
|
||||||
* chevron's drawn position and depth-test flag) instead of the Cesium
|
* chevron's drawn position and depth-test flag) instead of the Cesium
|
||||||
* viewer's primitives; the URL gets ?engine=. Without it, as before.
|
* viewer's primitives; the URL gets ?engine=. Without it, as before.
|
||||||
|
|||||||
+23
-18
@@ -1,19 +1,28 @@
|
|||||||
|
// Recent imagery on a keyless preview, starting on Navara: the NASA catalog
|
||||||
|
// and imagery tiles are fixtures, and the image, A/B, swap, divider, opacity,
|
||||||
|
// basemap, toggle, failure, box-drag and zoom-to-fit checks read the rendered
|
||||||
|
// pixels; both pinned dates survive a switch to Cesium and back. The report
|
||||||
|
// and its screenshots go next to the evidence file (or to the temp directory).
|
||||||
|
//
|
||||||
|
// node scripts/qa-w2-imagery.mjs http://127.0.0.1:<PORT>
|
||||||
|
// [--layers=recent-imagery] [--evidence=FILE]
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
|
import os from 'node:os';
|
||||||
|
import path from 'node:path';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
strictArguments,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
import { isExpectedPreviewSetupError, redact } from './qa-browserEvidence.mjs';
|
import { isExpectedPreviewSetupError, redact } from './qa-browserEvidence.mjs';
|
||||||
|
|
||||||
const args = process.argv.slice(2);
|
const { origin } = localPreview(process.argv[2]);
|
||||||
const option = (name, fallback) =>
|
const url = origin + '/?engine=navara';
|
||||||
args.includes(name) ? args[args.indexOf(name) + 1] : fallback;
|
const { evidence } = strictArguments();
|
||||||
const url = option('--url', 'http://127.0.0.1:4193');
|
const output = evidence
|
||||||
const output = option('--report', '/private/tmp/gev-imagery.json');
|
? path.join(path.dirname(evidence), 'imagery-report.json')
|
||||||
if (
|
: path.join(os.tmpdir(), 'gev-imagery.json');
|
||||||
['4173', '4185', '4186', '4187', '4188', '4189', '4194'].includes(
|
|
||||||
new URL(url).port,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('Reserved QA port');
|
|
||||||
const report = {
|
const report = {
|
||||||
url,
|
url,
|
||||||
checks: [],
|
checks: [],
|
||||||
@@ -26,7 +35,7 @@ let failLandsat = false,
|
|||||||
// The engine the page runs, for the strict assertion manifest.
|
// The engine the page runs, for the strict assertion manifest.
|
||||||
let engine = null;
|
let engine = null;
|
||||||
const strict = createStrict({
|
const strict = createStrict({
|
||||||
origin: localPreview(url).origin,
|
origin,
|
||||||
layers: ['recent-imagery'],
|
layers: ['recent-imagery'],
|
||||||
// The scenario answers the NASA catalog and imagery tiles itself.
|
// The scenario answers the NASA catalog and imagery tiles itself.
|
||||||
fixtures: ['*://*.earthdata.nasa.gov/*'],
|
fixtures: ['*://*.earthdata.nasa.gov/*'],
|
||||||
@@ -48,8 +57,6 @@ const browser = await strict.launch({
|
|||||||
headless: true,
|
headless: true,
|
||||||
args: [
|
args: [
|
||||||
'--no-sandbox',
|
'--no-sandbox',
|
||||||
'--use-angle=metal',
|
|
||||||
'--enable-gpu',
|
|
||||||
'--disable-background-timer-throttling',
|
'--disable-background-timer-throttling',
|
||||||
'--disable-renderer-backgrounding',
|
'--disable-renderer-backgrounding',
|
||||||
],
|
],
|
||||||
@@ -63,7 +70,7 @@ try {
|
|||||||
const text = redact(message.text());
|
const text = redact(message.text());
|
||||||
const expected =
|
const expected =
|
||||||
isExpectedPreviewSetupError(message, {
|
isExpectedPreviewSetupError(message, {
|
||||||
preview: args.includes('--preview'),
|
preview: true,
|
||||||
baseUrl: url,
|
baseUrl: url,
|
||||||
}) ||
|
}) ||
|
||||||
(failedTiles > 0 &&
|
(failedTiles > 0 &&
|
||||||
@@ -231,7 +238,6 @@ try {
|
|||||||
await sleep(2500);
|
await sleep(2500);
|
||||||
image = await capture('ab');
|
image = await capture('ab');
|
||||||
check('A/B dates on opposite sides', red(image.left) && green(image.right));
|
check('A/B dates on opposite sides', red(image.left) && green(image.right));
|
||||||
if (args.includes('--switch')) {
|
|
||||||
const first = await page.evaluate(() => {
|
const first = await page.evaluate(() => {
|
||||||
window.qaImageryOwner = window.__gevRecentImagery.layer;
|
window.qaImageryOwner = window.__gevRecentImagery.layer;
|
||||||
return window.qaImageryOwner.diagnostics().pins;
|
return window.qaImageryOwner.diagnostics().pins;
|
||||||
@@ -262,7 +268,6 @@ try {
|
|||||||
);
|
);
|
||||||
check(kind + ' reuses the STAC search', reads() === expectedReads);
|
check(kind + ' reuses the STAC search', reads() === expectedReads);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
await page.evaluate(() => window.__gevRecentImagery.layer.swapSides());
|
await page.evaluate(() => window.__gevRecentImagery.layer.swapSides());
|
||||||
image = await capture('swap');
|
image = await capture('swap');
|
||||||
check('swap reverses dates', green(image.left) && red(image.right));
|
check('swap reverses dates', green(image.left) && red(image.right));
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { fromDegrees } from '../src/geo/cartesian3.js';
|
import { fromDegrees } from '../src/geo/cartesian3.js';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
base.search = '?engine=navara&welcome=0&look=cinematic';
|
base.search = '?engine=navara&welcome=0&look=cinematic';
|
||||||
@@ -16,14 +21,8 @@ const strict = createStrict({
|
|||||||
'weather-cyclones',
|
'weather-cyclones',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b);
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
const times = ['2026-11-02T12:00:00.000Z', '2026-11-02T12:05:00.000Z'];
|
const times = ['2026-11-02T12:00:00.000Z', '2026-11-02T12:05:00.000Z'];
|
||||||
const ids = ['weather-radar', 'weather-satellite', 'weather-lightning'];
|
const ids = ['weather-radar', 'weather-satellite', 'weather-lightning'];
|
||||||
const images = new Map(),
|
const images = new Map(),
|
||||||
@@ -168,21 +167,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
console.log('GPU', msg.text().slice(0, 3000));
|
console.log('GPU', msg.text().slice(0, 3000));
|
||||||
});
|
});
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(p, 'navara');
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
const waitTime = (ids, time) =>
|
const waitTime = (ids, time) =>
|
||||||
p.waitForFunction(
|
p.waitForFunction(
|
||||||
(ids, time) =>
|
(ids, time) =>
|
||||||
@@ -251,8 +236,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
const polls = [...reads.entries()];
|
const polls = [...reads.entries()];
|
||||||
for (const kind of ['navara', 'cesium', 'navara']) {
|
for (const kind of ['navara', 'cesium', 'navara']) {
|
||||||
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
}
|
}
|
||||||
await waitTime(ids, times[0]);
|
await waitTime(ids, times[0]);
|
||||||
const state = await p.evaluate(
|
const state = await p.evaluate(
|
||||||
@@ -364,7 +348,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
for (const kind of ['navara', 'cesium', 'navara']) {
|
for (const kind of ['navara', 'cesium', 'navara']) {
|
||||||
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
||||||
await p.select('#globe-engine', kind);
|
await p.select('#globe-engine', kind);
|
||||||
await ready(kind);
|
await waitForEngine(p, kind);
|
||||||
}
|
}
|
||||||
await p.waitForFunction(
|
await p.waitForFunction(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createStrict, localPreview } from './qa-strict-harness.mjs';
|
import {
|
||||||
|
createStrict,
|
||||||
|
localPreview,
|
||||||
|
openPage,
|
||||||
|
waitForEngine,
|
||||||
|
} from './qa-strict-harness.mjs';
|
||||||
|
|
||||||
const base = localPreview(process.argv[2]);
|
const base = localPreview(process.argv[2]);
|
||||||
base.search = '?engine=navara&welcome=0&look=cinematic';
|
base.search = '?engine=navara&welcome=0&look=cinematic';
|
||||||
@@ -9,14 +14,8 @@ base.hash =
|
|||||||
// The fixture field is uniform, so the GPU seeds a fixed particle count.
|
// The fixture field is uniform, so the GPU seeds a fixed particle count.
|
||||||
const WIND_PATHS = 7200;
|
const WIND_PATHS = 7200;
|
||||||
const strict = createStrict({ origin: base.origin, layers: ['wind'] });
|
const strict = createStrict({ origin: base.origin, layers: ['wind'] });
|
||||||
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
await strict.run({}, async (b) => {
|
||||||
const p = await b.newPage();
|
const { page: p, errors } = await openPage(b);
|
||||||
await p.setViewport({ width: 1024, height: 768 });
|
|
||||||
const errors = [];
|
|
||||||
p.on('pageerror', (e) => {
|
|
||||||
errors.push(e.message);
|
|
||||||
console.log('error', e.message.split('?')[0]);
|
|
||||||
});
|
|
||||||
const reads = [];
|
const reads = [];
|
||||||
await p.setRequestInterception(true);
|
await p.setRequestInterception(true);
|
||||||
p.on('request', (req) => {
|
p.on('request', (req) => {
|
||||||
@@ -70,21 +69,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
await p.goto(base.href);
|
await p.goto(base.href);
|
||||||
const ready = async (kind) => {
|
await waitForEngine(p, 'navara');
|
||||||
await p.waitForFunction(
|
|
||||||
(k) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === k &&
|
|
||||||
!document.querySelector('#globe-engine')?.disabled &&
|
|
||||||
!document.querySelector('.engine-switch-frame') &&
|
|
||||||
document.querySelector('#loading-screen.hidden'),
|
|
||||||
{ timeout: 90000 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await p.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
await ready('navara');
|
|
||||||
await p.evaluate(async () => {
|
await p.evaluate(async () => {
|
||||||
const { dataManager, tileset } = window.__godsEyeView;
|
const { dataManager, tileset } = window.__godsEyeView;
|
||||||
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
if (tileset || window.__GOOGLE_MAPS_API_KEY__)
|
||||||
@@ -103,8 +88,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
|||||||
const baseline = reads.length;
|
const baseline = reads.length;
|
||||||
for (const kind of ['navara', 'cesium', 'navara']) {
|
for (const kind of ['navara', 'cesium', 'navara']) {
|
||||||
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
if ((await p.evaluate(() => window.__godsEyeView.engine.kind)) !== kind) {
|
||||||
await strict.pick(p, kind);
|
await strict.switchTo(p, kind);
|
||||||
await ready(kind);
|
|
||||||
}
|
}
|
||||||
await p.waitForFunction(
|
await p.waitForFunction(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
@@ -5,19 +5,7 @@
|
|||||||
// the session ids, listener counts and transport counters.
|
// the session ids, listener counts and transport counters.
|
||||||
import puppeteer from 'puppeteer';
|
import puppeteer from 'puppeteer';
|
||||||
import { blockPaidQaMaps } from '../qa-map-policy.mjs';
|
import { blockPaidQaMaps } from '../qa-map-policy.mjs';
|
||||||
|
import { localPreview, waitForEngine } from '../qa-strict-harness.mjs';
|
||||||
const RESERVED_PORTS = new Set([
|
|
||||||
'',
|
|
||||||
'4173',
|
|
||||||
'4174',
|
|
||||||
'4185',
|
|
||||||
'4186',
|
|
||||||
'4187',
|
|
||||||
'4188',
|
|
||||||
'4189',
|
|
||||||
'4194',
|
|
||||||
'4196',
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** Paid or live model endpoints no switch gate may reach. */
|
/** Paid or live model endpoints no switch gate may reach. */
|
||||||
export const BLOCKED_URLS = Object.freeze([
|
export const BLOCKED_URLS = Object.freeze([
|
||||||
@@ -36,13 +24,7 @@ export const BLOCKED_URLS = Object.freeze([
|
|||||||
|
|
||||||
/** The preview origin from argv, refusing the ports that belong to the user. */
|
/** The preview origin from argv, refusing the ports that belong to the user. */
|
||||||
export function previewBase(argument) {
|
export function previewBase(argument) {
|
||||||
const base = new URL(argument ?? '');
|
return localPreview(argument ?? '');
|
||||||
if (
|
|
||||||
!['localhost', '127.0.0.1'].includes(base.hostname) ||
|
|
||||||
RESERVED_PORTS.has(base.port)
|
|
||||||
)
|
|
||||||
throw new Error('Supply an unreserved local preview URL');
|
|
||||||
return base;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The page address: Boston on OSM, no welcome, on the given engine. */
|
/** The page address: Boston on OSM, no welcome, on the given engine. */
|
||||||
@@ -247,20 +229,8 @@ export async function openSessionPage(browser, address) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** The engine runs, its picker is idle and the loading screen is gone. */
|
/** The engine runs, its picker is idle and the loading screen is gone. */
|
||||||
export async function waitReady(page, kind) {
|
export function waitReady(page, kind) {
|
||||||
await page.waitForFunction(
|
return waitForEngine(page, kind, { timeout: 90_000 });
|
||||||
(kind) =>
|
|
||||||
window.__godsEyeView?.engine?.kind === kind &&
|
|
||||||
document.querySelector('#globe-engine')?.value === kind &&
|
|
||||||
!document.querySelector('#globe-engine').disabled &&
|
|
||||||
document.getElementById('loading-screen')?.classList.contains('hidden') &&
|
|
||||||
!document.querySelector('.engine-switch-frame'),
|
|
||||||
{ timeout: 90_000, polling: 100 },
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
await page.evaluate(
|
|
||||||
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
// Map tiles for headless QA: a disk cache shared by every run (in the system
|
// Map tiles for headless QA: the disk cache entry of a tile request and one
|
||||||
// temp directory) and one fetch per missing tile, with backoff on HTTP 429,
|
// fetch per missing tile, with backoff on HTTP 429, for the free tile hosts
|
||||||
// for the free tile hosts the QA pages load (OpenStreetMap imagery, the
|
// the QA pages load (OpenStreetMap imagery, the keyless re:earth terrain).
|
||||||
// keyless re:earth terrain). Back-to-back QA runs otherwise reload the same
|
// Back-to-back QA runs otherwise reload the same tiles until those hosts
|
||||||
// tiles until those hosts rate-limit them. Only requests to these hosts are
|
// rate-limit them. The strict harness (scripts/qa-strict-harness.mjs) pauses
|
||||||
// paused (CDP Fetch with URL patterns); page-wide request interception also
|
// only the requests to these hosts (CDP Fetch with URL patterns) and answers
|
||||||
// held the Navara workers' requests and kept workers alive past their globe.
|
// them from the cache in the system temp directory.
|
||||||
|
|
||||||
import { createHash } from 'node:crypto';
|
import { createHash } from 'node:crypto';
|
||||||
import fs from 'node:fs';
|
|
||||||
import os from 'node:os';
|
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
export const CACHED_TILE_HOSTS = Object.freeze([
|
export const CACHED_TILE_HOSTS = Object.freeze([
|
||||||
@@ -119,82 +117,3 @@ export function createMissLoader({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Serves the page's requests to CACHED_TILE_HOSTS (or `hosts`) from the
|
|
||||||
* cache (filling it on a miss). Returns the counts.
|
|
||||||
*/
|
|
||||||
export async function installTileCache(
|
|
||||||
page,
|
|
||||||
{
|
|
||||||
dir = path.join(os.tmpdir(), 'gev-qa-tiles'),
|
|
||||||
hosts = CACHED_TILE_HOSTS,
|
|
||||||
} = {},
|
|
||||||
) {
|
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
|
||||||
const stats = { hits: 0, fetched: 0, cancelled: 0, failed: 0, errors: {} };
|
|
||||||
const client = await page.createCDPSession();
|
|
||||||
const misses = createMissLoader();
|
|
||||||
client.on('Fetch.requestPaused', async ({ requestId, request }) => {
|
|
||||||
const accept = Object.entries(request.headers ?? {}).find(
|
|
||||||
([name]) => name.toLowerCase() === 'accept',
|
|
||||||
)?.[1];
|
|
||||||
const file =
|
|
||||||
request.method === 'GET'
|
|
||||||
? tileCacheFile(request.url, accept, dir, hosts)
|
|
||||||
: null;
|
|
||||||
try {
|
|
||||||
if (!file)
|
|
||||||
return await client.send('Fetch.continueRequest', { requestId });
|
|
||||||
let entry = null;
|
|
||||||
if (fs.existsSync(`${file}.json`)) {
|
|
||||||
entry = {
|
|
||||||
...JSON.parse(fs.readFileSync(`${file}.json`, 'utf8')),
|
|
||||||
body: fs.readFileSync(file),
|
|
||||||
};
|
|
||||||
stats.hits++;
|
|
||||||
} else {
|
|
||||||
const tile = await misses.load(request.url, accept);
|
|
||||||
// Only successes are cached; a 404 (no tile there) is passed on.
|
|
||||||
if (tile.status === 200) {
|
|
||||||
fs.writeFileSync(file, tile.body);
|
|
||||||
fs.writeFileSync(
|
|
||||||
`${file}.json`,
|
|
||||||
JSON.stringify({ status: 200, contentType: tile.contentType }),
|
|
||||||
);
|
|
||||||
stats.fetched++;
|
|
||||||
} else if (tile.status === 429) stats.failed++;
|
|
||||||
entry = tile;
|
|
||||||
}
|
|
||||||
await client.send('Fetch.fulfillRequest', {
|
|
||||||
requestId,
|
|
||||||
responseCode: entry.status,
|
|
||||||
responseHeaders: [
|
|
||||||
...(entry.contentType
|
|
||||||
? [{ name: 'Content-Type', value: entry.contentType }]
|
|
||||||
: []),
|
|
||||||
{ name: 'Access-Control-Allow-Origin', value: '*' },
|
|
||||||
],
|
|
||||||
body: entry.body.toString('base64'),
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
// The page dropped the request meanwhile (a tile no longer in view).
|
|
||||||
if (/Invalid InterceptionId/.test(String(error?.message))) {
|
|
||||||
stats.cancelled++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
stats.failed++;
|
|
||||||
const reason = String(error?.message ?? error).slice(0, 80);
|
|
||||||
stats.errors[reason] = (stats.errors[reason] ?? 0) + 1;
|
|
||||||
await client
|
|
||||||
.send('Fetch.failRequest', { requestId, errorReason: 'Failed' })
|
|
||||||
.catch(() => {});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await client.send('Fetch.enable', {
|
|
||||||
patterns: hosts.map((host) => ({
|
|
||||||
urlPattern: `https://${host}/*`,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
return stats;
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,7 @@ const LINE_ID = 'gev-draw-preview';
|
|||||||
const DOTS_ID = 'gev-draw-vertices';
|
const DOTS_ID = 'gev-draw-vertices';
|
||||||
|
|
||||||
/** '#rrggbb' → rgba in 0..1. */
|
/** '#rrggbb' → rgba in 0..1. */
|
||||||
export function cssRgba(css, alpha = 1) {
|
function cssRgba(css, alpha = 1) {
|
||||||
const hex = Number.parseInt(String(css).slice(1), 16);
|
const hex = Number.parseInt(String(css).slice(1), 16);
|
||||||
return [
|
return [
|
||||||
((hex >> 16) & 255) / 255,
|
((hex >> 16) & 255) / 255,
|
||||||
|
|||||||
@@ -68,14 +68,3 @@ export function catalogControlServices(catalog) {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Layer roles for controls on a page without data layers: one empty object
|
|
||||||
* per role, so the shell's optional calls
|
|
||||||
* (`layer.method?.()`) do nothing.
|
|
||||||
*/
|
|
||||||
export function absentLayerControlServices() {
|
|
||||||
return Object.fromEntries(
|
|
||||||
Object.keys(CONTROL_LAYER_IDS).map((role) => [role, {}]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import {
|
import { createLayerCatalog, catalogControlServices } from './catalog.js';
|
||||||
absentLayerControlServices,
|
|
||||||
createLayerCatalog,
|
|
||||||
catalogControlServices,
|
|
||||||
} from './catalog.js';
|
|
||||||
import { createApplicationData } from './data.js';
|
import { createApplicationData } from './data.js';
|
||||||
|
|
||||||
const metadata = (layers) =>
|
const metadata = (layers) =>
|
||||||
@@ -158,40 +154,6 @@ test('controls bind catalog instances rather than similarly named defaults', ()
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('without data layers the controls get one empty stand-in per layer role', () => {
|
|
||||||
const ids = [
|
|
||||||
'traffic',
|
|
||||||
'flights',
|
|
||||||
'military',
|
|
||||||
'satellites',
|
|
||||||
'cctv',
|
|
||||||
'street-level',
|
|
||||||
'radio',
|
|
||||||
'bikeshare',
|
|
||||||
'transit',
|
|
||||||
'ais-live-vessels',
|
|
||||||
'military-awareness',
|
|
||||||
'military-installations',
|
|
||||||
'rocket-launches',
|
|
||||||
'local-adsb',
|
|
||||||
];
|
|
||||||
const layers = ids.map((id) => ({ id }));
|
|
||||||
const roles = Object.keys(
|
|
||||||
catalogControlServices(createLayerCatalog(layers, metadata(layers))),
|
|
||||||
);
|
|
||||||
const absent = absentLayerControlServices();
|
|
||||||
assert.deepEqual(Object.keys(absent), roles);
|
|
||||||
for (const role of roles) {
|
|
||||||
assert.deepEqual(Object.keys(absent[role]), [], role);
|
|
||||||
assert.equal(absent[role].subscribe, undefined);
|
|
||||||
}
|
|
||||||
assert.notEqual(absent.flightsLayer, absent.trafficLayer);
|
|
||||||
assert.notEqual(
|
|
||||||
absentLayerControlServices().flightsLayer,
|
|
||||||
absent.flightsLayer,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('data setup without a viewer registers an empty catalog and no sonar scene', async (t) => {
|
test('data setup without a viewer registers an empty catalog and no sonar scene', async (t) => {
|
||||||
const previous = globalThis.document;
|
const previous = globalThis.document;
|
||||||
globalThis.document = { getElementById: () => null };
|
globalThis.document = { getElementById: () => null };
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ import { createAirQualityCesiumRenderer } from '../../layers/airQuality/render/c
|
|||||||
import { createFirmsCesiumRenderer } from '../../layers/firms/render/cesium.js';
|
import { createFirmsCesiumRenderer } from '../../layers/firms/render/cesium.js';
|
||||||
import { createEarthquakesCesiumRenderer } from '../../layers/earthquakes/render/cesium.js';
|
import { createEarthquakesCesiumRenderer } from '../../layers/earthquakes/render/cesium.js';
|
||||||
import { createLaunchesCesiumRenderer } from '../../layers/launches/render/cesium.js';
|
import { createLaunchesCesiumRenderer } from '../../layers/launches/render/cesium.js';
|
||||||
// Which engine draws which layer: the layer renderer registry (plan, "layer
|
// Which engine draws which layer: the layer renderer registry and the layer
|
||||||
// renderer interface") and the layer availability API built on it.
|
// availability API built on it.
|
||||||
//
|
//
|
||||||
// A renderer factory `(engine, options) => renderer` draws one layer on one
|
// A renderer factory `(engine, options) => renderer` draws one layer on one
|
||||||
// engine kind (`render/cesium.js`, `render/navara.js` of a layer). Cesium
|
// engine kind (`render/cesium.js`, `render/navara.js` of a layer). Cesium
|
||||||
// layers still draw themselves until their render/cesium.js is extracted
|
// layers without a render/cesium.js draw themselves: they are registered as
|
||||||
// (the *a tasks): they are registered as CESIUM_BUILTIN. Cesium entries are
|
// CESIUM_BUILTIN. Cesium entries are registered here, statically (main
|
||||||
// registered here, statically (main chunk); Navara factories are registered
|
// chunk); Navara factories are registered by the Navara mode after its
|
||||||
// by the Navara mode after its import() (src/app/navaraMode.js).
|
// import() (src/app/navaraMode.js).
|
||||||
//
|
//
|
||||||
// ## Layer availability (stable API, used by phase 4's layer list)
|
// ## Layer availability (stable API, used by the layer list)
|
||||||
//
|
//
|
||||||
// import { createLayerAvailability } from './app/layers/renderers.js';
|
// import { createLayerAvailability } from './app/layers/renderers.js';
|
||||||
// const availability = createLayerAvailability(engine.kind);
|
// const availability = createLayerAvailability(engine.kind);
|
||||||
@@ -28,8 +28,8 @@ import { createLaunchesCesiumRenderer } from '../../layers/launches/render/cesiu
|
|||||||
// The object is frozen; available() and unavailable() return fresh arrays.
|
// The object is frozen; available() and unavailable() return fresh arrays.
|
||||||
// It reads the registry when called, so a Navara factory registered later
|
// It reads the registry when called, so a Navara factory registered later
|
||||||
// (after the Navara mode loads) shows up. `name` is the layer's display name
|
// (after the Navara mode loads) shows up. `name` is the layer's display name
|
||||||
// (src/app/layers/directory.js). Phase 4 injects it as its `availability`
|
// (src/app/layers/directory.js). The layer list takes it as its
|
||||||
// provider (`{ engine, unavailable() }` is the part it reads).
|
// `availability` provider (`{ engine, unavailable() }` is the part it reads).
|
||||||
|
|
||||||
import { APPLICATION_LAYER_DIRECTORY } from './directory.js';
|
import { APPLICATION_LAYER_DIRECTORY } from './directory.js';
|
||||||
import { createFlightsCesiumRenderer } from '../../layers/flights/render/cesium.js';
|
import { createFlightsCesiumRenderer } from '../../layers/flights/render/cesium.js';
|
||||||
|
|||||||
+2
-2
@@ -14,7 +14,7 @@
|
|||||||
// drawn(id) what the layer draws per object (glyph, model),
|
// drawn(id) what the layer draws per object (glyph, model),
|
||||||
// as plain data, or null (layers with qaDrawn)
|
// as plain data, or null (layers with qaDrawn)
|
||||||
// following() { label, native } of engine.camera.following()
|
// following() { label, native } of engine.camera.following()
|
||||||
// movers() engine.movers.list()
|
// movers() engine.movers.list() ([] without movers: Cesium)
|
||||||
// framesDrawn() frames drawn since the probe started
|
// framesDrawn() frames drawn since the probe started
|
||||||
|
|
||||||
const plain = (position) =>
|
const plain = (position) =>
|
||||||
@@ -83,7 +83,7 @@ export function createQaProbe({ engine, dataManager, availability }) {
|
|||||||
native: handle.native?.id ?? null,
|
native: handle.native?.id ?? null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
movers: () => engine.movers.list(),
|
movers: () => engine.movers?.list() ?? [],
|
||||||
framesDrawn: () => frames,
|
framesDrawn: () => frames,
|
||||||
destroy: () => removeFrames(),
|
destroy: () => removeFrames(),
|
||||||
};
|
};
|
||||||
|
|||||||
+5
-5
@@ -217,11 +217,11 @@ async function createRenderer({
|
|||||||
googleTileset: tileset,
|
googleTileset: tileset,
|
||||||
cesiumToken,
|
cesiumToken,
|
||||||
initialStack: tileset ? 'photoreal' : 'esri-imagery',
|
initialStack: tileset ? 'photoreal' : 'esri-imagery',
|
||||||
// Task 5 (height-datum fix): rebroadcast stack changes as a window
|
// Rebroadcast stack changes as a window CustomEvent so data layers (CCTV
|
||||||
// CustomEvent so data layers (CCTV per-regime ground resolution) can
|
// per-regime ground resolution) can react without coupling
|
||||||
// react without coupling MapStackController to layer modules. Fires on
|
// MapStackController to layer modules. Fires on 'switching'/'ready'/
|
||||||
// 'switching'/'ready'/'error'; listeners derive the surface regime from
|
// 'error'; listeners derive the surface regime from live scene state, so
|
||||||
// live scene state, so intermediate emissions are harmless.
|
// intermediate emissions are harmless.
|
||||||
onChange: (state) => {
|
onChange: (state) => {
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new CustomEvent('gev:map-stack-changed', { detail: state }),
|
new CustomEvent('gev:map-stack-changed', { detail: state }),
|
||||||
|
|||||||
@@ -9,13 +9,6 @@
|
|||||||
// a release that runs, in reverse, when the part detaches (before its
|
// a release that runs, in reverse, when the part detaches (before its
|
||||||
// detachScene).
|
// detachScene).
|
||||||
|
|
||||||
const STATES = Object.freeze([
|
|
||||||
'detached',
|
|
||||||
'attaching',
|
|
||||||
'attached',
|
|
||||||
'detaching',
|
|
||||||
]);
|
|
||||||
|
|
||||||
export class SceneSwitchingError extends Error {
|
export class SceneSwitchingError extends Error {
|
||||||
constructor(message = 'The globe is switching engines') {
|
constructor(message = 'The globe is switching engines') {
|
||||||
super(message);
|
super(message);
|
||||||
@@ -208,5 +201,3 @@ export function createSceneHost({ onError = null } = {}) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SCENE_HOST_STATES = STATES;
|
|
||||||
+1
-1
@@ -327,7 +327,7 @@ function attachViewerTools(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Install the explicit scope mask used by the DISPLAY controls.
|
// Install the explicit scope mask used by the DISPLAY controls.
|
||||||
installScopeMask(viewer);
|
installScopeMask(engine);
|
||||||
defer(() => destroyScopeMask());
|
defer(() => destroyScopeMask());
|
||||||
|
|
||||||
// The follow camera recomputes the tracked target's dead-reckon position
|
// The follow camera recomputes the tracked target's dead-reckon position
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const PITCH_CLAMP_MIN_HEIGHT_M = 300_000;
|
|||||||
// with the first Cesium globe, after this module (src/app/loadCesium.js).
|
// with the first Cesium globe, after this module (src/app/loadCesium.js).
|
||||||
const RADIANS_PER_DEGREE = Math.PI / 180;
|
const RADIANS_PER_DEGREE = Math.PI / 180;
|
||||||
/** Look this far below the Earth's limb, so the globe stays on screen. */
|
/** Look this far below the Earth's limb, so the globe stays on screen. */
|
||||||
export const LIMB_MARGIN_RAD = 8 * RADIANS_PER_DEGREE;
|
const LIMB_MARGIN_RAD = 8 * RADIANS_PER_DEGREE;
|
||||||
const PITCH_EASE = 0.35;
|
const PITCH_EASE = 0.35;
|
||||||
const PITCH_SNAP_RAD = 0.1 * RADIANS_PER_DEGREE;
|
const PITCH_SNAP_RAD = 0.1 * RADIANS_PER_DEGREE;
|
||||||
const HEIGHT_EPSILON_M = 0.0001;
|
const HEIGHT_EPSILON_M = 0.0001;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Deterministic Launch Library 2 records for phase-3 mission/replay acceptance.
|
// Deterministic Launch Library 2 records for phase-3 mission/replay acceptance.
|
||||||
import { SATELLITE_FIXTURE_EPOCH } from './satelliteFixture.js';
|
import { SATELLITE_FIXTURE_EPOCH } from './satelliteFixture.js';
|
||||||
export const LAUNCH_FIXTURE_EPOCH = SATELLITE_FIXTURE_EPOCH;
|
const LAUNCH_FIXTURE_EPOCH = SATELLITE_FIXTURE_EPOCH;
|
||||||
export function launchFixture() {
|
export function launchFixture() {
|
||||||
const definitions = [
|
const definitions = [
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Deterministic TLE catalogs for phase 3 browser QA; no live feed requests.
|
// Deterministic TLE catalogs for browser QA; no live feed requests.
|
||||||
import { W1_FROZEN_MS } from './w1Feeds.js';
|
import { W1_FROZEN_MS } from './w1Feeds.js';
|
||||||
|
|
||||||
const checksum = (line) => {
|
const checksum = (line) => {
|
||||||
@@ -30,8 +30,6 @@ const GROUPS = {
|
|||||||
starlink: [50000, 6000, 53, 15.2, 'QA STARLINK'],
|
starlink: [50000, 6000, 53, 15.2, 'QA STARLINK'],
|
||||||
};
|
};
|
||||||
export const SATELLITE_FIXTURE_EPOCH = W1_FROZEN_MS;
|
export const SATELLITE_FIXTURE_EPOCH = W1_FROZEN_MS;
|
||||||
export const SATELLITE_FIXTURE_CORE_COUNT = 839;
|
|
||||||
export const SATELLITE_FIXTURE_DENSE_COUNT = 6839;
|
|
||||||
export function satelliteFixture(group) {
|
export function satelliteFixture(group) {
|
||||||
const spec = GROUPS[group];
|
const spec = GROUPS[group];
|
||||||
if (!spec) throw new TypeError('Unknown fixture group');
|
if (!spec) throw new TypeError('Unknown fixture group');
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ function fakeViewer({ cameraHeightM, surfaceM, canvas = null }) {
|
|||||||
const position = Cesium.Cartographic.fromDegrees(-97.7603, 30.3141, cameraHeightM);
|
const position = Cesium.Cartographic.fromDegrees(-97.7603, 30.3141, cameraHeightM);
|
||||||
const viewer = {
|
const viewer = {
|
||||||
flights,
|
flights,
|
||||||
|
canvas,
|
||||||
scene: {
|
scene: {
|
||||||
canvas,
|
canvas,
|
||||||
sampleHeightSupported: true,
|
sampleHeightSupported: true,
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ test('vessel and fire layers announce valid clicks and never fly cameras', () =>
|
|||||||
assert.doesNotMatch(source, /camera\.flyTo/);
|
assert.doesNotMatch(source, /camera\.flyTo/);
|
||||||
}
|
}
|
||||||
// Vessels' click rule is a function the engine's selection dispatcher
|
// Vessels' click rule is a function the engine's selection dispatcher
|
||||||
// calls (phase 3, 13a): the same body, out of the old LEFT_CLICK handler.
|
// calls: the same body, out of the old LEFT_CLICK handler.
|
||||||
const vesselClick = body(
|
const vesselClick = body(
|
||||||
vessels,
|
vessels,
|
||||||
/function handleClick\(click\) \{([\s\S]*?)\n \}/,
|
/function handleClick\(click\) \{([\s\S]*?)\n \}/,
|
||||||
|
|||||||
@@ -2370,17 +2370,3 @@ export function flyRoute(
|
|||||||
};
|
};
|
||||||
return typeof runNavigation === 'function' ? runNavigation(start) : start();
|
return typeof runNavigation === 'function' ? runNavigation(start) : start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// QA (development builds only; production builds drop it): the verbs and
|
|
||||||
// their diagnostics, so a browser scenario can drive them on an engine whose
|
|
||||||
// voice tools do not offer them yet.
|
|
||||||
if (import.meta.env?.DEV && typeof window !== 'undefined')
|
|
||||||
window.__gevCameraVerbs = Object.freeze({
|
|
||||||
cameraVerbsSupported,
|
|
||||||
flyRoute,
|
|
||||||
getActiveCameraMotion,
|
|
||||||
initCameraVerbs,
|
|
||||||
interruptCameraMotion,
|
|
||||||
moveCamera,
|
|
||||||
releaseCameraVerbs,
|
|
||||||
});
|
|
||||||
@@ -261,10 +261,8 @@ const TRACKED_RASTER_PX = 192;
|
|||||||
* Default size serves the fleet; pass `aircraftIcon(kind, TRACKED_ICON_PX)`
|
* Default size serves the fleet; pass `aircraftIcon(kind, TRACKED_ICON_PX)`
|
||||||
* (re-exported below) for the tracked billboard. */
|
* (re-exported below) for the tracked billboard. */
|
||||||
export const TRACKED_ICON_PX = TRACKED_RASTER_PX;
|
export const TRACKED_ICON_PX = TRACKED_RASTER_PX;
|
||||||
/** The fleet raster size (aircraftIcon's default). */
|
|
||||||
export const FLEET_ICON_PX = FLEET_RASTER_PX;
|
|
||||||
/** Every silhouette kind aircraftIcon draws (an engine's sprite atlas). */
|
/** Every silhouette kind aircraftIcon draws (an engine's sprite atlas). */
|
||||||
export const AIRCRAFT_ICON_KINDS = Object.freeze(Object.keys(BODIES));
|
const AIRCRAFT_ICON_KINDS = Object.freeze(Object.keys(BODIES));
|
||||||
export function aircraftIcon(kind, px = FLEET_RASTER_PX) {
|
export function aircraftIcon(kind, px = FLEET_RASTER_PX) {
|
||||||
const k = BODIES[kind] ? kind : 'airliner';
|
const k = BODIES[kind] ? kind : 'airliner';
|
||||||
const key = `${k}@${px}`;
|
const key = `${k}@${px}`;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { engineFromViewer } from '../globe/cesium/engineFromViewer.js';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Arrival corrections belong to one globe engine and yield to its next camera
|
* Arrival corrections belong to one globe engine and yield to its next camera
|
||||||
* owner. Callers may name the engine or (layers until phase 1c) the viewer it
|
* owner. Callers may name the engine or (layers that still hold one) the
|
||||||
* is registered for; both reach the same correction.
|
* viewer it is registered for; both reach the same correction.
|
||||||
*/
|
*/
|
||||||
const arrivals = new WeakMap();
|
const arrivals = new WeakMap();
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ for (const layer of LAYERS) {
|
|||||||
|
|
||||||
test(`${layer.name}: every GLB creation bypasses the tile-contended frame-spread queue`, () => {
|
test(`${layer.name}: every GLB creation bypasses the tile-contended frame-spread queue`, () => {
|
||||||
// The aircraft renderer loads through its `loadModel`, Cesium.Model.fromGltfAsync
|
// The aircraft renderer loads through its `loadModel`, Cesium.Model.fromGltfAsync
|
||||||
// unless a test injects a loader (phase 3).
|
// unless a test injects a loader.
|
||||||
assert.match(source, /loadModel = \(options\) => Cesium\.Model\.fromGltfAsync\(options\)/);
|
assert.match(source, /loadModel = \(options\) => Cesium\.Model\.fromGltfAsync\(options\)/);
|
||||||
const calls = [...source.matchAll(/(?:Cesium\.Model\.fromGltfAsync|\bloadModel)\(\{([\s\S]*?)\}\)/g)];
|
const calls = [...source.matchAll(/(?:Cesium\.Model\.fromGltfAsync|\bloadModel)\(\{([\s\S]*?)\}\)/g)];
|
||||||
assert.ok(calls.length >= 3, `expected fleet, tracked, and preload model calls; found ${calls.length}`);
|
assert.ok(calls.length >= 3, `expected fleet, tracked, and preload model calls; found ${calls.length}`);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const OPENMAPTILES_CREDIT = {
|
|||||||
key: 'openfreemap',
|
key: 'openfreemap',
|
||||||
html: 'Vector tiles: <a href="https://openfreemap.org" target="_blank" rel="noopener">OpenFreeMap</a> © <a href="https://openmaptiles.org" target="_blank" rel="noopener">OpenMapTiles</a>',
|
html: 'Vector tiles: <a href="https://openfreemap.org" target="_blank" rel="noopener">OpenFreeMap</a> © <a href="https://openmaptiles.org" target="_blank" rel="noopener">OpenMapTiles</a>',
|
||||||
};
|
};
|
||||||
export const GOOGLE_PLACES_CREDIT = {
|
const GOOGLE_PLACES_CREDIT = {
|
||||||
key: 'google-maps-places',
|
key: 'google-maps-places',
|
||||||
html: 'Google Maps Places: <a href="https://developers.google.com/maps/documentation/places/web-service/policies" target="_blank" rel="noopener">Google Maps</a>',
|
html: 'Google Maps Places: <a href="https://developers.google.com/maps/documentation/places/web-service/policies" target="_blank" rel="noopener">Google Maps</a>',
|
||||||
};
|
};
|
||||||
@@ -45,7 +45,7 @@ const viewerCredits = new WeakMap();
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Credits of a globe engine, or of a Cesium viewer's credit display for
|
* Credits of a globe engine, or of a Cesium viewer's credit display for
|
||||||
* callers that still hold a viewer (layers until phase 1c); null without one.
|
* callers that still hold a viewer (Cesium layers); null without one.
|
||||||
* @returns {{ addStatic: Function, request: () => void } | null}
|
* @returns {{ addStatic: Function, request: () => void } | null}
|
||||||
*/
|
*/
|
||||||
function creditsOf(target) {
|
function creditsOf(target) {
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ test('every scene click handler consults ownership before it picks', () => {
|
|||||||
'src/layers/submarineCables/interaction.js',
|
'src/layers/submarineCables/interaction.js',
|
||||||
'viewer.scene.pick(click.position)',
|
'viewer.scene.pick(click.position)',
|
||||||
],
|
],
|
||||||
// Vessels take the engine dispatcher's pick (phase 3, 13a): the guard
|
// Vessels take the engine dispatcher's pick: the guard
|
||||||
// comes before the click rule reads it.
|
// comes before the click rule reads it.
|
||||||
['src/layers/vessels/selection.js', 'resolvePickId(picked)'],
|
['src/layers/vessels/selection.js', 'resolvePickId(picked)'],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ test('sampler on a globe engine: samples engine.surface and latches against the
|
|||||||
assert.equal(cachedMeshFloor(cell.lat, cell.lon), 137.0);
|
assert.equal(cachedMeshFloor(cell.lat, cell.lon), 137.0);
|
||||||
const [call] = engine.fake.callsOf('surface.sampleHeightDetailed');
|
const [call] = engine.fake.callsOf('surface.sampleHeightDetailed');
|
||||||
assert.ok(Math.abs(Cesium.Math.toDegrees(call.args[0].latitude) - cell.lat) < 1e-9);
|
assert.ok(Math.abs(Cesium.Math.toDegrees(call.args[0].latitude) - cell.lat) < 1e-9);
|
||||||
assert.deepEqual(call.args[1], { exclude: ['own'], lod: 'visible' });
|
assert.deepEqual(call.args[1], { exclude: ['own'] });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sampler: a coarse tile answer is a miss; a fine one latches', async () => {
|
test('sampler: a coarse tile answer is a miss; a fine one latches', async () => {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Native constants shared by the W1 adapters; keep the SDK import here.
|
// Native constants shared by the W1 adapters; keep the SDK import here.
|
||||||
// Read on call: the production build's Cesium global loads with the first
|
// Read on call: the production build's Cesium global loads with the first
|
||||||
// Cesium globe, after this module has loaded on a Navara start.
|
// Cesium globe, after this module has loaded on a Navara start.
|
||||||
import { Axis, ScreenSpaceEventType } from 'cesium';
|
import { ScreenSpaceEventType } from 'cesium';
|
||||||
|
|
||||||
export const modelAxis = () => Axis;
|
|
||||||
export const trackingEventTypes = () => ScreenSpaceEventType;
|
export const trackingEventTypes = () => ScreenSpaceEventType;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { nearbyContact } from './pickRegistry.js';
|
import { nearbyContact } from './pickRegistry.js';
|
||||||
// One selection dispatcher for the layers that have moved onto the globe
|
// One selection dispatcher for the layers that draw through the globe
|
||||||
// engine (phase 3 Task 4): one click subscription with picks
|
// engine: one click subscription with picks
|
||||||
// (engine.input.on('click', …, { pick: true, drill: 8 })), the press
|
// (engine.input.on('click', …, { pick: true, drill: 8 })), the press
|
||||||
// gesture and one Escape listener, instead of a ScreenSpaceEventHandler
|
// gesture and one Escape listener, instead of a ScreenSpaceEventHandler
|
||||||
// per layer. Owners register as with pickRegistry (registerPickOwner, so
|
// per layer. Owners register as with pickRegistry (registerPickOwner, so
|
||||||
@@ -38,7 +38,7 @@ import {
|
|||||||
isTrackingSelectionGesture,
|
isTrackingSelectionGesture,
|
||||||
} from './trackingClickGesture.js';
|
} from './trackingClickGesture.js';
|
||||||
|
|
||||||
export const SELECTION_DRILL = 8;
|
const SELECTION_DRILL = 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('../globe/contract.js').GlobeEngine} engine
|
* @param {import('../globe/contract.js').GlobeEngine} engine
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ test('flights, AIS, and FIRMS enable paths are wired through the shared sprite r
|
|||||||
assert.deepEqual(calls, [viewer, viewer, viewer]);
|
assert.deepEqual(calls, [viewer, viewer, viewer]);
|
||||||
|
|
||||||
const firmsLayer = createFirmsHeatmapLayer({ id: 'firms', name: 'FIRMS' });
|
const firmsLayer = createFirmsHeatmapLayer({ id: 'firms', name: 'FIRMS' });
|
||||||
// Flights draws through its renderer (phase 3): enable asks the fleet to
|
// Flights draws through its renderer: enable asks the fleet to
|
||||||
// restore its order, which the Cesium aircraft renderer wires to the shared
|
// restore its order, which the Cesium aircraft renderer wires to the shared
|
||||||
// restorer under the flights' sprite slot.
|
// restorer under the flights' sprite slot.
|
||||||
assert.match(flightsLayer.enable.toString(), /fleet\.restoreOrderOnEnable\(\)/);
|
assert.match(flightsLayer.enable.toString(), /fleet\.restoreOrderOnEnable\(\)/);
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ test('civilian and military click handlers apply duration only at the deselect b
|
|||||||
readLayerSource(new URL('./flights.js', import.meta.url)),
|
readLayerSource(new URL('./flights.js', import.meta.url)),
|
||||||
readLayerSource(new URL('./militaryFlights.js', import.meta.url)),
|
readLayerSource(new URL('./militaryFlights.js', import.meta.url)),
|
||||||
];
|
];
|
||||||
// Flights and military moved to the selection dispatcher (phase 3), whose
|
// Flights and military use the selection dispatcher, whose
|
||||||
// source applies the same two rules: the selection gesture before a
|
// source applies the same two rules: the selection gesture before a
|
||||||
// select, the full click classifier before the empty-space deselect, which
|
// select, the full click classifier before the empty-space deselect, which
|
||||||
// each layer answers with a user-origin clear.
|
// each layer answers with a user-origin clear.
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
// Seeded, deterministic bit-for-bit comparison of src/geo against Cesium.
|
// Seeded, deterministic bit-for-bit comparison of src/geo against Cesium's
|
||||||
|
// release build (Build/Cesium, debug checks stripped, as production ships it).
|
||||||
// Every number is compared with Object.is (so -0 and NaN count), a throw on one
|
// Every number is compared with Object.is (so -0 and NaN count), a throw on one
|
||||||
// side must be a throw on the other, and `result` aliasing is exercised the
|
// side must be a throw on the other, and `result` aliasing is exercised the
|
||||||
// same way on both sides.
|
// same way on both sides.
|
||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import * as Cesium from 'cesium';
|
import { createRequire } from 'node:module';
|
||||||
import * as geo from './index.js';
|
import * as geo from './index.js';
|
||||||
|
|
||||||
|
const Cesium = createRequire(import.meta.url)('cesium/Build/Cesium/index.cjs');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
cartesian3: c3,
|
cartesian3: c3,
|
||||||
cartographic,
|
cartographic,
|
||||||
@@ -108,7 +111,7 @@ const cesium = (v) => new C3(v.x, v.y, v.z);
|
|||||||
|
|
||||||
test('vector operations, including -0 inputs and aliased results, equal Cesium bit for bit', () => {
|
test('vector operations, including -0 inputs and aliased results, equal Cesium bit for bit', () => {
|
||||||
const { scalar, vector } = inputs(1);
|
const { scalar, vector } = inputs(1);
|
||||||
// Directionless vectors first: normalize must throw on both sides.
|
// Directionless vectors first: normalize returns NaN on both sides.
|
||||||
const DEGENERATE = [
|
const DEGENERATE = [
|
||||||
{ x: 0, y: 0, z: 0 },
|
{ x: 0, y: 0, z: 0 },
|
||||||
{ x: -0, y: 0, z: -0 },
|
{ x: -0, y: 0, z: -0 },
|
||||||
@@ -397,12 +400,7 @@ test('matrices and quaternions, including aliased results and every fromRotation
|
|||||||
][i % 4];
|
][i % 4];
|
||||||
const angle =
|
const angle =
|
||||||
i % 8 < 4 ? Math.PI - 0.5 * random() : (random() - 0.5) * 2 * Math.PI;
|
i % 8 < 4 ? Math.PI - 0.5 * random() : (random() - 0.5) * 2 * Math.PI;
|
||||||
let m;
|
const m = rotation(axis, angle);
|
||||||
try {
|
|
||||||
m = rotation(axis, angle);
|
|
||||||
} catch {
|
|
||||||
continue; // a zero axis: Cesium throws, nothing to compare
|
|
||||||
}
|
|
||||||
const trace = m[0] + m[4] + m[8];
|
const trace = m[0] + m[4] + m[8];
|
||||||
branches.add(
|
branches.add(
|
||||||
trace > 0
|
trace > 0
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Ported from CesiumJS 1.138 (Apache-2.0) — BoundingSphere.js subset.
|
// Ported from CesiumJS 1.138 (Apache-2.0) — BoundingSphere.js subset.
|
||||||
// Exact mission framing: Ritter/box fit and the union of two spheres.
|
// Exact mission framing: Ritter/box fit and the union of two spheres.
|
||||||
import * as Cartesian3 from './cartesian3.js';
|
import * as Cartesian3 from './cartesian3.js';
|
||||||
import { Check } from './check.js';
|
|
||||||
import { DEBUG } from './debug.js';
|
|
||||||
const defined = (value) => value !== null && value !== undefined;
|
const defined = (value) => value !== null && value !== undefined;
|
||||||
export function create(center = Cartesian3.ZERO, radius = 0) {
|
export function create(center = Cartesian3.ZERO, radius = 0) {
|
||||||
return { center: Cartesian3.clone(center), radius };
|
return { center: Cartesian3.clone(center), radius };
|
||||||
@@ -184,11 +182,6 @@ export function fromPoints(positions, result) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
export function union(left, right, result) {
|
export function union(left, right, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('left', left);
|
|
||||||
Check.typeOf.object('right', right);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!defined(result)) {
|
if (!defined(result)) {
|
||||||
result = create();
|
result = create();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-11
@@ -1,17 +1,12 @@
|
|||||||
// Ported from CesiumJS 1.138 (Apache-2.0) — Cartesian2.js
|
// Ported from CesiumJS 1.138 (Apache-2.0) — Cartesian2.js
|
||||||
//
|
//
|
||||||
// 2D vectors (screen coordinates) as plain-data {x, y} objects. With DEBUG on
|
// 2D vectors (screen coordinates) as plain-data {x, y} objects.
|
||||||
// (development and tests; see debug.js) invalid arguments throw the same
|
|
||||||
// DeveloperError as Cesium's debug checks.
|
|
||||||
//
|
//
|
||||||
// Geo values are not Object.prototype objects, so src/app/stateChannel.js
|
// Geo values are not Object.prototype objects, so src/app/stateChannel.js
|
||||||
// copyState and src/director/documentFields.js jsonTree reject them: copy
|
// copyState and src/director/documentFields.js jsonTree reject them: copy
|
||||||
// the fields into a literal before putting one into product state or a
|
// the fields into a literal before putting one into product state or a
|
||||||
// scene document.
|
// scene document.
|
||||||
|
|
||||||
import { DEBUG } from './debug.js';
|
|
||||||
import { Check } from './check.js';
|
|
||||||
|
|
||||||
// Values are instances of a module-private constructor rather than object
|
// Values are instances of a module-private constructor rather than object
|
||||||
// literals: every `{x, y}` literal in the process shares one V8 hidden
|
// literals: every `{x, y}` literal in the process shares one V8 hidden
|
||||||
// class, and code elsewhere that builds such literals would make geo's hot
|
// class, and code elsewhere that builds such literals would make geo's hot
|
||||||
@@ -38,11 +33,6 @@ export function clone(cartesian, result) {
|
|||||||
|
|
||||||
/** The distance between two points. */
|
/** The distance between two points. */
|
||||||
export function distance(left, right) {
|
export function distance(left, right) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('left', left);
|
|
||||||
Check.typeOf.object('right', right);
|
|
||||||
}
|
|
||||||
|
|
||||||
const x = left.x - right.x;
|
const x = left.x - right.x;
|
||||||
const y = left.y - right.y;
|
const y = left.y - right.y;
|
||||||
return Math.sqrt(x * x + y * y);
|
return Math.sqrt(x * x + y * y);
|
||||||
|
|||||||
+3
-144
@@ -3,19 +3,14 @@
|
|||||||
// 3D vectors as plain-data {x, y, z} objects. Arithmetic takes a required `result`
|
// 3D vectors as plain-data {x, y, z} objects. Arithmetic takes a required `result`
|
||||||
// (as Cesium does) that may be one of the inputs; creators take an optional
|
// (as Cesium does) that may be one of the inputs; creators take an optional
|
||||||
// one. The operation order matches Cesium so the outputs are bit-identical.
|
// one. The operation order matches Cesium so the outputs are bit-identical.
|
||||||
// With DEBUG on (development and tests; see debug.js) invalid arguments throw
|
// As in release Cesium, arguments are not checked and `normalize` returns NaN
|
||||||
// the same DeveloperError as Cesium's debug checks, and `normalize` throws on a
|
// components for a zero or non-finite vector.
|
||||||
// zero or non-finite vector; production builds skip the checks and return NaN
|
|
||||||
// components there, as release Cesium does.
|
|
||||||
//
|
//
|
||||||
// Geo values are not Object.prototype objects, so src/app/stateChannel.js
|
// Geo values are not Object.prototype objects, so src/app/stateChannel.js
|
||||||
// copyState and src/director/documentFields.js jsonTree reject them: copy
|
// copyState and src/director/documentFields.js jsonTree reject them: copy
|
||||||
// the fields into a literal before putting one into product state or a
|
// the fields into a literal before putting one into product state or a
|
||||||
// scene document.
|
// scene document.
|
||||||
|
|
||||||
import { DEBUG } from './debug.js';
|
|
||||||
import { Check } from './check.js';
|
|
||||||
import { DeveloperError } from './developerError.js';
|
|
||||||
import { equalsEpsilon as scalarEqualsEpsilon, toRadians } from './math.js';
|
import { equalsEpsilon as scalarEqualsEpsilon, toRadians } from './math.js';
|
||||||
|
|
||||||
// Values are instances of a module-private constructor rather than object
|
// Values are instances of a module-private constructor rather than object
|
||||||
@@ -59,10 +54,6 @@ export const UNIT_Y = Object.freeze(create(0.0, 1.0, 0.0));
|
|||||||
export const UNIT_Z = Object.freeze(create(0.0, 0.0, 1.0));
|
export const UNIT_Z = Object.freeze(create(0.0, 0.0, 1.0));
|
||||||
|
|
||||||
export function magnitudeSquared(cartesian) {
|
export function magnitudeSquared(cartesian) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('cartesian', cartesian);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
cartesian.x * cartesian.x +
|
cartesian.x * cartesian.x +
|
||||||
cartesian.y * cartesian.y +
|
cartesian.y * cartesian.y +
|
||||||
@@ -77,60 +68,29 @@ export function magnitude(cartesian) {
|
|||||||
const distanceScratch = create();
|
const distanceScratch = create();
|
||||||
|
|
||||||
export function distance(left, right) {
|
export function distance(left, right) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('left', left);
|
|
||||||
Check.typeOf.object('right', right);
|
|
||||||
}
|
|
||||||
|
|
||||||
subtract(left, right, distanceScratch);
|
subtract(left, right, distanceScratch);
|
||||||
return magnitude(distanceScratch);
|
return magnitude(distanceScratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function distanceSquared(left, right) {
|
export function distanceSquared(left, right) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('left', left);
|
|
||||||
Check.typeOf.object('right', right);
|
|
||||||
}
|
|
||||||
|
|
||||||
subtract(left, right, distanceScratch);
|
subtract(left, right, distanceScratch);
|
||||||
return magnitudeSquared(distanceScratch);
|
return magnitudeSquared(distanceScratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The unit vector; with DEBUG on, throws when the vector has no direction (zero or non-finite). */
|
/** The unit vector; NaN components when the vector has no direction (zero or non-finite). */
|
||||||
export function normalize(cartesian, result) {
|
export function normalize(cartesian, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('cartesian', cartesian);
|
|
||||||
Check.typeOf.object('result', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
const length = magnitude(cartesian);
|
const length = magnitude(cartesian);
|
||||||
result.x = cartesian.x / length;
|
result.x = cartesian.x / length;
|
||||||
result.y = cartesian.y / length;
|
result.y = cartesian.y / length;
|
||||||
result.z = cartesian.z / length;
|
result.z = cartesian.z / length;
|
||||||
if (DEBUG) {
|
|
||||||
if (isNaN(result.x) || isNaN(result.y) || isNaN(result.z)) {
|
|
||||||
throw new DeveloperError('normalized result is not a number');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dot(left, right) {
|
export function dot(left, right) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('left', left);
|
|
||||||
Check.typeOf.object('right', right);
|
|
||||||
}
|
|
||||||
|
|
||||||
return left.x * right.x + left.y * right.y + left.z * right.z;
|
return left.x * right.x + left.y * right.y + left.z * right.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function multiplyComponents(left, right, result) {
|
export function multiplyComponents(left, right, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('left', left);
|
|
||||||
Check.typeOf.object('right', right);
|
|
||||||
Check.typeOf.object('result', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.x = left.x * right.x;
|
result.x = left.x * right.x;
|
||||||
result.y = left.y * right.y;
|
result.y = left.y * right.y;
|
||||||
result.z = left.z * right.z;
|
result.z = left.z * right.z;
|
||||||
@@ -138,12 +98,6 @@ export function multiplyComponents(left, right, result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function add(left, right, result) {
|
export function add(left, right, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('left', left);
|
|
||||||
Check.typeOf.object('right', right);
|
|
||||||
Check.typeOf.object('result', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.x = left.x + right.x;
|
result.x = left.x + right.x;
|
||||||
result.y = left.y + right.y;
|
result.y = left.y + right.y;
|
||||||
result.z = left.z + right.z;
|
result.z = left.z + right.z;
|
||||||
@@ -151,12 +105,6 @@ export function add(left, right, result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function subtract(left, right, result) {
|
export function subtract(left, right, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('left', left);
|
|
||||||
Check.typeOf.object('right', right);
|
|
||||||
Check.typeOf.object('result', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.x = left.x - right.x;
|
result.x = left.x - right.x;
|
||||||
result.y = left.y - right.y;
|
result.y = left.y - right.y;
|
||||||
result.z = left.z - right.z;
|
result.z = left.z - right.z;
|
||||||
@@ -164,12 +112,6 @@ export function subtract(left, right, result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function multiplyByScalar(cartesian, scalar, result) {
|
export function multiplyByScalar(cartesian, scalar, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('cartesian', cartesian);
|
|
||||||
Check.typeOf.number('scalar', scalar);
|
|
||||||
Check.typeOf.object('result', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.x = cartesian.x * scalar;
|
result.x = cartesian.x * scalar;
|
||||||
result.y = cartesian.y * scalar;
|
result.y = cartesian.y * scalar;
|
||||||
result.z = cartesian.z * scalar;
|
result.z = cartesian.z * scalar;
|
||||||
@@ -177,12 +119,6 @@ export function multiplyByScalar(cartesian, scalar, result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function divideByScalar(cartesian, scalar, result) {
|
export function divideByScalar(cartesian, scalar, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('cartesian', cartesian);
|
|
||||||
Check.typeOf.number('scalar', scalar);
|
|
||||||
Check.typeOf.object('result', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.x = cartesian.x / scalar;
|
result.x = cartesian.x / scalar;
|
||||||
result.y = cartesian.y / scalar;
|
result.y = cartesian.y / scalar;
|
||||||
result.z = cartesian.z / scalar;
|
result.z = cartesian.z / scalar;
|
||||||
@@ -190,11 +126,6 @@ export function divideByScalar(cartesian, scalar, result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function negate(cartesian, result) {
|
export function negate(cartesian, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('cartesian', cartesian);
|
|
||||||
Check.typeOf.object('result', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.x = -cartesian.x;
|
result.x = -cartesian.x;
|
||||||
result.y = -cartesian.y;
|
result.y = -cartesian.y;
|
||||||
result.z = -cartesian.z;
|
result.z = -cartesian.z;
|
||||||
@@ -204,25 +135,12 @@ export function negate(cartesian, result) {
|
|||||||
const lerpScratch = create();
|
const lerpScratch = create();
|
||||||
|
|
||||||
export function lerp(start, end, t, result) {
|
export function lerp(start, end, t, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('start', start);
|
|
||||||
Check.typeOf.object('end', end);
|
|
||||||
Check.typeOf.number('t', t);
|
|
||||||
Check.typeOf.object('result', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
multiplyByScalar(end, t, lerpScratch);
|
multiplyByScalar(end, t, lerpScratch);
|
||||||
result = multiplyByScalar(start, 1.0 - t, result);
|
result = multiplyByScalar(start, 1.0 - t, result);
|
||||||
return add(lerpScratch, result, result);
|
return add(lerpScratch, result, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cross(left, right, result) {
|
export function cross(left, right, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('left', left);
|
|
||||||
Check.typeOf.object('right', right);
|
|
||||||
Check.typeOf.object('result', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
const leftX = left.x;
|
const leftX = left.x;
|
||||||
const leftY = left.y;
|
const leftY = left.y;
|
||||||
const leftZ = left.z;
|
const leftZ = left.z;
|
||||||
@@ -241,12 +159,6 @@ export function cross(left, right, result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function midpoint(left, right, result) {
|
export function midpoint(left, right, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('left', left);
|
|
||||||
Check.typeOf.object('right', right);
|
|
||||||
Check.typeOf.object('result', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.x = (left.x + right.x) * 0.5;
|
result.x = (left.x + right.x) * 0.5;
|
||||||
result.y = (left.y + right.y) * 0.5;
|
result.y = (left.y + right.y) * 0.5;
|
||||||
result.z = (left.z + right.z) * 0.5;
|
result.z = (left.z + right.z) * 0.5;
|
||||||
@@ -292,11 +204,6 @@ let scratchK = create();
|
|||||||
|
|
||||||
/** The Earth-fixed position of a geodetic one in radians on the ellipsoid (WGS84 by default). */
|
/** The Earth-fixed position of a geodetic one in radians on the ellipsoid (WGS84 by default). */
|
||||||
export function fromRadians(longitude, latitude, height, ellipsoid, result) {
|
export function fromRadians(longitude, latitude, height, ellipsoid, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.number('longitude', longitude);
|
|
||||||
Check.typeOf.number('latitude', latitude);
|
|
||||||
}
|
|
||||||
|
|
||||||
height = height ?? 0.0;
|
height = height ?? 0.0;
|
||||||
const radiiSquared =
|
const radiiSquared =
|
||||||
ellipsoid == null ? WGS84_RADII_SQUARED : ellipsoid.radiiSquared;
|
ellipsoid == null ? WGS84_RADII_SQUARED : ellipsoid.radiiSquared;
|
||||||
@@ -318,11 +225,6 @@ export function fromRadians(longitude, latitude, height, ellipsoid, result) {
|
|||||||
|
|
||||||
/** The Earth-fixed position of a geodetic one in degrees on the ellipsoid (WGS84 by default). */
|
/** The Earth-fixed position of a geodetic one in degrees on the ellipsoid (WGS84 by default). */
|
||||||
export function fromDegrees(longitude, latitude, height, ellipsoid, result) {
|
export function fromDegrees(longitude, latitude, height, ellipsoid, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.number('longitude', longitude);
|
|
||||||
Check.typeOf.number('latitude', latitude);
|
|
||||||
}
|
|
||||||
|
|
||||||
longitude = toRadians(longitude);
|
longitude = toRadians(longitude);
|
||||||
latitude = toRadians(latitude);
|
latitude = toRadians(latitude);
|
||||||
return fromRadians(longitude, latitude, height, ellipsoid, result);
|
return fromRadians(longitude, latitude, height, ellipsoid, result);
|
||||||
@@ -330,15 +232,6 @@ export function fromDegrees(longitude, latitude, height, ellipsoid, result) {
|
|||||||
|
|
||||||
/** Positions from a flat [lon, lat, lon, lat, …] list in degrees, at height 0. */
|
/** Positions from a flat [lon, lat, lon, lat, …] list in degrees, at height 0. */
|
||||||
export function fromDegreesArray(coordinates, ellipsoid, result) {
|
export function fromDegreesArray(coordinates, ellipsoid, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.defined('coordinates', coordinates);
|
|
||||||
if (coordinates.length < 2 || coordinates.length % 2 !== 0) {
|
|
||||||
throw new DeveloperError(
|
|
||||||
'the number of coordinates must be a multiple of 2 and at least 2',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const length = coordinates.length;
|
const length = coordinates.length;
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = new Array(length / 2);
|
result = new Array(length / 2);
|
||||||
@@ -358,43 +251,9 @@ export function fromDegreesArray(coordinates, ellipsoid, result) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Positions from a flat [lon, lat, height, …] list in degrees and metres. */
|
|
||||||
export function fromDegreesArrayHeights(coordinates, ellipsoid, result) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Check.defined('coordinates', coordinates);
|
|
||||||
if (coordinates.length < 3 || coordinates.length % 3 !== 0) {
|
|
||||||
throw new DeveloperError(
|
|
||||||
'the number of coordinates must be a multiple of 3 and at least 3',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const length = coordinates.length;
|
|
||||||
if (result == null) {
|
|
||||||
result = new Array(length / 3);
|
|
||||||
} else {
|
|
||||||
result.length = length / 3;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < length; i += 3) {
|
|
||||||
const index = i / 3;
|
|
||||||
result[index] = fromDegrees(
|
|
||||||
coordinates[i],
|
|
||||||
coordinates[i + 1],
|
|
||||||
coordinates[i + 2],
|
|
||||||
ellipsoid,
|
|
||||||
result[index],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const angleBetweenScratch = create();
|
const angleBetweenScratch = create();
|
||||||
const angleBetweenScratch2 = create();
|
const angleBetweenScratch2 = create();
|
||||||
export function angleBetween(left, right) {
|
export function angleBetween(left, right) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('left', left);
|
|
||||||
Check.typeOf.object('right', right);
|
|
||||||
}
|
|
||||||
normalize(left, angleBetweenScratch);
|
normalize(left, angleBetweenScratch);
|
||||||
normalize(right, angleBetweenScratch2);
|
normalize(right, angleBetweenScratch2);
|
||||||
const cosine = dot(angleBetweenScratch, angleBetweenScratch2);
|
const cosine = dot(angleBetweenScratch, angleBetweenScratch2);
|
||||||
|
|||||||
@@ -21,78 +21,16 @@ const same = (ours, theirs, label) =>
|
|||||||
label,
|
label,
|
||||||
);
|
);
|
||||||
|
|
||||||
test('binary vector ops equal Cesium bit for bit', () => {
|
test('magnitudeSquared equals Cesium', () => {
|
||||||
for (const a of SAMPLES)
|
|
||||||
for (const b of SAMPLES) {
|
|
||||||
for (const op of ['add', 'subtract', 'cross', 'multiplyComponents']) {
|
|
||||||
same(
|
|
||||||
c3[op](asPlain(a), asPlain(b), c3.create()),
|
|
||||||
C[op](asCesium(a), asCesium(b), new C()),
|
|
||||||
`${op}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
assert.equal(
|
|
||||||
c3.dot(asPlain(a), asPlain(b)),
|
|
||||||
C.dot(asCesium(a), asCesium(b)),
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
c3.distance(asPlain(a), asPlain(b)),
|
|
||||||
C.distance(asCesium(a), asCesium(b)),
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
c3.distanceSquared(asPlain(a), asPlain(b)),
|
|
||||||
C.distanceSquared(asCesium(a), asCesium(b)),
|
|
||||||
);
|
|
||||||
same(
|
|
||||||
c3.lerp(asPlain(a), asPlain(b), 0.3, c3.create()),
|
|
||||||
C.lerp(asCesium(a), asCesium(b), 0.3, new C()),
|
|
||||||
'lerp',
|
|
||||||
);
|
|
||||||
same(
|
|
||||||
c3.midpoint(asPlain(a), asPlain(b), c3.create()),
|
|
||||||
C.midpoint(asCesium(a), asCesium(b), new C()),
|
|
||||||
'midpoint',
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
c3.equalsEpsilon(asPlain(a), asPlain(b), 1e-7),
|
|
||||||
C.equalsEpsilon(asCesium(a), asCesium(b), 1e-7),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('unary ops and scalars equal Cesium', () => {
|
|
||||||
for (const a of SAMPLES) {
|
for (const a of SAMPLES) {
|
||||||
assert.equal(c3.magnitude(asPlain(a)), C.magnitude(asCesium(a)));
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
c3.magnitudeSquared(asPlain(a)),
|
c3.magnitudeSquared(asPlain(a)),
|
||||||
C.magnitudeSquared(asCesium(a)),
|
C.magnitudeSquared(asCesium(a)),
|
||||||
);
|
);
|
||||||
same(
|
|
||||||
c3.negate(asPlain(a), c3.create()),
|
|
||||||
C.negate(asCesium(a), new C()),
|
|
||||||
'negate',
|
|
||||||
);
|
|
||||||
same(
|
|
||||||
c3.multiplyByScalar(asPlain(a), 2.5, c3.create()),
|
|
||||||
C.multiplyByScalar(asCesium(a), 2.5, new C()),
|
|
||||||
'mul',
|
|
||||||
);
|
|
||||||
same(
|
|
||||||
c3.divideByScalar(asPlain(a), 4, c3.create()),
|
|
||||||
C.divideByScalar(asCesium(a), 4, new C()),
|
|
||||||
'div',
|
|
||||||
);
|
|
||||||
if (C.magnitude(asCesium(a)) !== 0) {
|
|
||||||
same(
|
|
||||||
c3.normalize(asPlain(a), c3.create()),
|
|
||||||
C.normalize(asCesium(a), new C()),
|
|
||||||
'normalize',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('geodetic constructors equal Cesium', () => {
|
test('fromRadians and fromDegreesArray equal Cesium', () => {
|
||||||
for (const [lon, lat, h] of [
|
for (const [lon, lat, h] of [
|
||||||
[0, 0, 0],
|
[0, 0, 0],
|
||||||
[-73.9857, 40.7484, 381],
|
[-73.9857, 40.7484, 381],
|
||||||
@@ -100,11 +38,6 @@ test('geodetic constructors equal Cesium', () => {
|
|||||||
[180, -90, 1e4],
|
[180, -90, 1e4],
|
||||||
[12.5, 89.999999, 1],
|
[12.5, 89.999999, 1],
|
||||||
]) {
|
]) {
|
||||||
same(
|
|
||||||
c3.fromDegrees(lon, lat, h),
|
|
||||||
C.fromDegrees(lon, lat, h),
|
|
||||||
`fromDegrees ${lon},${lat}`,
|
|
||||||
);
|
|
||||||
const lr = [Cesium.Math.toRadians(lon), Cesium.Math.toRadians(lat), h];
|
const lr = [Cesium.Math.toRadians(lon), Cesium.Math.toRadians(lat), h];
|
||||||
same(c3.fromRadians(...lr), C.fromRadians(...lr), 'fromRadians');
|
same(c3.fromRadians(...lr), C.fromRadians(...lr), 'fromRadians');
|
||||||
}
|
}
|
||||||
@@ -112,10 +45,6 @@ test('geodetic constructors equal Cesium', () => {
|
|||||||
c3.fromDegreesArray(flat).forEach((p, i) =>
|
c3.fromDegreesArray(flat).forEach((p, i) =>
|
||||||
same(p, C.fromDegreesArray(flat)[i], `array ${i}`),
|
same(p, C.fromDegreesArray(flat)[i], `array ${i}`),
|
||||||
);
|
);
|
||||||
const heights = [-74, 40.7, 10, -73.9, 40.8, 20];
|
|
||||||
c3.fromDegreesArrayHeights(heights).forEach((p, i) =>
|
|
||||||
same(p, C.fromDegreesArrayHeights(heights)[i], `heights ${i}`),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('result aliasing works like Cesium and creators reuse result', () => {
|
test('result aliasing works like Cesium and creators reuse result', () => {
|
||||||
@@ -138,20 +67,3 @@ test('plain results are accepted by Cesium functions (duck typing)', () => {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('normalize throws on a zero or NaN vector, as Cesium does when run from source', () => {
|
|
||||||
for (const [x, y, z] of [
|
|
||||||
[0, 0, 0],
|
|
||||||
[-0, 0, -0],
|
|
||||||
[Number.NaN, 1, 2],
|
|
||||||
]) {
|
|
||||||
assert.throws(
|
|
||||||
() => C.normalize(new C(x, y, z), new C()),
|
|
||||||
/normalized result is not a number/,
|
|
||||||
);
|
|
||||||
assert.throws(
|
|
||||||
() => c3.normalize({ x, y, z }, c3.create()),
|
|
||||||
/normalized result is not a number/,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
+1
-19
@@ -1,17 +1,13 @@
|
|||||||
// Ported from CesiumJS 1.138 (Apache-2.0) — Cartographic.js
|
// Ported from CesiumJS 1.138 (Apache-2.0) — Cartographic.js
|
||||||
//
|
//
|
||||||
// Geodetic positions as plain-data {longitude, latitude, height} objects (radians and
|
// Geodetic positions as plain-data {longitude, latitude, height} objects (radians and
|
||||||
// metres). Every function takes an optional `result` and returns it. With
|
// metres). Every function takes an optional `result` and returns it.
|
||||||
// DEBUG on (development and tests; see debug.js) invalid arguments throw the
|
|
||||||
// same DeveloperError as Cesium's debug checks.
|
|
||||||
//
|
//
|
||||||
// Geo values are not Object.prototype objects, so src/app/stateChannel.js
|
// Geo values are not Object.prototype objects, so src/app/stateChannel.js
|
||||||
// copyState and src/director/documentFields.js jsonTree reject them: copy
|
// copyState and src/director/documentFields.js jsonTree reject them: copy
|
||||||
// the fields into a literal before putting one into product state or a
|
// the fields into a literal before putting one into product state or a
|
||||||
// scene document.
|
// scene document.
|
||||||
|
|
||||||
import { DEBUG } from './debug.js';
|
|
||||||
import { Check } from './check.js';
|
|
||||||
import { toRadians } from './math.js';
|
import { toRadians } from './math.js';
|
||||||
import * as cartesian3 from './cartesian3.js';
|
import * as cartesian3 from './cartesian3.js';
|
||||||
import { scaleToGeodeticSurface } from './scaleToGeodeticSurface.js';
|
import { scaleToGeodeticSurface } from './scaleToGeodeticSurface.js';
|
||||||
@@ -51,11 +47,6 @@ const WGS84_CENTER_TOLERANCE_SQUARED = 0.1;
|
|||||||
|
|
||||||
/** A geodetic position from radians. */
|
/** A geodetic position from radians. */
|
||||||
export function fromRadians(longitude, latitude, height, result) {
|
export function fromRadians(longitude, latitude, height, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.number('longitude', longitude);
|
|
||||||
Check.typeOf.number('latitude', latitude);
|
|
||||||
}
|
|
||||||
|
|
||||||
height = height ?? 0.0;
|
height = height ?? 0.0;
|
||||||
if (result == null) return create(longitude, latitude, height);
|
if (result == null) return create(longitude, latitude, height);
|
||||||
result.longitude = longitude;
|
result.longitude = longitude;
|
||||||
@@ -66,11 +57,6 @@ export function fromRadians(longitude, latitude, height, result) {
|
|||||||
|
|
||||||
/** A geodetic position from degrees; the result is in radians. */
|
/** A geodetic position from degrees; the result is in radians. */
|
||||||
export function fromDegrees(longitude, latitude, height, result) {
|
export function fromDegrees(longitude, latitude, height, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.number('longitude', longitude);
|
|
||||||
Check.typeOf.number('latitude', latitude);
|
|
||||||
}
|
|
||||||
|
|
||||||
longitude = toRadians(longitude);
|
longitude = toRadians(longitude);
|
||||||
latitude = toRadians(latitude);
|
latitude = toRadians(latitude);
|
||||||
return fromRadians(longitude, latitude, height, result);
|
return fromRadians(longitude, latitude, height, result);
|
||||||
@@ -133,10 +119,6 @@ export function fromCartesian(cartesian, ellipsoid, result) {
|
|||||||
* where Cesium uses Ellipsoid.default; gev never changes it).
|
* where Cesium uses Ellipsoid.default; gev never changes it).
|
||||||
*/
|
*/
|
||||||
export function toCartesian(cartographic, ellipsoid, result) {
|
export function toCartesian(cartographic, ellipsoid, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.defined('cartographic', cartographic);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cartesian3.fromRadians(
|
return cartesian3.fromRadians(
|
||||||
cartographic.longitude,
|
cartographic.longitude,
|
||||||
cartographic.latitude,
|
cartographic.latitude,
|
||||||
|
|||||||
@@ -4,12 +4,6 @@ import assert from 'node:assert/strict';
|
|||||||
import * as Cesium from 'cesium';
|
import * as Cesium from 'cesium';
|
||||||
import * as cartographic from './cartographic.js';
|
import * as cartographic from './cartographic.js';
|
||||||
|
|
||||||
const close = (a, b, tol, label) =>
|
|
||||||
assert.ok(
|
|
||||||
Math.abs(a - b) <= tol * Math.max(1, Math.abs(b)),
|
|
||||||
`${label}: ${a} vs ${b}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
test('fromDegrees and fromRadians equal Cesium', () => {
|
test('fromDegrees and fromRadians equal Cesium', () => {
|
||||||
for (const [lon, lat, h] of [
|
for (const [lon, lat, h] of [
|
||||||
[0, 0, 0],
|
[0, 0, 0],
|
||||||
@@ -30,29 +24,6 @@ test('fromDegrees and fromRadians equal Cesium', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fromCartesian and toCartesian equal Cesium, including the centre case', () => {
|
|
||||||
for (const [x, y, z] of [
|
|
||||||
[1334762.2, -4654869.1, 4137287.9],
|
|
||||||
[6378137, 0, 0],
|
|
||||||
[0.01, 0, 0],
|
|
||||||
]) {
|
|
||||||
const ours = cartographic.fromCartesian({ x, y, z });
|
|
||||||
const theirs = Cesium.Cartographic.fromCartesian(
|
|
||||||
new Cesium.Cartesian3(x, y, z),
|
|
||||||
);
|
|
||||||
if (theirs === undefined) {
|
|
||||||
assert.equal(ours, undefined);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
close(ours.longitude, theirs.longitude, 1e-12, 'lon');
|
|
||||||
close(ours.latitude, theirs.latitude, 1e-12, 'lat');
|
|
||||||
close(ours.height, theirs.height, 1e-9, 'h');
|
|
||||||
const back = cartographic.toCartesian(ours);
|
|
||||||
const cBack = Cesium.Cartographic.toCartesian(theirs);
|
|
||||||
for (const k of ['x', 'y', 'z']) close(back[k], cBack[k], 1e-12, k);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('result objects are reused', () => {
|
test('result objects are reused', () => {
|
||||||
const out = cartographic.create();
|
const out = cartographic.create();
|
||||||
assert.equal(cartographic.fromDegrees(1, 2, 3, out), out);
|
assert.equal(cartographic.fromDegrees(1, 2, 3, out), out);
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
// Ported from CesiumJS 1.138 (Apache-2.0) — Check.js
|
|
||||||
//
|
|
||||||
// Cesium's argument checks with the same conditions and message text. Only the
|
|
||||||
// checks src/geo needs are ported. Callers run them inside `if (DEBUG)` blocks
|
|
||||||
// (see debug.js), as Cesium runs them inside its debug pragmas, so production
|
|
||||||
// builds drop the calls and this module with them.
|
|
||||||
|
|
||||||
import { DeveloperError } from './developerError.js';
|
|
||||||
|
|
||||||
const undefinedMessage = (name) =>
|
|
||||||
`${name} is required, actual value was undefined`;
|
|
||||||
const typeMessage = (actual, expected, name) =>
|
|
||||||
`Expected ${name} to be typeof ${expected}, actual typeof was ${actual}`;
|
|
||||||
|
|
||||||
/** Throws unless `test` is neither undefined nor null. */
|
|
||||||
function defined(name, test) {
|
|
||||||
if (test === undefined || test === null) {
|
|
||||||
throw new DeveloperError(undefinedMessage(name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Throws unless `typeof test` is 'number' (NaN passes, as in Cesium). */
|
|
||||||
function number(name, test) {
|
|
||||||
if (typeof test !== 'number') {
|
|
||||||
throw new DeveloperError(typeMessage(typeof test, 'number', name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attached in a function marked pure, not by top-level assignments, so that a
|
|
||||||
// production build, where no caller runs a check, drops this module entirely.
|
|
||||||
function withBounds(check) {
|
|
||||||
check.lessThanOrEquals = function (name, test, limit) {
|
|
||||||
check(name, test);
|
|
||||||
if (test > limit) {
|
|
||||||
throw new DeveloperError(
|
|
||||||
`Expected ${name} to be less than or equal to ${limit}, actual value was ${test}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
check.greaterThanOrEquals = function (name, test, limit) {
|
|
||||||
check(name, test);
|
|
||||||
if (test < limit) {
|
|
||||||
throw new DeveloperError(
|
|
||||||
`Expected ${name} to be greater than or equal to ${limit}, actual value was ${test}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return check;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Throws unless `typeof test` is 'object' (null passes, as in Cesium). */
|
|
||||||
function object(name, test) {
|
|
||||||
if (typeof test !== 'object') {
|
|
||||||
throw new DeveloperError(typeMessage(typeof test, 'object', name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Check = /* @__PURE__ */ Object.freeze({
|
|
||||||
defined,
|
|
||||||
typeOf: Object.freeze({ number: /* @__PURE__ */ withBounds(number), object }),
|
|
||||||
});
|
|
||||||
@@ -1,486 +0,0 @@
|
|||||||
// Argument-check parity with Cesium. gev runs Cesium from source, where its
|
|
||||||
// debug checks are live, so for any input both sides must throw an error with
|
|
||||||
// the same name and message, or neither may throw. The inputs are seeded and
|
|
||||||
// deterministic: each case builds its arguments twice from the same seed, once
|
|
||||||
// for geo and once for Cesium, so no object is shared between the two sides.
|
|
||||||
import { test } from 'node:test';
|
|
||||||
import assert from 'node:assert/strict';
|
|
||||||
import * as Cesium from 'cesium';
|
|
||||||
import * as geo from './index.js';
|
|
||||||
import { DeveloperError } from './developerError.js';
|
|
||||||
|
|
||||||
const TRIALS = 150;
|
|
||||||
|
|
||||||
function generator(seed) {
|
|
||||||
return () => {
|
|
||||||
seed = (seed + 0x6d2b79f5) | 0;
|
|
||||||
let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
|
|
||||||
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
||||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid values every argument may receive. NaN is a number to Check.typeOf.number
|
|
||||||
// and null an object to Check.typeOf.object, so both reach the arithmetic.
|
|
||||||
const INVALID = [undefined, null, Number.NaN, 'abc', 5, true, {}];
|
|
||||||
|
|
||||||
const vectorValues = { x: 1.5, y: -2, z: 3 };
|
|
||||||
const VALID = {
|
|
||||||
num: (side, random) => [0, -0, 0.7, -2.5, 1e6][Math.floor(random() * 5)],
|
|
||||||
index3: (side, random) => Math.floor(random() * 5) - 1,
|
|
||||||
index4: (side, random) => Math.floor(random() * 6) - 1,
|
|
||||||
nonNegative: (side, random) => [0, 3, 6378137, -1][Math.floor(random() * 4)],
|
|
||||||
vec: (side) =>
|
|
||||||
side === 'geo' ? { ...vectorValues } : new Cesium.Cartesian3(1.5, -2, 3),
|
|
||||||
zeroVec: (side) =>
|
|
||||||
side === 'geo' ? { x: 0, y: 0, z: 0 } : new Cesium.Cartesian3(),
|
|
||||||
vec4: (side) =>
|
|
||||||
side === 'geo' ? { x: 0, y: 0, z: 0, w: 0 } : new Cesium.Cartesian4(),
|
|
||||||
carto: (side) =>
|
|
||||||
side === 'geo'
|
|
||||||
? { longitude: 0.3, latitude: -0.2, height: 10 }
|
|
||||||
: new Cesium.Cartographic(0.3, -0.2, 10),
|
|
||||||
partialCarto: (side) =>
|
|
||||||
side === 'geo'
|
|
||||||
? { longitude: 0.3, latitude: -0.2 }
|
|
||||||
: { longitude: 0.3, latitude: -0.2 },
|
|
||||||
quat: (side) =>
|
|
||||||
side === 'geo'
|
|
||||||
? { x: 0.1, y: 0.2, z: 0.3, w: 0.9 }
|
|
||||||
: new Cesium.Quaternion(0.1, 0.2, 0.3, 0.9),
|
|
||||||
mat3: (side) =>
|
|
||||||
side === 'geo'
|
|
||||||
? geo.matrix3.fromRotationZ(0.4)
|
|
||||||
: Cesium.Matrix3.fromRotationZ(0.4, new Cesium.Matrix3()),
|
|
||||||
mat4: (side) =>
|
|
||||||
side === 'geo'
|
|
||||||
? geo.transforms.eastNorthUpToFixedFrame({ x: 6378137, y: 1, z: 2 })
|
|
||||||
: Cesium.Transforms.eastNorthUpToFixedFrame(
|
|
||||||
new Cesium.Cartesian3(6378137, 1, 2),
|
|
||||||
),
|
|
||||||
hpr: (side) =>
|
|
||||||
side === 'geo'
|
|
||||||
? { heading: 0.3, pitch: -0.2, roll: 0.1 }
|
|
||||||
: new Cesium.HeadingPitchRoll(0.3, -0.2, 0.1),
|
|
||||||
partialHpr: () => ({ heading: 0.3 }),
|
|
||||||
ellipsoid: (side) =>
|
|
||||||
side === 'geo' ? geo.ellipsoid.WGS84 : Cesium.Ellipsoid.WGS84,
|
|
||||||
coords2: (side, random) =>
|
|
||||||
[[1, 2], [1, 2, 3], [], [1, 2, 3, 4]][Math.floor(random() * 4)],
|
|
||||||
coords3: (side, random) =>
|
|
||||||
[[1, 2, 3], [1, 2], [], [1, 2, 3, 4, 5, 6], [1, 2, 'h']][
|
|
||||||
Math.floor(random() * 5)
|
|
||||||
],
|
|
||||||
nanVec: (side) =>
|
|
||||||
side === 'geo'
|
|
||||||
? { x: 1, y: Number.NaN, z: 2 }
|
|
||||||
: new Cesium.Cartesian3(1, Number.NaN, 2),
|
|
||||||
partialVec: () => ({ x: 1, y: 2 }),
|
|
||||||
stringHeight: () => '5',
|
|
||||||
};
|
|
||||||
|
|
||||||
/** One argument: usually a valid value of its kind, sometimes an invalid one. */
|
|
||||||
function argument(kinds, side, random) {
|
|
||||||
if (random() < 0.4) return INVALID[Math.floor(random() * INVALID.length)];
|
|
||||||
const kind = kinds[Math.floor(random() * kinds.length)];
|
|
||||||
return VALID[kind](side, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
const W = Cesium.Ellipsoid.WGS84;
|
|
||||||
const C3 = Cesium.Cartesian3;
|
|
||||||
const V = ['vec', 'zeroVec', 'nanVec', 'partialVec'];
|
|
||||||
const N = ['num'];
|
|
||||||
const R = ['vec'];
|
|
||||||
|
|
||||||
// [name, geo function, Cesium function, argument kinds per position]
|
|
||||||
const CASES = [
|
|
||||||
['math.toRadians', geo.math.toRadians, Cesium.Math.toRadians, [N]],
|
|
||||||
['math.toDegrees', geo.math.toDegrees, Cesium.Math.toDegrees, [N]],
|
|
||||||
['math.clamp', geo.math.clamp, Cesium.Math.clamp, [N, N, N]],
|
|
||||||
['math.lerp', geo.math.lerp, Cesium.Math.lerp, [N, N, N]],
|
|
||||||
['math.mod', geo.math.mod, Cesium.Math.mod, [N, ['num', 'nonNegative']]],
|
|
||||||
['math.zeroToTwoPi', geo.math.zeroToTwoPi, Cesium.Math.zeroToTwoPi, [N]],
|
|
||||||
[
|
|
||||||
'math.negativePiToPi',
|
|
||||||
geo.math.negativePiToPi,
|
|
||||||
Cesium.Math.negativePiToPi,
|
|
||||||
[N],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'math.equalsEpsilon',
|
|
||||||
geo.math.equalsEpsilon,
|
|
||||||
Cesium.Math.equalsEpsilon,
|
|
||||||
[N, N, N, N],
|
|
||||||
],
|
|
||||||
|
|
||||||
['cartesian2.clone', geo.cartesian2.clone, Cesium.Cartesian2.clone, [R, R]],
|
|
||||||
[
|
|
||||||
'cartesian2.distance',
|
|
||||||
geo.cartesian2.distance,
|
|
||||||
Cesium.Cartesian2.distance,
|
|
||||||
[R, R],
|
|
||||||
],
|
|
||||||
|
|
||||||
['cartesian3.clone', geo.cartesian3.clone, C3.clone, [V, R]],
|
|
||||||
[
|
|
||||||
'cartesian3.fromElements',
|
|
||||||
geo.cartesian3.fromElements,
|
|
||||||
C3.fromElements,
|
|
||||||
[N, N, N, R],
|
|
||||||
],
|
|
||||||
['cartesian3.magnitude', geo.cartesian3.magnitude, C3.magnitude, [V]],
|
|
||||||
[
|
|
||||||
'cartesian3.magnitudeSquared',
|
|
||||||
geo.cartesian3.magnitudeSquared,
|
|
||||||
C3.magnitudeSquared,
|
|
||||||
[V],
|
|
||||||
],
|
|
||||||
['cartesian3.distance', geo.cartesian3.distance, C3.distance, [V, V]],
|
|
||||||
[
|
|
||||||
'cartesian3.distanceSquared',
|
|
||||||
geo.cartesian3.distanceSquared,
|
|
||||||
C3.distanceSquared,
|
|
||||||
[V, V],
|
|
||||||
],
|
|
||||||
['cartesian3.normalize', geo.cartesian3.normalize, C3.normalize, [V, R]],
|
|
||||||
['cartesian3.dot', geo.cartesian3.dot, C3.dot, [V, V]],
|
|
||||||
[
|
|
||||||
'cartesian3.multiplyComponents',
|
|
||||||
geo.cartesian3.multiplyComponents,
|
|
||||||
C3.multiplyComponents,
|
|
||||||
[V, V, R],
|
|
||||||
],
|
|
||||||
['cartesian3.add', geo.cartesian3.add, C3.add, [V, V, R]],
|
|
||||||
['cartesian3.subtract', geo.cartesian3.subtract, C3.subtract, [V, V, R]],
|
|
||||||
[
|
|
||||||
'cartesian3.multiplyByScalar',
|
|
||||||
geo.cartesian3.multiplyByScalar,
|
|
||||||
C3.multiplyByScalar,
|
|
||||||
[V, N, R],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'cartesian3.divideByScalar',
|
|
||||||
geo.cartesian3.divideByScalar,
|
|
||||||
C3.divideByScalar,
|
|
||||||
[V, N, R],
|
|
||||||
],
|
|
||||||
['cartesian3.negate', geo.cartesian3.negate, C3.negate, [V, R]],
|
|
||||||
['cartesian3.lerp', geo.cartesian3.lerp, C3.lerp, [V, V, N, R]],
|
|
||||||
['cartesian3.cross', geo.cartesian3.cross, C3.cross, [V, V, R]],
|
|
||||||
['cartesian3.midpoint', geo.cartesian3.midpoint, C3.midpoint, [V, V, R]],
|
|
||||||
['cartesian3.equals', geo.cartesian3.equals, C3.equals, [V, V]],
|
|
||||||
[
|
|
||||||
'cartesian3.equalsEpsilon',
|
|
||||||
geo.cartesian3.equalsEpsilon,
|
|
||||||
C3.equalsEpsilon,
|
|
||||||
[V, V, N, N],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'cartesian3.fromRadians',
|
|
||||||
geo.cartesian3.fromRadians,
|
|
||||||
C3.fromRadians,
|
|
||||||
[N, N, ['num', 'stringHeight'], ['ellipsoid'], R],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'cartesian3.fromDegrees',
|
|
||||||
geo.cartesian3.fromDegrees,
|
|
||||||
C3.fromDegrees,
|
|
||||||
[N, N, ['num', 'stringHeight'], ['ellipsoid'], R],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'cartesian3.fromDegreesArray',
|
|
||||||
geo.cartesian3.fromDegreesArray,
|
|
||||||
C3.fromDegreesArray,
|
|
||||||
[['coords2'], ['ellipsoid']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'cartesian3.fromDegreesArrayHeights',
|
|
||||||
geo.cartesian3.fromDegreesArrayHeights,
|
|
||||||
C3.fromDegreesArrayHeights,
|
|
||||||
[['coords3'], ['ellipsoid']],
|
|
||||||
],
|
|
||||||
|
|
||||||
[
|
|
||||||
'cartographic.fromRadians',
|
|
||||||
geo.cartographic.fromRadians,
|
|
||||||
Cesium.Cartographic.fromRadians,
|
|
||||||
[N, N, N, ['carto']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'cartographic.fromDegrees',
|
|
||||||
geo.cartographic.fromDegrees,
|
|
||||||
Cesium.Cartographic.fromDegrees,
|
|
||||||
[N, N, N, ['carto']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'cartographic.fromCartesian',
|
|
||||||
geo.cartographic.fromCartesian,
|
|
||||||
Cesium.Cartographic.fromCartesian,
|
|
||||||
[V, ['ellipsoid'], ['carto']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'cartographic.toCartesian',
|
|
||||||
geo.cartographic.toCartesian,
|
|
||||||
Cesium.Cartographic.toCartesian,
|
|
||||||
[['carto', 'partialCarto'], ['ellipsoid'], R],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'cartographic.clone',
|
|
||||||
geo.cartographic.clone,
|
|
||||||
Cesium.Cartographic.clone,
|
|
||||||
[['carto'], ['carto']],
|
|
||||||
],
|
|
||||||
|
|
||||||
[
|
|
||||||
'ellipsoid.create',
|
|
||||||
geo.ellipsoid.create,
|
|
||||||
(x, y, z) => new Cesium.Ellipsoid(x, y, z),
|
|
||||||
[['nonNegative'], ['nonNegative'], ['nonNegative']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'WGS84.geodeticSurfaceNormal',
|
|
||||||
geo.ellipsoid.WGS84.geodeticSurfaceNormal,
|
|
||||||
(c, r) => W.geodeticSurfaceNormal(c, r),
|
|
||||||
[V, R],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'WGS84.geodeticSurfaceNormalCartographic',
|
|
||||||
geo.ellipsoid.WGS84.geodeticSurfaceNormalCartographic,
|
|
||||||
(c, r) => W.geodeticSurfaceNormalCartographic(c, r),
|
|
||||||
[['carto', 'partialCarto'], R],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'WGS84.scaleToGeodeticSurface',
|
|
||||||
geo.ellipsoid.WGS84.scaleToGeodeticSurface,
|
|
||||||
(c, r) => W.scaleToGeodeticSurface(c, r),
|
|
||||||
[V, R],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'WGS84.cartographicToCartesian',
|
|
||||||
geo.ellipsoid.WGS84.cartographicToCartesian,
|
|
||||||
(c, r) => W.cartographicToCartesian(c, r),
|
|
||||||
[['carto', 'partialCarto'], R],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'WGS84.cartesianToCartographic',
|
|
||||||
geo.ellipsoid.WGS84.cartesianToCartographic,
|
|
||||||
(c, r) => W.cartesianToCartographic(c, r),
|
|
||||||
[V, ['carto']],
|
|
||||||
],
|
|
||||||
|
|
||||||
[
|
|
||||||
'matrix3.fromQuaternion',
|
|
||||||
geo.matrix3.fromQuaternion,
|
|
||||||
Cesium.Matrix3.fromQuaternion,
|
|
||||||
[['quat'], ['mat3']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix3.fromRotationZ',
|
|
||||||
geo.matrix3.fromRotationZ,
|
|
||||||
Cesium.Matrix3.fromRotationZ,
|
|
||||||
[N, ['mat3']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix3.multiplyByVector',
|
|
||||||
geo.matrix3.multiplyByVector,
|
|
||||||
Cesium.Matrix3.multiplyByVector,
|
|
||||||
[['mat3'], V, R],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix3.getColumn',
|
|
||||||
geo.matrix3.getColumn,
|
|
||||||
Cesium.Matrix3.getColumn,
|
|
||||||
[['mat3'], ['index3'], R],
|
|
||||||
],
|
|
||||||
|
|
||||||
[
|
|
||||||
'matrix4.clone',
|
|
||||||
geo.matrix4.clone,
|
|
||||||
Cesium.Matrix4.clone,
|
|
||||||
[['mat4'], ['mat4']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix4.equals',
|
|
||||||
geo.matrix4.equals,
|
|
||||||
Cesium.Matrix4.equals,
|
|
||||||
[['mat4'], ['mat4']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix4.multiply',
|
|
||||||
geo.matrix4.multiply,
|
|
||||||
Cesium.Matrix4.multiply,
|
|
||||||
[['mat4'], ['mat4'], ['mat4']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix4.multiplyTransformation',
|
|
||||||
geo.matrix4.multiplyTransformation,
|
|
||||||
Cesium.Matrix4.multiplyTransformation,
|
|
||||||
[['mat4'], ['mat4'], ['mat4']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix4.multiplyByPoint',
|
|
||||||
geo.matrix4.multiplyByPoint,
|
|
||||||
Cesium.Matrix4.multiplyByPoint,
|
|
||||||
[['mat4'], V, R],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix4.multiplyByPointAsVector',
|
|
||||||
geo.matrix4.multiplyByPointAsVector,
|
|
||||||
Cesium.Matrix4.multiplyByPointAsVector,
|
|
||||||
[['mat4'], V, R],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix4.getTranslation',
|
|
||||||
geo.matrix4.getTranslation,
|
|
||||||
Cesium.Matrix4.getTranslation,
|
|
||||||
[['mat4'], R],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix4.getMatrix3',
|
|
||||||
geo.matrix4.getMatrix3,
|
|
||||||
Cesium.Matrix4.getMatrix3,
|
|
||||||
[['mat4'], ['mat3']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix4.getColumn',
|
|
||||||
geo.matrix4.getColumn,
|
|
||||||
Cesium.Matrix4.getColumn,
|
|
||||||
[['mat4'], ['index4'], ['vec4']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix4.inverseTransformation',
|
|
||||||
geo.matrix4.inverseTransformation,
|
|
||||||
Cesium.Matrix4.inverseTransformation,
|
|
||||||
[['mat4'], ['mat4']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix4.fromRotationTranslation',
|
|
||||||
geo.matrix4.fromRotationTranslation,
|
|
||||||
Cesium.Matrix4.fromRotationTranslation,
|
|
||||||
[['mat3'], R, ['mat4']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'matrix4.fromUniformScale',
|
|
||||||
geo.matrix4.fromUniformScale,
|
|
||||||
Cesium.Matrix4.fromUniformScale,
|
|
||||||
[N, ['mat4']],
|
|
||||||
],
|
|
||||||
|
|
||||||
[
|
|
||||||
'quaternion.fromAxisAngle',
|
|
||||||
geo.quaternion.fromAxisAngle,
|
|
||||||
Cesium.Quaternion.fromAxisAngle,
|
|
||||||
[V, N, ['quat']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'quaternion.fromRotationMatrix',
|
|
||||||
geo.quaternion.fromRotationMatrix,
|
|
||||||
Cesium.Quaternion.fromRotationMatrix,
|
|
||||||
[['mat3'], ['quat']],
|
|
||||||
],
|
|
||||||
|
|
||||||
[
|
|
||||||
'transforms.eastNorthUpToFixedFrame',
|
|
||||||
geo.transforms.eastNorthUpToFixedFrame,
|
|
||||||
Cesium.Transforms.eastNorthUpToFixedFrame,
|
|
||||||
[V, ['ellipsoid'], ['mat4']],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'transforms.headingPitchRollToFixedFrame',
|
|
||||||
geo.transforms.headingPitchRollToFixedFrame,
|
|
||||||
Cesium.Transforms.headingPitchRollToFixedFrame,
|
|
||||||
[V, ['hpr', 'partialHpr'], ['ellipsoid'], [], ['mat4']],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
function outcome(fn, args) {
|
|
||||||
try {
|
|
||||||
fn(...args);
|
|
||||||
return 'returned';
|
|
||||||
} catch (error) {
|
|
||||||
return `${error?.name}: ${error?.message}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test('every ported function throws exactly where Cesium does, with the same name and message', () => {
|
|
||||||
let thrown = 0;
|
|
||||||
let checked = 0;
|
|
||||||
const mismatches = [];
|
|
||||||
CASES.forEach(([name, ours, theirs, kinds], caseIndex) => {
|
|
||||||
for (let trial = 0; trial < TRIALS; trial++) {
|
|
||||||
const seed = caseIndex * 100003 + trial;
|
|
||||||
// Trial 0 passes no arguments at all; the rest draw from the pools.
|
|
||||||
const build = (side) => {
|
|
||||||
if (trial === 0) return [];
|
|
||||||
const random = generator(seed);
|
|
||||||
return kinds.map((k) =>
|
|
||||||
k.length ? argument(k, side, random) : undefined,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const a = outcome(ours, build('geo'));
|
|
||||||
const b = outcome(theirs, build('cesium'));
|
|
||||||
if (a !== b)
|
|
||||||
mismatches.push(`${name} (seed ${seed}): geo ${a} | Cesium ${b}`);
|
|
||||||
checked++;
|
|
||||||
if (b !== 'returned') thrown++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assert.deepEqual(mismatches, []);
|
|
||||||
// The pools must actually reach the checks.
|
|
||||||
assert.ok(thrown > checked / 4, `only ${thrown} of ${checked} cases threw`);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('the checks throw a DeveloperError with Cesium message text', () => {
|
|
||||||
const cases = [
|
|
||||||
[() => geo.math.toRadians(undefined), 'degrees is required.'],
|
|
||||||
[() => geo.math.mod(5, 0), 'divisor cannot be 0.'],
|
|
||||||
[
|
|
||||||
() => geo.math.clamp('1', 0, 1),
|
|
||||||
'Expected value to be typeof number, actual typeof was string',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
() => geo.cartesian3.fromDegrees(undefined, 1),
|
|
||||||
'Expected longitude to be typeof number, actual typeof was undefined',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
() => geo.cartesian3.add({ x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }),
|
|
||||||
'Expected result to be typeof object, actual typeof was undefined',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
() => geo.cartesian3.fromDegreesArray([1]),
|
|
||||||
'the number of coordinates must be a multiple of 2 and at least 2',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
() => geo.cartesian3.fromDegreesArray(),
|
|
||||||
'coordinates is required, actual value was undefined',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
() => geo.ellipsoid.create(-1, 1, 1),
|
|
||||||
'Expected x to be greater than or equal to 0, actual value was -1',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
() => geo.matrix3.getColumn(geo.matrix3.create(), 3, {}),
|
|
||||||
'Expected index to be less than or equal to 2, actual value was 3',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
() =>
|
|
||||||
geo.transforms.eastNorthUpToFixedFrame({ x: 1, y: Number.NaN, z: 0 }),
|
|
||||||
'origin has a NaN component',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
() => geo.cartographic.fromCartesian({ x: 1, y: 2, z: 3 }, {}),
|
|
||||||
'oneOverRadii is required.',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
for (const [fn, message] of cases) {
|
|
||||||
assert.throws(fn, (error) => {
|
|
||||||
assert.ok(error instanceof DeveloperError);
|
|
||||||
assert.equal(error.name, 'DeveloperError');
|
|
||||||
assert.equal(error.message, message);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// NaN is typeof number, so Check.typeOf.number lets it through (as in Cesium).
|
|
||||||
assert.ok(Number.isNaN(geo.math.clamp(Number.NaN, 0, 1)));
|
|
||||||
assert.ok(Number.isNaN(Cesium.Math.clamp(Number.NaN, 0, 1)));
|
|
||||||
});
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
// Whether src/geo runs Cesium's debug argument checks.
|
|
||||||
//
|
|
||||||
// Cesium wraps its argument checks in `//>>includeStart('debug')` pragmas: they
|
|
||||||
// are live in its Source modules (Node tests, the Vite dev server) and stripped
|
|
||||||
// from Build/Cesium, which vite-plugin-cesium copies into production builds.
|
|
||||||
// src/geo does the same: every Check call and every debug throw sits in an
|
|
||||||
// `if (DEBUG)` block. DEBUG is true in Node (no import.meta.env) and in Vite
|
|
||||||
// dev; `vite build` folds it to false and the minifier drops the blocks, so a
|
|
||||||
// production build returns what release Cesium returns (NaN, not a throw, for
|
|
||||||
// a degenerate input).
|
|
||||||
|
|
||||||
export const DEBUG = import.meta.env?.PROD !== true;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
// Ported from CesiumJS 1.138 (Apache-2.0) — DeveloperError.js
|
|
||||||
//
|
|
||||||
// The error Cesium throws for invalid arguments. Its debug argument checks are
|
|
||||||
// live in Cesium's Source modules (Node tests, the Vite dev server) and
|
|
||||||
// stripped from production builds; src/geo throws the same name and message
|
|
||||||
// wherever Cesium does while DEBUG is on (see debug.js).
|
|
||||||
|
|
||||||
export class DeveloperError extends Error {
|
|
||||||
constructor(message) {
|
|
||||||
super(message);
|
|
||||||
this.name = 'DeveloperError';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-22
@@ -3,14 +3,9 @@
|
|||||||
// A quadratic surface x²/a² + y²/b² + z²/c² = 1 as a plain, frozen object.
|
// A quadratic surface x²/a² + y²/b² + z²/c² = 1 as a plain, frozen object.
|
||||||
// Every method takes an optional `result` and returns it (a new object when it
|
// Every method takes an optional `result` and returns it (a new object when it
|
||||||
// is absent); `result` may be the input. The algorithms and the operation order
|
// is absent); `result` may be the input. The algorithms and the operation order
|
||||||
// match Cesium so the outputs are bit-identical. With DEBUG on (development
|
// match Cesium so the outputs are bit-identical.
|
||||||
// and tests; see debug.js) invalid arguments throw the same DeveloperError as
|
|
||||||
// Cesium's debug checks.
|
|
||||||
|
|
||||||
import { DEBUG } from './debug.js';
|
|
||||||
import { Check } from './check.js';
|
|
||||||
import { create as createCartographic } from './cartographic.js';
|
import { create as createCartographic } from './cartographic.js';
|
||||||
import { DeveloperError } from './developerError.js';
|
|
||||||
import { EPSILON14 } from './math.js';
|
import { EPSILON14 } from './math.js';
|
||||||
import { scaleToGeodeticSurface } from './scaleToGeodeticSurface.js';
|
import { scaleToGeodeticSurface } from './scaleToGeodeticSurface.js';
|
||||||
import {
|
import {
|
||||||
@@ -39,11 +34,6 @@ export function create(x, y, z) {
|
|||||||
x = x ?? 0.0;
|
x = x ?? 0.0;
|
||||||
y = y ?? 0.0;
|
y = y ?? 0.0;
|
||||||
z = z ?? 0.0;
|
z = z ?? 0.0;
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.number.greaterThanOrEquals('x', x, 0.0);
|
|
||||||
Check.typeOf.number.greaterThanOrEquals('y', y, 0.0);
|
|
||||||
Check.typeOf.number.greaterThanOrEquals('z', z, 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const radii = Object.freeze(vec(x, y, z));
|
const radii = Object.freeze(vec(x, y, z));
|
||||||
const radiiSquared = Object.freeze(vec(x * x, y * y, z * z));
|
const radiiSquared = Object.freeze(vec(x * x, y * y, z * z));
|
||||||
@@ -68,10 +58,6 @@ export function create(x, y, z) {
|
|||||||
|
|
||||||
/** The normal to the ellipsoid surface at a geodetic position. */
|
/** The normal to the ellipsoid surface at a geodetic position. */
|
||||||
function geodeticSurfaceNormalCartographic(cartographic, result) {
|
function geodeticSurfaceNormalCartographic(cartographic, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('cartographic', cartographic);
|
|
||||||
}
|
|
||||||
|
|
||||||
const longitude = cartographic.longitude;
|
const longitude = cartographic.longitude;
|
||||||
const latitude = cartographic.latitude;
|
const latitude = cartographic.latitude;
|
||||||
const cosLatitude = Math.cos(latitude);
|
const cosLatitude = Math.cos(latitude);
|
||||||
@@ -89,13 +75,6 @@ export function create(x, y, z) {
|
|||||||
|
|
||||||
/** The normal to the ellipsoid surface at a position, or `undefined` at the centre. */
|
/** The normal to the ellipsoid surface at a position, or `undefined` at the centre. */
|
||||||
function geodeticSurfaceNormal(cartesian, result) {
|
function geodeticSurfaceNormal(cartesian, result) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.object('cartesian', cartesian);
|
|
||||||
if (isNaN(cartesian.x) || isNaN(cartesian.y) || isNaN(cartesian.z)) {
|
|
||||||
throw new DeveloperError('cartesian has a NaN component');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (equalsEpsilon(cartesian, ZERO, EPSILON14)) {
|
if (equalsEpsilon(cartesian, ZERO, EPSILON14)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,18 +12,6 @@ const close = (a, b, tol, label) =>
|
|||||||
const closeVec = (a, b, tol, label) => {
|
const closeVec = (a, b, tol, label) => {
|
||||||
for (const k of ['x', 'y', 'z']) close(a[k], b[k], tol, `${label}.${k}`);
|
for (const k of ['x', 'y', 'z']) close(a[k], b[k], tol, `${label}.${k}`);
|
||||||
};
|
};
|
||||||
const POINTS = [
|
|
||||||
[6378137, 0, 0],
|
|
||||||
[0, 6378137, 0],
|
|
||||||
[0, 0, 6356752.314245],
|
|
||||||
[1334762.2, -4654869.1, 4137287.9],
|
|
||||||
[-2694045, -4293642, 3857878],
|
|
||||||
[1e3, 1e3, 1e3],
|
|
||||||
[0.01, 0.01, 0.01],
|
|
||||||
[7e6, -2e6, 1e6],
|
|
||||||
[4510000, 4510000, 1],
|
|
||||||
[1, 1, 6400000],
|
|
||||||
];
|
|
||||||
|
|
||||||
test('WGS84 constants equal Cesium', () => {
|
test('WGS84 constants equal Cesium', () => {
|
||||||
const c = Cesium.Ellipsoid.WGS84;
|
const c = Cesium.Ellipsoid.WGS84;
|
||||||
@@ -40,39 +28,6 @@ test('WGS84 constants equal Cesium', () => {
|
|||||||
assert.equal(WGS84.maximumRadius, c.maximumRadius);
|
assert.equal(WGS84.maximumRadius, c.maximumRadius);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('surface normals, scaling and conversions equal Cesium', () => {
|
|
||||||
const c = Cesium.Ellipsoid.WGS84;
|
|
||||||
for (const [x, y, z] of POINTS) {
|
|
||||||
const p = { x, y, z };
|
|
||||||
const cp = new Cesium.Cartesian3(x, y, z);
|
|
||||||
closeVec(
|
|
||||||
WGS84.geodeticSurfaceNormal(p),
|
|
||||||
c.geodeticSurfaceNormal(cp),
|
|
||||||
1e-12,
|
|
||||||
'normal',
|
|
||||||
);
|
|
||||||
const ours = WGS84.scaleToGeodeticSurface(p);
|
|
||||||
const theirs = c.scaleToGeodeticSurface(cp);
|
|
||||||
if (theirs === undefined) assert.equal(ours, undefined);
|
|
||||||
else closeVec(ours, theirs, 1e-12, 'scale');
|
|
||||||
const carto = WGS84.cartesianToCartographic(p);
|
|
||||||
const cCarto = c.cartesianToCartographic(cp);
|
|
||||||
if (cCarto === undefined) {
|
|
||||||
assert.equal(carto, undefined);
|
|
||||||
} else {
|
|
||||||
close(carto.longitude, cCarto.longitude, 1e-12, 'lon');
|
|
||||||
close(carto.latitude, cCarto.latitude, 1e-12, 'lat');
|
|
||||||
close(carto.height, cCarto.height, 1e-9, 'height');
|
|
||||||
closeVec(
|
|
||||||
WGS84.cartographicToCartesian(carto),
|
|
||||||
c.cartographicToCartesian(cCarto),
|
|
||||||
1e-12,
|
|
||||||
'roundtrip',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('result parameters are filled in place and may alias the input', () => {
|
test('result parameters are filled in place and may alias the input', () => {
|
||||||
const p = { x: 1334762.2, y: -4654869.1, z: 4137287.9 };
|
const p = { x: 1334762.2, y: -4654869.1, z: 4137287.9 };
|
||||||
const out = { x: 0, y: 0, z: 0 };
|
const out = { x: 0, y: 0, z: 0 };
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
// Ported from CesiumJS 1.138 (Apache-2.0) — EllipsoidGeodesic.js.
|
// Ported from CesiumJS 1.138 (Apache-2.0) — EllipsoidGeodesic.js.
|
||||||
// Vincenty inverse and surface interpolation, preserving operation order.
|
// Vincenty inverse and surface interpolation, preserving operation order.
|
||||||
import * as Cartesian3 from './cartesian3.js';
|
|
||||||
import * as Cartographic from './cartographic.js';
|
import * as Cartographic from './cartographic.js';
|
||||||
import * as Ellipsoid from './ellipsoid.js';
|
import * as Ellipsoid from './ellipsoid.js';
|
||||||
import * as CesiumMath from './math.js';
|
import * as CesiumMath from './math.js';
|
||||||
import { Check } from './check.js';
|
|
||||||
import { DEBUG } from './debug.js';
|
|
||||||
const defined = (value) => value !== null && value !== undefined;
|
const defined = (value) => value !== null && value !== undefined;
|
||||||
function setConstants(ellipsoidGeodesic) {
|
function setConstants(ellipsoidGeodesic) {
|
||||||
const uSquared = ellipsoidGeodesic._uSquared;
|
const uSquared = ellipsoidGeodesic._uSquared;
|
||||||
@@ -230,29 +227,7 @@ function vincentyInverseFormula(
|
|||||||
ellipsoidGeodesic._uSquared = uSquared;
|
ellipsoidGeodesic._uSquared = uSquared;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scratchCart1 = Cartesian3.create();
|
|
||||||
const scratchCart2 = Cartesian3.create();
|
|
||||||
function computeProperties(ellipsoidGeodesic, start, end, ellipsoid) {
|
function computeProperties(ellipsoidGeodesic, start, end, ellipsoid) {
|
||||||
const firstCartesian = Cartesian3.normalize(
|
|
||||||
ellipsoid.cartographicToCartesian(start, scratchCart2),
|
|
||||||
scratchCart1,
|
|
||||||
);
|
|
||||||
const lastCartesian = Cartesian3.normalize(
|
|
||||||
ellipsoid.cartographicToCartesian(end, scratchCart2),
|
|
||||||
scratchCart2,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Check.typeOf.number.greaterThanOrEquals(
|
|
||||||
'value',
|
|
||||||
Math.abs(
|
|
||||||
Math.abs(Cartesian3.angleBetween(firstCartesian, lastCartesian)) -
|
|
||||||
Math.PI,
|
|
||||||
),
|
|
||||||
0.0125,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
vincentyInverseFormula(
|
vincentyInverseFormula(
|
||||||
ellipsoidGeodesic,
|
ellipsoidGeodesic,
|
||||||
ellipsoid.maximumRadius,
|
ellipsoid.maximumRadius,
|
||||||
@@ -322,10 +297,6 @@ Object.defineProperties(EllipsoidGeodesic.prototype, {
|
|||||||
*/
|
*/
|
||||||
surfaceDistance: {
|
surfaceDistance: {
|
||||||
get: function () {
|
get: function () {
|
||||||
if (DEBUG) {
|
|
||||||
Check.defined('distance', this._distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._distance;
|
return this._distance;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -362,10 +333,6 @@ Object.defineProperties(EllipsoidGeodesic.prototype, {
|
|||||||
*/
|
*/
|
||||||
startHeading: {
|
startHeading: {
|
||||||
get: function () {
|
get: function () {
|
||||||
if (DEBUG) {
|
|
||||||
Check.defined('distance', this._distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._startHeading;
|
return this._startHeading;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -378,10 +345,6 @@ Object.defineProperties(EllipsoidGeodesic.prototype, {
|
|||||||
*/
|
*/
|
||||||
endHeading: {
|
endHeading: {
|
||||||
get: function () {
|
get: function () {
|
||||||
if (DEBUG) {
|
|
||||||
Check.defined('distance', this._distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._endHeading;
|
return this._endHeading;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -394,11 +357,6 @@ Object.defineProperties(EllipsoidGeodesic.prototype, {
|
|||||||
* @param {Cartographic} end The final planetodetic point on the path.
|
* @param {Cartographic} end The final planetodetic point on the path.
|
||||||
*/
|
*/
|
||||||
EllipsoidGeodesic.prototype.setEndPoints = function (start, end) {
|
EllipsoidGeodesic.prototype.setEndPoints = function (start, end) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.defined('start', start);
|
|
||||||
Check.defined('end', end);
|
|
||||||
}
|
|
||||||
|
|
||||||
computeProperties(this, start, end, this._ellipsoid);
|
computeProperties(this, start, end, this._ellipsoid);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -425,17 +383,11 @@ EllipsoidGeodesic.prototype.interpolateUsingFraction = function (
|
|||||||
* @param {number} distance The distance from the initial point to the point of interest along the geodesic
|
* @param {number} distance The distance from the initial point to the point of interest along the geodesic
|
||||||
* @param {Cartographic} [result] The object in which to store the result.
|
* @param {Cartographic} [result] The object in which to store the result.
|
||||||
* @returns {Cartographic} The location of the point along the geodesic.
|
* @returns {Cartographic} The location of the point along the geodesic.
|
||||||
*
|
|
||||||
* @exception {DeveloperError} start and end must be set before calling function interpolateUsingSurfaceDistance
|
|
||||||
*/
|
*/
|
||||||
EllipsoidGeodesic.prototype.interpolateUsingSurfaceDistance = function (
|
EllipsoidGeodesic.prototype.interpolateUsingSurfaceDistance = function (
|
||||||
distance,
|
distance,
|
||||||
result,
|
result,
|
||||||
) {
|
) {
|
||||||
if (DEBUG) {
|
|
||||||
Check.defined('distance', this._distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
const constants = this._constants;
|
const constants = this._constants;
|
||||||
|
|
||||||
const s = constants.distanceRatio + distance / constants.b;
|
const s = constants.distanceRatio + distance / constants.b;
|
||||||
|
|||||||
@@ -66,17 +66,3 @@ test('endpoint reset, result reuse, custom sphere and dateline/polar paths retai
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
test('unset and near-antipodal geodesics reject the same debug inputs', () => {
|
|
||||||
assert.throws(() => geo.ellipsoidGeodesic.create().surfaceDistance, {
|
|
||||||
name: 'DeveloperError',
|
|
||||||
});
|
|
||||||
const a = geo.cartographic.fromDegrees(0, 0),
|
|
||||||
b = geo.cartographic.fromDegrees(180, 0);
|
|
||||||
assert.throws(() => geo.ellipsoidGeodesic.create(a, b), {
|
|
||||||
name: 'DeveloperError',
|
|
||||||
});
|
|
||||||
assert.throws(
|
|
||||||
() => geo.ellipsoidGeodesic.create().setEndPoints(undefined, b),
|
|
||||||
{ name: 'DeveloperError' },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
Loaded 100 of 272 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user