mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
Move scene effects and imported geometry behind the globe contract
Move Cesium viewer, map, sky, projection, trail and sonar internals into its engine. Drive styles, bloom, sharpening and contact sonar through engine.render. Count camera-input suspensions across all three engines and migrate the feature callers. Preserve Cesium's effects and input behaviour; fix transit lens clicks on Cesium and make zero sharpening on Navara disable sharpening, as it does on Cesium. Parse imported files into plain records and draw them through movers, lines, ground and imagery on both engines. Follow Cesium's KML style precedence, preserve local KMZ images and frame the drawable records. Open overlays use imagery when the engine has no native map-overlay API. Run every imported-display case on fake and headless Cesium engines. The shared import path retains these known differences from Cesium's old DataSources: wall fills, extruded prisms and gx:LatLonQuad are undrawn; raised polygon fills drape while outlines retain altitude; GroundOverlay rotation, altitude and RGB tint are not applied; rhumb lines become great circles. Ground heights use the loaded surface synchronously instead of waiting for most-detailed sampling. Supported KMZ ground overlays now draw on Navara too. Document the limits rather than claiming full parity. Combine batches B and C because their disjoint path manifests cannot place the shared boundary and format files in a later commit while keeping B independently valid. This combined commit owns the shared docs, package boundaries, format scope, locale catalogs and coupling baseline. Remove five obsolete import strings from all eleven catalogs. Make the load-time source scan include new files and skip deleted paths so the same checks work before and after recording the moves. Coupling: Navara renderer lines fall from 1695 to 1294, Cesium import files from 25 to 9, viewer lines from 1177 to 1026, SDK loads from 4 to 3 and engine-name lines from 62 to 42. No count grows. The full unit suite and the boundary, format and coupling checks pass. Browser validation remains to be run. Browser checks to run GOOGLE_MAPS_API_KEY= CESIUM_ION_TOKEN= npm run dev -- --port 4291 node scripts/qa-contract-suite.mjs http://localhost:4291 node scripts/qa-navara-visual-styles.mjs http://localhost:4291 node scripts/qa-navara-bloom.mjs http://localhost:4291 node scripts/qa-navara-sonar.mjs http://localhost:4291 QA_BASE_URL=http://localhost:4291 node scripts/qa-visual-effects.mjs QA_BASE_URL=http://localhost:4291 node scripts/qa-zoom-out-guard.mjs node scripts/qa-sky-model-lighting.mjs --url http://localhost:4291 node scripts/qa-geo-import.mjs http://localhost:4291 node scripts/qa-geo-import-regressions.mjs http://localhost:4291 node scripts/qa-geo-import-large.mjs http://localhost:4291 node scripts/qa-navara-geo-import.mjs http://localhost:4291 Use the keyless server for every check. Photoreal-only cases remain unverified; do not enable paid tiles to run them.
This commit is contained in:
1 parent
c079b329d0
commit
97c4dc76ce
130 files changed
+4763
-3495
No files matched your search
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// node scripts/check-cesium-load-time.mjs [--json]
|
||||
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { parse } from '@babel/parser';
|
||||
|
||||
@@ -17,7 +17,7 @@ const root = new URL('..', import.meta.url);
|
||||
|
||||
/** Files under src/ that the app ships (tests and test support excluded). */
|
||||
export function sourceFiles() {
|
||||
return execFileSync('git', ['ls-files', 'src'], {
|
||||
return execFileSync('git', ['ls-files', '--cached', '--others', '--exclude-standard', 'src'], {
|
||||
cwd: root,
|
||||
encoding: 'utf8',
|
||||
})
|
||||
@@ -27,7 +27,8 @@ export function sourceFiles() {
|
||||
/\.(?:js|mjs)$/.test(path) &&
|
||||
!/\.test\.m?js$/.test(path) &&
|
||||
!path.startsWith('src/testSupport/') &&
|
||||
!path.includes('/testing/'),
|
||||
!path.includes('/testing/') &&
|
||||
existsSync(new URL(path, root)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"metrics": {
|
||||
"cesiumLayerRendererLines": 344,
|
||||
"navaraLayerRendererLines": 1695,
|
||||
"cesiumImportFiles": 25,
|
||||
"viewerLines": 1177,
|
||||
"navaraLayerRendererLines": 1294,
|
||||
"cesiumImportFiles": 9,
|
||||
"viewerLines": 1026,
|
||||
"navaraKindChecks": 2,
|
||||
"engineSniffCalls": 0,
|
||||
"engineExtensionReads": 9,
|
||||
"groundEffectPasses": 1,
|
||||
"cesiumSdkLoads": 4,
|
||||
"cesiumSdkLoads": 3,
|
||||
"screenSpaceHandlerFiles": 4,
|
||||
"engineNameLines": 62
|
||||
"engineNameLines": 42
|
||||
},
|
||||
"measuredAt": "Remove weather and wind fallback imagery; retain the explicit Cesium wind flow backend seam"
|
||||
"measuredAt": "Move Cesium internals into the engine and draw imported records through shared primitives"
|
||||
}
|
||||
+28
-13
@@ -56,11 +56,11 @@
|
||||
"src/app/application.test.mjs",
|
||||
"src/app/mapLifecycle.test.mjs",
|
||||
"src/app/startupCamera.test.mjs",
|
||||
"src/app/viewer.js",
|
||||
"src/app/zoomOutGuard.test.mjs",
|
||||
"src/globe/cesium/viewer.js",
|
||||
"src/globe/cesium/zoomOutGuard.test.mjs",
|
||||
"src/app/controls.test.mjs",
|
||||
"scripts/qa-zoom-out-guard.mjs",
|
||||
"src/app/atmosphereCompat.test.mjs",
|
||||
"src/globe/cesium/atmosphereCompat.test.mjs",
|
||||
"src/standalone/application.js",
|
||||
"src/standalone/controls.js",
|
||||
"src/standalone/data.js",
|
||||
@@ -642,9 +642,9 @@
|
||||
"src/maps/credits.js",
|
||||
"src/maps/defaultSources.js",
|
||||
"src/maps/google3d.js",
|
||||
"src/maps/imagery.js",
|
||||
"src/globe/cesium/mapImagery.js",
|
||||
"src/maps/registry.js",
|
||||
"src/maps/terrain.js",
|
||||
"src/globe/cesium/terrain.js",
|
||||
"src/maps/terrainRetry.js",
|
||||
"src/maps/terrainRetry.test.mjs",
|
||||
"src/mapStackController.js",
|
||||
@@ -1100,7 +1100,7 @@
|
||||
"scripts/build-bright-stars.mjs",
|
||||
"scripts/build-milky-way.mjs",
|
||||
"src/sky/stars.test.mjs",
|
||||
"src/sky/starrySky.test.mjs",
|
||||
"src/globe/cesium/starrySky.test.mjs",
|
||||
"src/tooling/skyAssets.test.mjs",
|
||||
"src/tooling/skyAssets.paintOut.test.mjs",
|
||||
"scripts/qa-anime-sky.mjs",
|
||||
@@ -1240,8 +1240,8 @@
|
||||
"scripts/fixtures/w2/cctv.mjs",
|
||||
"src/layers/radio/screenClusters.js",
|
||||
"src/layers/radio/screenClusters.test.mjs",
|
||||
"src/maps/sceneApply.js",
|
||||
"src/maps/sceneApply.test.mjs",
|
||||
"src/globe/cesium/mapScene.js",
|
||||
"src/globe/cesium/mapScene.test.mjs",
|
||||
"src/globe/contract.test.mjs",
|
||||
"src/globe/testing/fakeEngine.test.mjs",
|
||||
"src/globe/cesium/events.test.mjs",
|
||||
@@ -1365,7 +1365,7 @@
|
||||
"src/bench/w1Feeds.js",
|
||||
"src/bench/satelliteFixture.js",
|
||||
"src/bench/satelliteFixture.test.mjs",
|
||||
"src/celestialRing/render/cesium.js",
|
||||
"src/globe/cesium/celestial.js",
|
||||
"src/celestialRing/view.js",
|
||||
"src/geo/ellipsoidGeodesic.test.mjs",
|
||||
"src/bench/launchFixture.js",
|
||||
@@ -1374,12 +1374,12 @@
|
||||
"src/layers/launches/launchPadFrame.test.mjs",
|
||||
"src/layers/satellites/interaction.test.mjs",
|
||||
"src/layers/launches/lifecycleSnapshot.test.mjs",
|
||||
"src/data/render/cesiumProjection.js",
|
||||
"src/data/render/cesiumTrailRenderer.js",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/globe/cesium/trail.js",
|
||||
"src/layers/flights/trackingRefresh.test.mjs",
|
||||
"scripts/qa-heading-navara.mjs",
|
||||
"src/layers/aircraft/qaDrawn.test.mjs",
|
||||
"src/data/render/cesiumConstants.js",
|
||||
"src/globe/cesium/screenEvents.js",
|
||||
"src/globe/navara/movers/frameCamera.js",
|
||||
"src/globe/navara/overlayPass.js",
|
||||
"src/globe/navara/overlayPass.test.mjs",
|
||||
@@ -1658,5 +1658,20 @@
|
||||
"src/globe/navara/ground/corridors.js",
|
||||
"scripts/qa-ground-looks.mjs",
|
||||
"src/layers/wind/render/cesiumFlow.js",
|
||||
"src/testSupport/imageryDisplayEngines.mjs"
|
||||
"src/testSupport/imageryDisplayEngines.mjs",
|
||||
"src/data/geoImportDisplay.js",
|
||||
"src/data/geoImportDisplay.test.mjs",
|
||||
"src/data/geoImportKml.js",
|
||||
"src/data/geoImportKml.test.mjs",
|
||||
"src/data/geoImportRecords.js",
|
||||
"src/globe/cesium/atmosphereCompat.js",
|
||||
"src/globe/cesium/postStages.js",
|
||||
"src/globe/cesium/postStages.test.mjs",
|
||||
"src/globe/cesium/sonar.js",
|
||||
"src/globe/cesium/sonar.test.mjs",
|
||||
"src/globe/cesium/sonarCommands.js",
|
||||
"src/globe/cesium/sonarCommands.test.mjs",
|
||||
"src/globe/cesium/starrySky.js",
|
||||
"src/globe/cesium/zoomOutGuard.js",
|
||||
"scripts/qa-geo-import-probe.mjs"
|
||||
]
|
||||
@@ -167,15 +167,16 @@
|
||||
"viewer": {
|
||||
"exports": ["./application/viewer"],
|
||||
"modules": [
|
||||
"src/app/viewer.js",
|
||||
"src/app/atmosphereCompat.js",
|
||||
"src/globe/cesium/viewer.js",
|
||||
"src/globe/cesium/atmosphereCompat.js",
|
||||
"src/globeInView.js",
|
||||
"src/geo/",
|
||||
"src/sky/starrySky.js",
|
||||
"src/globe/cesium/starrySky.js",
|
||||
"src/sky/stars.js",
|
||||
"src/data/bundledJson.js",
|
||||
"src/renderGovernor.js",
|
||||
"src/globe/contract.js"
|
||||
"src/globe/contract.js",
|
||||
"src/maps/google3d.js"
|
||||
],
|
||||
"external": ["cesium"]
|
||||
},
|
||||
@@ -1419,8 +1420,8 @@
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/ui/unitLabels.js",
|
||||
"src/data/render/cesiumProjection.js",
|
||||
"src/data/render/cesiumConstants.js",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/globe/cesium/screenEvents.js",
|
||||
"src/geo/",
|
||||
"src/time/clock.js",
|
||||
"src/globe/contract.js",
|
||||
@@ -1486,8 +1487,8 @@
|
||||
"src/i18n/locales/uk.json",
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/data/render/cesiumProjection.js",
|
||||
"src/data/render/cesiumConstants.js",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/globe/cesium/screenEvents.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/geo/",
|
||||
"src/time/clock.js",
|
||||
@@ -1525,8 +1526,8 @@
|
||||
"src/layers/aircraft/classification.js",
|
||||
"src/layers/aircraft/index.js",
|
||||
"src/data/inputOwnership.js",
|
||||
"src/data/render/cesiumProjection.js",
|
||||
"src/data/render/cesiumConstants.js",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/globe/cesium/screenEvents.js",
|
||||
"src/geo/",
|
||||
"src/globe/contract.js",
|
||||
"src/globe/cesium/engineFromViewer.js"
|
||||
@@ -1573,7 +1574,7 @@
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/layers/vessels/icons.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/data/render/cesiumProjection.js",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/geo/",
|
||||
"src/globe/contract.js",
|
||||
"src/globe/cesium/engineFromViewer.js"
|
||||
@@ -1847,7 +1848,7 @@
|
||||
"src/data/installationFeedback.js",
|
||||
"src/ui/layerNames.js",
|
||||
"src/ui/unitLabels.js",
|
||||
"src/data/render/cesiumProjection.js",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/geo/",
|
||||
"src/globe/contract.js",
|
||||
"src/globe/cesium/engineFromViewer.js"
|
||||
@@ -2247,10 +2248,10 @@
|
||||
"src/maps/credits.js",
|
||||
"src/maps/defaultSources.js",
|
||||
"src/maps/google3d.js",
|
||||
"src/maps/imagery.js",
|
||||
"src/globe/cesium/mapImagery.js",
|
||||
"src/maps/sentinel2.js",
|
||||
"src/maps/registry.js",
|
||||
"src/maps/terrain.js",
|
||||
"src/globe/cesium/terrain.js",
|
||||
"src/maps/terrainRetry.js",
|
||||
"src/keySetupCore.mjs",
|
||||
"src/localRequestGate.mjs",
|
||||
@@ -2269,7 +2270,7 @@
|
||||
"src/i18n/locales/zh-CN.json",
|
||||
"src/i18n/locales/zh-Hant.json",
|
||||
"src/globe/cesium/credits.js",
|
||||
"src/maps/sceneApply.js"
|
||||
"src/globe/cesium/mapScene.js"
|
||||
],
|
||||
"external": ["cesium"]
|
||||
},
|
||||
@@ -2389,14 +2390,14 @@
|
||||
"src/data/headingDegrees.js",
|
||||
"src/data/distanceFormat.js",
|
||||
"src/ui/placeNames.js",
|
||||
"src/cyberSonarScene.js",
|
||||
"src/globe/cesium/sonar.js",
|
||||
"src/cyberSonarEngine.js",
|
||||
"src/cyberSonar.js",
|
||||
"src/awarenessEcho.js",
|
||||
"src/hudLayouts.js",
|
||||
"src/hudLayoutPolicy.js",
|
||||
"src/ui/radioPanelPlacement.js",
|
||||
"src/cyberSonarGpu.js",
|
||||
"src/globe/cesium/sonarCommands.js",
|
||||
"src/scenes/nepalEvidencePack.js",
|
||||
"src/data/approxDistance.js",
|
||||
"src/data/bhoteKoshiEvent.js",
|
||||
@@ -2442,7 +2443,7 @@
|
||||
"src/app/operations.js",
|
||||
"src/app/scene.js",
|
||||
"src/app/loadCesium.js",
|
||||
"src/app/zoomOutGuard.js",
|
||||
"src/globe/cesium/zoomOutGuard.js",
|
||||
"src/sources/sourceSlot.js",
|
||||
"src/app/sources.js",
|
||||
"src/app/startupChrome.js",
|
||||
@@ -2453,10 +2454,10 @@
|
||||
"src/app/sessionVoice.js",
|
||||
"src/app/sceneParts.js",
|
||||
"src/app/shellPart.js",
|
||||
"src/app/viewer.js",
|
||||
"src/sky/starrySky.js",
|
||||
"src/globe/cesium/viewer.js",
|
||||
"src/globe/cesium/starrySky.js",
|
||||
"src/sky/stars.js",
|
||||
"src/app/atmosphereCompat.js",
|
||||
"src/globe/cesium/atmosphereCompat.js",
|
||||
"src/bloom.js",
|
||||
"src/camera.js",
|
||||
"src/cameraGroundGuard.js",
|
||||
@@ -2828,10 +2829,10 @@
|
||||
"src/maps/credits.js",
|
||||
"src/maps/defaultSources.js",
|
||||
"src/maps/google3d.js",
|
||||
"src/maps/imagery.js",
|
||||
"src/globe/cesium/mapImagery.js",
|
||||
"src/maps/sentinel2.js",
|
||||
"src/maps/registry.js",
|
||||
"src/maps/terrain.js",
|
||||
"src/globe/cesium/terrain.js",
|
||||
"src/maps/terrainRetry.js",
|
||||
"src/navigationPolicy.js",
|
||||
"src/orbit.js",
|
||||
@@ -2880,7 +2881,6 @@
|
||||
"src/styles/thermal.js",
|
||||
"src/ui/applicationShell.js",
|
||||
"src/ui/mobileShell.js",
|
||||
"src/exclusiveSurfaces.js",
|
||||
"src/ui/localePicker.js",
|
||||
"src/ui/cyberSonarControls.js",
|
||||
"src/ui/locationNavigation.js",
|
||||
@@ -3326,7 +3326,6 @@
|
||||
"src/ui/stores/cockpit.js",
|
||||
"src/ui/stores/locale.js",
|
||||
"src/ui/stores/providerSettings.js",
|
||||
"src/ui/stores/sceneSharing.js",
|
||||
"src/ui/stores/voice.js",
|
||||
"src/ui/stores/location.js",
|
||||
"src/ui/stores/display.js",
|
||||
@@ -3401,15 +3400,15 @@
|
||||
"src/app/navaraMode.js",
|
||||
"src/annotations/navaraAnnotationRenderer.js",
|
||||
"src/annotations/drawInputNavara.js",
|
||||
"src/data/render/cesiumProjection.js",
|
||||
"src/data/render/cesiumConstants.js",
|
||||
"src/data/render/cesiumTrailRenderer.js",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/globe/cesium/screenEvents.js",
|
||||
"src/globe/cesium/trail.js",
|
||||
"src/layers/alpr/overlay.js",
|
||||
"src/layers/alpr/presentation.js",
|
||||
"src/layers/bikeshare/selectionModel.js",
|
||||
"src/layers/bikeshare/presentation.js",
|
||||
"src/layers/vessels/icons.js",
|
||||
"src/maps/sceneApply.js",
|
||||
"src/globe/cesium/mapScene.js",
|
||||
"src/layers/localAdsb/qaDrawn.js",
|
||||
"src/geo/",
|
||||
"src/time/clock.js",
|
||||
@@ -3453,7 +3452,7 @@
|
||||
"src/layers/aircraft/qaDrawn.js",
|
||||
"src/layers/localAdsb/render/display.js",
|
||||
"src/layers/shared/render/engineView.js",
|
||||
"src/celestialRing/render/cesium.js",
|
||||
"src/globe/cesium/celestial.js",
|
||||
"src/celestialRing/view.js",
|
||||
"src/layers/shared/render/navaraPolygons.js",
|
||||
"src/layers/awareness/render/navaraPulses.js",
|
||||
@@ -3471,8 +3470,6 @@
|
||||
"src/layers/shared/render/navaraMoverBank.js",
|
||||
"src/layers/shared/render/moverBank.js",
|
||||
"src/layers/cctv/render/navaraMeshes.js",
|
||||
"src/layers/alpr/overlay.js",
|
||||
"src/layers/alpr/presentation.js",
|
||||
"src/layers/radio/screenClusters.js",
|
||||
"src/layers/shared/render/imageAtlas.js",
|
||||
"src/layers/shared/render/navaraGroundSurface.js",
|
||||
@@ -3578,7 +3575,11 @@
|
||||
"src/globe/cesium/imagery.js",
|
||||
"src/globe/cesium/imageryShell.js",
|
||||
"src/globe/navara/ground/corridors.js",
|
||||
"src/layers/wind/render/cesiumFlow.js"
|
||||
"src/layers/wind/render/cesiumFlow.js",
|
||||
"src/globe/cesium/postStages.js",
|
||||
"src/data/geoImportRecords.js",
|
||||
"src/data/geoImportKml.js",
|
||||
"src/data/geoImportDisplay.js"
|
||||
],
|
||||
"external": [
|
||||
"hls.js",
|
||||
@@ -4190,12 +4191,12 @@
|
||||
"src/globe/cesium/engineFromViewer.js",
|
||||
"src/globe/cesium/createCesiumEngine.js",
|
||||
"src/globe/cesium/maps.js",
|
||||
"src/maps/terrain.js",
|
||||
"src/globe/cesium/terrain.js",
|
||||
"src/maps/terrainRetry.js",
|
||||
"src/maps/sceneApply.js",
|
||||
"src/globe/cesium/mapScene.js",
|
||||
"src/maps/credits.js",
|
||||
"src/globe/cesium/surface.js",
|
||||
"src/maps/imagery.js",
|
||||
"src/globe/cesium/mapImagery.js",
|
||||
"src/maps/sentinel2.js",
|
||||
"src/services/groundSnap.js",
|
||||
"src/maps/google3d.js",
|
||||
@@ -4210,9 +4211,9 @@
|
||||
"src/globe/cesium/projector.js",
|
||||
"src/globe/cesium/credits.js",
|
||||
"src/globe/contract.js",
|
||||
"src/data/render/cesiumProjection.js",
|
||||
"src/data/render/cesiumConstants.js",
|
||||
"src/data/render/cesiumTrailRenderer.js",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/globe/cesium/screenEvents.js",
|
||||
"src/globe/cesium/trail.js",
|
||||
"src/layers/alpr/overlay.js",
|
||||
"src/layers/alpr/presentation.js",
|
||||
"src/layers/bikeshare/selectionModel.js",
|
||||
@@ -4235,7 +4236,7 @@
|
||||
"src/layers/aircraft/qaDrawn.js",
|
||||
"src/layers/localAdsb/render/display.js",
|
||||
"src/layers/shared/render/moverBank.js",
|
||||
"src/celestialRing/render/cesium.js",
|
||||
"src/globe/cesium/celestial.js",
|
||||
"src/celestialRing/view.js",
|
||||
"src/layers/shared/render/engineView.js",
|
||||
"src/layers/firms/rectangle.js",
|
||||
@@ -4326,7 +4327,18 @@
|
||||
"src/layers/wind/render/flowMesh.js",
|
||||
"src/layers/shared/render/navaraGroundEffect.js",
|
||||
"src/layers/awareness/render/navaraPulses.js",
|
||||
"src/layers/wind/render/cesiumFlow.js"
|
||||
"src/layers/wind/render/cesiumFlow.js",
|
||||
"src/globe/cesium/postStages.js",
|
||||
"src/globe/cesium/sonar.js",
|
||||
"src/globe/cesium/sonarCommands.js",
|
||||
"src/styles/retro.js",
|
||||
"src/styles/surveillance.js",
|
||||
"src/styles/thermal.js",
|
||||
"src/styles/anime.js",
|
||||
"src/styles/noir.js",
|
||||
"src/data/geoImportRecords.js",
|
||||
"src/data/geoImportKml.js",
|
||||
"src/data/geoImportDisplay.js"
|
||||
],
|
||||
"external": [
|
||||
"hls.js",
|
||||
@@ -5054,13 +5066,13 @@
|
||||
"src/globe/cesium/input.js",
|
||||
"src/maps/google3d.js",
|
||||
"src/services/groundSnap.js",
|
||||
"src/maps/imagery.js",
|
||||
"src/globe/cesium/mapImagery.js",
|
||||
"src/maps/sentinel2.js",
|
||||
"src/globe/cesium/surface.js",
|
||||
"src/maps/credits.js",
|
||||
"src/maps/sceneApply.js",
|
||||
"src/globe/cesium/mapScene.js",
|
||||
"src/maps/terrainRetry.js",
|
||||
"src/maps/terrain.js",
|
||||
"src/globe/cesium/terrain.js",
|
||||
"src/globe/cesium/maps.js",
|
||||
"src/globe/cesium/createCesiumEngine.js",
|
||||
"src/globe/cesium/engineFromViewer.js",
|
||||
@@ -5070,17 +5082,25 @@
|
||||
"src/globe/contract.js",
|
||||
"src/celestialRing/view.js",
|
||||
"src/layers/shared/render/engineView.js",
|
||||
"src/data/render/cesiumProjection.js",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/layers/shared/color.js",
|
||||
"src/geo/",
|
||||
"src/time/clock.js",
|
||||
"src/globe/surfaceDetail.js",
|
||||
"src/celestialRing/render/cesium.js",
|
||||
"src/globe/cesium/celestial.js",
|
||||
"src/overlays/worldOverlayChrome.js",
|
||||
"src/layers/shared/render/moverBank.js",
|
||||
"src/layers/shared/render/imageAtlas.js",
|
||||
"src/layers/transit/render/style.js",
|
||||
"src/layers/transit/render/display.js"
|
||||
"src/layers/transit/render/display.js",
|
||||
"src/globe/cesium/postStages.js",
|
||||
"src/globe/cesium/sonar.js",
|
||||
"src/globe/cesium/sonarCommands.js",
|
||||
"src/styles/retro.js",
|
||||
"src/styles/surveillance.js",
|
||||
"src/styles/thermal.js",
|
||||
"src/styles/anime.js",
|
||||
"src/styles/noir.js"
|
||||
],
|
||||
"external": ["cesium", "egm96-universal", "pbf"]
|
||||
},
|
||||
@@ -5310,7 +5330,10 @@
|
||||
"src/data/geoImportImages.js",
|
||||
"src/data/geoImportPins.js",
|
||||
"src/layers/streetLevel/tileMath.js",
|
||||
"src/layers/streetLevel/view.js"
|
||||
"src/layers/streetLevel/view.js",
|
||||
"src/data/geoImportRecords.js",
|
||||
"src/data/geoImportKml.js",
|
||||
"src/data/geoImportDisplay.js"
|
||||
],
|
||||
"external": ["cesium"]
|
||||
},
|
||||
@@ -5635,7 +5658,7 @@
|
||||
"src/layers/wildlifeSightings/sprites.js",
|
||||
"src/layers/wildlifeSightings/photoText.js",
|
||||
"src/layers/wildlifeSightings/performance.js",
|
||||
"src/data/render/cesiumProjection.js",
|
||||
"src/globe/cesium/sceneProjection.js",
|
||||
"src/geo/",
|
||||
"src/globe/contract.js",
|
||||
"src/globe/cesium/engineFromViewer.js",
|
||||
|
||||
+124
-63
@@ -7,9 +7,13 @@ import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import puppeteer from 'puppeteer';
|
||||
import { installImportProbe } from './qa-geo-import-probe.mjs';
|
||||
import { hookRenderErrors, readRenderErrors } from './qa-browserEvidence.mjs';
|
||||
|
||||
const base = process.env.QA_BASE_URL || 'http://localhost:4173';
|
||||
const base =
|
||||
process.argv.find((arg) => /^https?:/.test(arg)) ||
|
||||
process.env.QA_BASE_URL ||
|
||||
'http://localhost:4173';
|
||||
const supplied = process.argv.includes('--file')
|
||||
? path.resolve(process.argv[process.argv.indexOf('--file') + 1])
|
||||
: null;
|
||||
@@ -90,6 +94,7 @@ try {
|
||||
document.getElementById('loading-screen')?.classList.contains('hidden'),
|
||||
{ timeout: 60000 },
|
||||
);
|
||||
await installImportProbe(page);
|
||||
await page.evaluate(async () => {
|
||||
const { viewer, dataManager } = window.__godsEyeView;
|
||||
const C = window.__CESIUM__;
|
||||
@@ -103,49 +108,102 @@ try {
|
||||
destination: C.Cartesian3.fromDegrees(-122.5, 37.72, 50000),
|
||||
orientation: { heading: 0, pitch: -Math.PI / 2, roll: 0 },
|
||||
});
|
||||
const rgba = (color) => [color.red, color.green, color.blue, color.alpha];
|
||||
const xyz = (p) => [p.x, p.y, p.z];
|
||||
const lonLat = (p) => {
|
||||
const c = C.Cartographic.fromCartesian(p);
|
||||
return [C.Math.toDegrees(c.longitude), C.Math.toDegrees(c.latitude)];
|
||||
};
|
||||
const canonical = (rows) =>
|
||||
rows
|
||||
.map((row) =>
|
||||
JSON.stringify(row, (_, value) =>
|
||||
typeof value === 'number'
|
||||
? Math.round(value * 1e5) / 1e5 + 0
|
||||
: value,
|
||||
),
|
||||
)
|
||||
.sort();
|
||||
window.__qaStyleSnapshot = (source) => {
|
||||
const now = viewer.clock.currentTime;
|
||||
const images = new Map();
|
||||
const imageValue = (p) => {
|
||||
const value = p?.getValue(now);
|
||||
if (value?.tagName !== 'CANVAS') return value;
|
||||
if (!images.has(value)) images.set(value, value.toDataURL());
|
||||
return images.get(value);
|
||||
};
|
||||
const color = (p) => p?.getValue(now)?.toCssColorString();
|
||||
const positions = (p) => p?.getValue(now)?.map((v) => [v.x, v.y, v.z]);
|
||||
const renderedPins = new Map();
|
||||
const collection = source.importedBillboards;
|
||||
if (collection)
|
||||
for (let i = 0; i < collection.length; i++) {
|
||||
const pin = collection.get(i);
|
||||
renderedPins.set(pin.id, pin);
|
||||
const value = (property) => property?.getValue(now);
|
||||
const rows = [];
|
||||
for (const e of source.entities.values) {
|
||||
if (e.billboard && e.position) {
|
||||
const b = e.billboard,
|
||||
image = value(b.image);
|
||||
const scale = value(b.scale) ?? 1;
|
||||
const width = (value(b.width) ?? image.width) * scale;
|
||||
const height = (value(b.height) ?? image.height) * scale;
|
||||
const offset = value(b.pixelOffset) ?? { x: 0, y: 0 };
|
||||
const centerY =
|
||||
offset.y -
|
||||
(value(b.verticalOrigin) === C.VerticalOrigin.BOTTOM
|
||||
? height / 2
|
||||
: 0);
|
||||
rows.push({
|
||||
kind: 'pin',
|
||||
position: xyz(value(e.position)),
|
||||
size:
|
||||
2 *
|
||||
Math.max(
|
||||
Math.abs(offset.x) + width / 2,
|
||||
Math.abs(centerY) + height / 2,
|
||||
0.5,
|
||||
),
|
||||
rotation: value(b.rotation) ?? 0,
|
||||
color: rgba(value(b.color) ?? C.Color.WHITE),
|
||||
image: Boolean(image),
|
||||
});
|
||||
}
|
||||
const actual = (e, key, fallback) => {
|
||||
const pin = renderedPins.get(e);
|
||||
return pin ? pin[key] : (e.billboard?.[key]?.getValue(now) ?? fallback);
|
||||
};
|
||||
return (source.importedEntities || source.entities.values)
|
||||
.map((e) => ({
|
||||
name: e.name,
|
||||
point: renderedPins.get(e)?.position || e.position?.getValue(now),
|
||||
scale: e.billboard ? actual(e, 'scale', 1) : undefined,
|
||||
rotation: e.billboard ? actual(e, 'rotation', 0) : undefined,
|
||||
image: renderedPins.get(e)?.image || imageValue(e.billboard?.image),
|
||||
color: e.billboard
|
||||
? actual(e, 'color', C.Color.WHITE).toCssColorString()
|
||||
: undefined,
|
||||
width: e.polyline?.width?.getValue(now),
|
||||
lineColor: color(e.polyline?.material?.color),
|
||||
line: positions(e.polyline?.positions),
|
||||
fill: e.polygon?.fill?.getValue(now),
|
||||
outline: e.polygon?.outline?.getValue(now),
|
||||
polygonColor: color(e.polygon?.material?.color),
|
||||
polygon: e.polygon?.hierarchy
|
||||
?.getValue(now)
|
||||
?.positions?.map((v) => [v.x, v.y, v.z]),
|
||||
}))
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
if (e.polyline)
|
||||
rows.push({
|
||||
kind: 'line',
|
||||
positions: value(e.polyline.positions).map(lonLat),
|
||||
width: value(e.polyline.width),
|
||||
color: rgba(value(e.polyline.material.color)),
|
||||
});
|
||||
if (e.polygon && value(e.polygon.fill) !== false) {
|
||||
const hierarchy = value(e.polygon.hierarchy);
|
||||
rows.push({
|
||||
kind: 'polygon',
|
||||
rings: [
|
||||
hierarchy.positions,
|
||||
...hierarchy.holes.map((hole) => hole.positions),
|
||||
].map((ring) => ring.map(lonLat)),
|
||||
fill: rgba(value(e.polygon.material.color)),
|
||||
});
|
||||
}
|
||||
}
|
||||
return canonical(rows);
|
||||
};
|
||||
window.__qaDrawnSnapshot = () => {
|
||||
const rows = [];
|
||||
for (const r of window.__qaImports.records()) {
|
||||
if (r.kind === 'movers')
|
||||
for (const item of r.items)
|
||||
rows.push({
|
||||
kind: 'pin',
|
||||
position: xyz(item.position),
|
||||
size: r.options.sprite.sizePx,
|
||||
rotation: item.rotation,
|
||||
color: item.color,
|
||||
image: r.options.sprite.atlas.canvas.width > 0,
|
||||
});
|
||||
if (r.kind === 'ground')
|
||||
for (const item of r.items)
|
||||
rows.push(
|
||||
r.options.kind === 'corridor'
|
||||
? {
|
||||
kind: 'line',
|
||||
positions: item.positions,
|
||||
width: item.widthPx,
|
||||
color: item.color,
|
||||
}
|
||||
: { kind: 'polygon', rings: item.rings, fill: item.fill },
|
||||
);
|
||||
}
|
||||
return canonical(rows);
|
||||
};
|
||||
window.__qaUnsharedKml = async (text) => {
|
||||
const doc = new DOMParser().parseFromString(text, 'application/xml');
|
||||
@@ -170,14 +228,15 @@ try {
|
||||
const outcomes = await window.__gevGeoImport.importFiles([
|
||||
new File([text], 'style-equivalence.kml'),
|
||||
]);
|
||||
const candidate = viewer.dataSources.get(viewer.dataSources.length - 1);
|
||||
const candidate = window.__gevGeoImport
|
||||
.list()
|
||||
.find((file) => file.id === outcomes[0]?.id);
|
||||
const result = {
|
||||
ok: outcomes[0]?.ok,
|
||||
same:
|
||||
JSON.stringify(window.__qaStyleSnapshot(control)) ===
|
||||
JSON.stringify(window.__qaStyleSnapshot(candidate)),
|
||||
entities: (candidate.importedEntities || candidate.entities.values)
|
||||
.length,
|
||||
JSON.stringify(window.__qaDrawnSnapshot()),
|
||||
entities: candidate?.entityCount,
|
||||
};
|
||||
await window.__gevGeoImport.remove(outcomes[0]?.id);
|
||||
return result;
|
||||
@@ -251,7 +310,7 @@ try {
|
||||
|
||||
const loadPicked = async (filename, expected) => {
|
||||
const before = await page.evaluate(
|
||||
() => window.__godsEyeView.viewer.dataSources.length,
|
||||
() => window.__qaImports.groups().length,
|
||||
);
|
||||
const priorIds = await page.evaluate(() =>
|
||||
window.__gevGeoImport.list().map((f) => f.id),
|
||||
@@ -276,7 +335,7 @@ try {
|
||||
const state = await page.evaluate(() => ({
|
||||
files: window.__gevGeoImport.list(),
|
||||
hint: document.getElementById('geo-import-hint').textContent,
|
||||
sources: window.__godsEyeView.viewer.dataSources.length,
|
||||
groups: window.__qaImports.groups().length,
|
||||
}));
|
||||
const item = state.files.find((f) => !priorIds.includes(f.id));
|
||||
check(
|
||||
@@ -285,7 +344,7 @@ try {
|
||||
(expected === null
|
||||
? item?.entityCount > 0
|
||||
: item?.entityCount === expected) &&
|
||||
state.sources === before + 1,
|
||||
state.groups === before + 1,
|
||||
JSON.stringify({
|
||||
bytes: fs.statSync(filename).size,
|
||||
loadMs: Math.round(performance.now() - started),
|
||||
@@ -297,10 +356,8 @@ try {
|
||||
const item = await loadPicked(file, 5000);
|
||||
if (item) {
|
||||
const same = await page.evaluate(() => {
|
||||
const viewer = window.__godsEyeView.viewer;
|
||||
const source = viewer.dataSources.get(viewer.dataSources.length - 1);
|
||||
return (
|
||||
JSON.stringify(window.__qaStyleSnapshot(source)) ===
|
||||
JSON.stringify(window.__qaDrawnSnapshot()) ===
|
||||
window.__qaUnsharedSnapshot
|
||||
);
|
||||
});
|
||||
@@ -338,17 +395,15 @@ try {
|
||||
const markers = await page.evaluate(() => {
|
||||
const { viewer } = window.__godsEyeView,
|
||||
C = window.__CESIUM__;
|
||||
const source = viewer.dataSources.get(viewer.dataSources.length - 1);
|
||||
const points = (
|
||||
source.importedEntities || source.entities.values
|
||||
).filter((e) => e.position && e.billboard);
|
||||
const images = points.filter((e) =>
|
||||
e.billboard.image?.getValue(viewer.clock.currentTime),
|
||||
);
|
||||
const sets = window.__qaImports
|
||||
.records()
|
||||
.filter((record) => record.kind === 'movers');
|
||||
const points = sets.flatMap((record) => record.items);
|
||||
const images = sets
|
||||
.filter((record) => record.options.sprite.atlas.canvas.width > 0)
|
||||
.flatMap((record) => record.items);
|
||||
if (images.length) {
|
||||
const at = C.Cartographic.fromCartesian(
|
||||
images[0].position.getValue(viewer.clock.currentTime),
|
||||
);
|
||||
const at = C.Cartographic.fromCartesian(images[0].position);
|
||||
viewer.camera.cancelFlight();
|
||||
viewer.camera.setView({
|
||||
destination: C.Cartesian3.fromRadians(
|
||||
@@ -436,8 +491,14 @@ try {
|
||||
JSON.stringify(errors),
|
||||
);
|
||||
check(
|
||||
'all imported sources are removed',
|
||||
await page.evaluate(() => window.__gevGeoImport.list().length === 0),
|
||||
'all imported files and drawing sets are removed',
|
||||
await page.evaluate(
|
||||
() =>
|
||||
window.__gevGeoImport.list().length === 0 &&
|
||||
Object.values(window.__qaImports.list()).every(
|
||||
(list) => list.length === 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
console.log('Fixture retained at', file);
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/** Record import writes at the globe contract, including per-instance styles. */
|
||||
export async function installImportProbe(page) {
|
||||
await page.evaluate(() => {
|
||||
const engine = window.__godsEyeView.engine;
|
||||
const records = new Map();
|
||||
for (const [kind, method] of [
|
||||
['movers', 'createSet'],
|
||||
['ground', 'createSet'],
|
||||
['lines', 'createLine'],
|
||||
['imagery', 'createLayer'],
|
||||
]) {
|
||||
const create = engine[kind][method].bind(engine[kind]);
|
||||
engine[kind][method] = (options) => {
|
||||
const handle = create(options);
|
||||
if (!options.id.startsWith('geo-import-')) return handle;
|
||||
const record = { kind, options, handle, items: [], positions: [] };
|
||||
records.set(options.id, record);
|
||||
const wrap = (name, after) => {
|
||||
const original = handle[name].bind(handle);
|
||||
handle[name] = (...args) => {
|
||||
const result = original(...args);
|
||||
after(...args);
|
||||
return result;
|
||||
};
|
||||
};
|
||||
if (kind === 'movers')
|
||||
wrap('set', (index, fields) => {
|
||||
record.items[index] = { ...record.items[index], ...fields };
|
||||
});
|
||||
if (kind === 'ground')
|
||||
wrap('replace', (items) => {
|
||||
record.items = items;
|
||||
});
|
||||
if (kind === 'lines')
|
||||
wrap('setPositions', (positions) => {
|
||||
record.positions = positions;
|
||||
});
|
||||
wrap('destroy', () => records.delete(options.id));
|
||||
return handle;
|
||||
};
|
||||
}
|
||||
window.__qaImports = {
|
||||
list: () =>
|
||||
Object.fromEntries(
|
||||
['movers', 'ground', 'lines', 'imagery'].map((kind) => [
|
||||
kind,
|
||||
engine[kind]
|
||||
.list()
|
||||
.filter(({ id }) => id.startsWith('geo-import-')),
|
||||
]),
|
||||
),
|
||||
records: () => [...records.values()],
|
||||
groups: () => {
|
||||
const groups = new Map();
|
||||
for (const record of records.values()) {
|
||||
const id = record.options.id.match(/^geo-import-\d+/)[0];
|
||||
if (!groups.has(id)) groups.set(id, []);
|
||||
groups.get(id).push(record);
|
||||
}
|
||||
return [...groups.values()];
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
/** Import regressions through the real UI controller and Cesium loaders.
|
||||
/** Import regressions through the real UI controller and globe primitives.
|
||||
* Run against dev and preview: QA_BASE_URL=http://localhost:4173 node scripts/qa-geo-import-regressions.mjs
|
||||
*/
|
||||
import puppeteer from 'puppeteer';
|
||||
import * as Cesium from 'cesium';
|
||||
import { installImportProbe } from './qa-geo-import-probe.mjs';
|
||||
import { hookRenderErrors, readRenderErrors } from './qa-browserEvidence.mjs';
|
||||
|
||||
const browser = await puppeteer.launch({ headless: true });
|
||||
@@ -86,7 +87,7 @@ let failed = 0;
|
||||
try {
|
||||
await page.setViewport({ width: 390, height: 844 });
|
||||
await page.goto(
|
||||
`${process.env.QA_BASE_URL || 'http://localhost:4173'}/?welcome=0`,
|
||||
`${process.argv.find((arg) => /^https?:/.test(arg)) || process.env.QA_BASE_URL || 'http://localhost:4173'}/?welcome=0`,
|
||||
{ waitUntil: 'domcontentloaded' },
|
||||
);
|
||||
await page.waitForFunction(
|
||||
@@ -112,8 +113,9 @@ try {
|
||||
page.on('request', (r) => {
|
||||
if (!/^(data:|blob:)/.test(r.url())) requests.push(r.url());
|
||||
});
|
||||
await installImportProbe(page);
|
||||
const results = await page.evaluate(async () => {
|
||||
const { viewer } = window.__godsEyeView,
|
||||
const { viewer, engine } = window.__godsEyeView,
|
||||
C = window.__CESIUM__,
|
||||
tool = window.__gevGeoImport;
|
||||
const results = [],
|
||||
@@ -128,14 +130,27 @@ try {
|
||||
typeof value === 'object' && !ArrayBuffer.isView(value)
|
||||
? JSON.stringify(value)
|
||||
: value;
|
||||
const before = new Set(window.__qaImports.records());
|
||||
const [outcome] = await tool.importFiles([new File([content], name)]);
|
||||
return {
|
||||
outcome,
|
||||
source: outcome.ok
|
||||
? viewer.dataSources.get(viewer.dataSources.length - 1)
|
||||
: null,
|
||||
drawing: window.__qaImports
|
||||
.records()
|
||||
.filter((record) => !before.has(record)),
|
||||
};
|
||||
};
|
||||
const pins = (r) =>
|
||||
r.drawing
|
||||
.filter((record) => record.kind === 'movers')
|
||||
.flatMap((record) => record.items);
|
||||
const ground = (r) =>
|
||||
r.drawing
|
||||
.filter((record) => record.kind === 'ground')
|
||||
.flatMap((record) => record.items);
|
||||
const lines = (r) => r.drawing.filter((record) => record.kind === 'lines');
|
||||
const screenOf = (p) => engine.projector.worldToWindow(p);
|
||||
const inFrame = (p) =>
|
||||
p && p.x > 0 && p.x < innerWidth && p.y > 0 && p.y < innerHeight;
|
||||
const remove = (r) => {
|
||||
if (r.outcome.ok) tool.remove(r.outcome.id);
|
||||
};
|
||||
@@ -171,26 +186,20 @@ try {
|
||||
},
|
||||
'xyz.geojson',
|
||||
);
|
||||
const pin = (r.source?.importedEntities || r.source?.entities.values).find(
|
||||
(e) => e.billboard,
|
||||
),
|
||||
polygon = (r.source?.importedEntities || r.source?.entities.values).find(
|
||||
(e) => e.polygon,
|
||||
);
|
||||
const pin = pins(r)[0],
|
||||
polygon = ground(r)[0];
|
||||
check(
|
||||
'XYZ ground footprint and isolated entity IDs',
|
||||
'XYZ ground footprint and isolated pick IDs',
|
||||
r.outcome.ok &&
|
||||
pin.id !== 'kartaview-owned' &&
|
||||
Math.abs(
|
||||
C.Cartographic.fromCartesian(pin.position.getValue(now)).height,
|
||||
) < 0.01 &&
|
||||
polygon.polygon.perPositionHeight?.getValue(now) !== true,
|
||||
pin.pickId === undefined &&
|
||||
Math.abs(C.Cartographic.fromCartesian(pin.position).height) < 0.01 &&
|
||||
polygon.rings.flat().every((p) => p.length === 2),
|
||||
);
|
||||
check(
|
||||
'ground pins have no persistent clamping callbacks',
|
||||
pin?.billboard.heightReference?.getValue(now) ===
|
||||
C.HeightReference.NONE &&
|
||||
pin.billboard.disableDepthTestDistance.getValue(now) === Infinity,
|
||||
'ground pins use fixed positions and ignore scene depth',
|
||||
typeof pin.position.x === 'number' &&
|
||||
r.drawing.find((record) => record.kind === 'movers').options
|
||||
.depthTest === 'always',
|
||||
);
|
||||
remove(r);
|
||||
r = await load(
|
||||
@@ -201,24 +210,18 @@ try {
|
||||
),
|
||||
'tracks.kml',
|
||||
);
|
||||
const find = (name) =>
|
||||
(r.source?.importedEntities || r.source?.entities.values).find(
|
||||
(e) => e.name === name,
|
||||
);
|
||||
check(
|
||||
'ground tracks and untessellated authored lines clamp',
|
||||
find('ground')?.polyline.clampToGround?.getValue(now) &&
|
||||
find('line')?.polyline.clampToGround?.getValue(now),
|
||||
ground(r).length === 2,
|
||||
);
|
||||
check(
|
||||
'explicit track altitude and original vertices survive',
|
||||
find('absolute')?.polyline.positions.getValue(now).length === 2 &&
|
||||
find('absolute')
|
||||
.polyline.positions.getValue(now)
|
||||
.every(
|
||||
(p) =>
|
||||
Math.abs(C.Cartographic.fromCartesian(p).height - 10000) < 0.01,
|
||||
),
|
||||
lines(r).length === 1 &&
|
||||
lines(r)[0].positions.length === 2 &&
|
||||
lines(r)[0].positions.every(
|
||||
(p) =>
|
||||
Math.abs(C.Cartographic.fromCartesian(p).height - 10000) < 0.01,
|
||||
),
|
||||
);
|
||||
remove(r);
|
||||
r = await load(
|
||||
@@ -245,10 +248,7 @@ try {
|
||||
);
|
||||
check(
|
||||
'import show controls own previously hidden KML content',
|
||||
r.outcome.ok &&
|
||||
(r.source.importedEntities || r.source.entities.values).every(
|
||||
(e) => e.isShowing,
|
||||
),
|
||||
r.outcome.ok && pins(r).length === 1 && pins(r).every((pin) => pin.show),
|
||||
);
|
||||
remove(r);
|
||||
r = await load(
|
||||
@@ -260,9 +260,8 @@ try {
|
||||
check(
|
||||
'a colored overlay without an icon is drawable',
|
||||
r.outcome.ok &&
|
||||
(r.source.importedEntities || r.source.entities.values).some(
|
||||
(e) => e.rectangle,
|
||||
),
|
||||
ground(r).length === 1 &&
|
||||
ground(r)[0].fill.join() === '0,1,0,1',
|
||||
);
|
||||
remove(r);
|
||||
const xml = wrap(`<Placemark>${point}</Placemark>`);
|
||||
@@ -325,11 +324,10 @@ try {
|
||||
'portrait.geojson',
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 2300));
|
||||
const positions = (r.source?.importedEntities ||
|
||||
r.source?.entities.values)[0].polyline.positions.getValue(now);
|
||||
const screen = positions?.map((p) =>
|
||||
C.SceneTransforms.worldToWindowCoordinates(viewer.scene, p),
|
||||
const positions = ground(r)[0].positions.map(([lon, lat]) =>
|
||||
C.Cartesian3.fromDegrees(lon, lat),
|
||||
);
|
||||
const screen = positions.map(screenOf);
|
||||
check(
|
||||
'portrait flight includes both route endpoints',
|
||||
screen?.every(
|
||||
@@ -349,28 +347,13 @@ try {
|
||||
'intercontinental.geojson',
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 2300));
|
||||
const intercontinental = (
|
||||
r.source?.importedEntities || r.source?.entities.values
|
||||
)
|
||||
.filter((e) => e.position)
|
||||
.map((e) => e.position.getValue(now));
|
||||
const occluder = new C.EllipsoidalOccluder(
|
||||
C.Ellipsoid.WGS84,
|
||||
viewer.camera.positionWC,
|
||||
);
|
||||
const intercontinental = pins(r).map((pin) => pin.position);
|
||||
check(
|
||||
'intercontinental points remain in frame and in front of the horizon',
|
||||
intercontinental?.every((p) => {
|
||||
const s = C.SceneTransforms.worldToWindowCoordinates(viewer.scene, p);
|
||||
return (
|
||||
occluder.isPointVisible(p) &&
|
||||
s &&
|
||||
s.x > 0 &&
|
||||
s.x < innerWidth &&
|
||||
s.y > 0 &&
|
||||
s.y < innerHeight
|
||||
);
|
||||
}),
|
||||
intercontinental.length === 2 &&
|
||||
intercontinental.every(
|
||||
(p) => engine.projector.horizonVisible(p) && inFrame(screenOf(p)),
|
||||
),
|
||||
);
|
||||
remove(r);
|
||||
r = await load(
|
||||
@@ -381,20 +364,12 @@ try {
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 2300));
|
||||
check(
|
||||
'a wall contributes both endpoints to the flight',
|
||||
'a wall top edge contributes both endpoints to the flight',
|
||||
r.outcome.ok &&
|
||||
(r.source.importedEntities || r.source.entities.values)
|
||||
.find((e) => e.wall)
|
||||
?.wall.positions.getValue(now)
|
||||
.every((p) => {
|
||||
const s = C.SceneTransforms.worldToWindowCoordinates(
|
||||
viewer.scene,
|
||||
p,
|
||||
);
|
||||
return (
|
||||
s && s.x > 0 && s.x < innerWidth && s.y > 0 && s.y < innerHeight
|
||||
);
|
||||
}),
|
||||
lines(r).length === 1 &&
|
||||
lines(r)[0].positions.length === 2 &&
|
||||
lines(r)[0].positions.every((p) => inFrame(screenOf(p))) &&
|
||||
tool.list().find((file) => file.id === r.outcome.id).undrawn === 1,
|
||||
);
|
||||
remove(r);
|
||||
r = await load(
|
||||
@@ -447,9 +422,7 @@ try {
|
||||
{ type: 'Point', coordinates: [-122.5, 37.7, 0] },
|
||||
'mesh-point.geojson',
|
||||
);
|
||||
const meshPoint = (r.source?.importedEntities || r.source?.entities.values)
|
||||
.find((e) => e.billboard)
|
||||
?.position.getValue(now);
|
||||
const meshPoint = pins(r)[0]?.position;
|
||||
const meshHeight =
|
||||
meshPoint && C.Cartographic.fromCartesian(meshPoint).height;
|
||||
check(
|
||||
@@ -463,9 +436,7 @@ try {
|
||||
);
|
||||
tool.flyTo(r.outcome.id);
|
||||
await new Promise((resolve) => setTimeout(resolve, 2300));
|
||||
const meshScreen =
|
||||
meshPoint &&
|
||||
C.SceneTransforms.worldToWindowCoordinates(viewer.scene, meshPoint);
|
||||
const meshScreen = meshPoint && screenOf(meshPoint);
|
||||
check(
|
||||
'a high-surface arrival frames the elevated pin',
|
||||
meshScreen &&
|
||||
@@ -480,7 +451,7 @@ try {
|
||||
meshHeight,
|
||||
},
|
||||
);
|
||||
const groundSource = r.source;
|
||||
const groundDrawing = r;
|
||||
const elevated = await load(
|
||||
wrap(
|
||||
'<Placemark><Point><altitudeMode>absolute</altitudeMode><coordinates>-122.5,37.7,5000</coordinates></Point></Placemark>',
|
||||
@@ -492,9 +463,8 @@ try {
|
||||
'a higher imported pin never becomes the sampled ground',
|
||||
elevated.outcome.ok &&
|
||||
Math.abs(
|
||||
C.Cartographic.fromCartesian(
|
||||
groundSource.importedBillboards.get(0).position,
|
||||
).height - 2000,
|
||||
C.Cartographic.fromCartesian(pins(groundDrawing)[0].position).height -
|
||||
2000,
|
||||
) < 2,
|
||||
);
|
||||
remove(elevated);
|
||||
@@ -526,7 +496,13 @@ try {
|
||||
'the shared clock stays exactly unchanged',
|
||||
C.JulianDate.equals(now, viewer.clock.currentTime),
|
||||
);
|
||||
check('no file sources remain', tool.list().length === 0);
|
||||
check(
|
||||
'no files or drawing sets remain',
|
||||
tool.list().length === 0 &&
|
||||
Object.values(window.__qaImports.list()).every(
|
||||
(list) => list.length === 0,
|
||||
),
|
||||
);
|
||||
return results;
|
||||
});
|
||||
for (const r of results) {
|
||||
|
||||
+69
-85
@@ -1,19 +1,19 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Rendered acceptance for UTILITIES ▸ Import: real files through the real file
|
||||
* input and a real drop, parsed by Cesium in the page.
|
||||
* input and a real drop, drawn through the globe contract.
|
||||
*
|
||||
* Proves: GeoJSON, KML, KMZ (with a packed icon and ground overlay) and GPX
|
||||
* each become one row and one data source; the imported shape actually paints
|
||||
* each become one row and owned drawing sets; the imported shape actually paints
|
||||
* on the globe in its row's color (a GeoJSON `stroke`/`fill` does not override
|
||||
* it) and stops painting when hidden; no import makes the page request
|
||||
* anything named in it (network link, remote icon, remote style, palette
|
||||
* icon, balloon or extended-data HTML, maki marker) or anything outside the
|
||||
* page for it; no entity keeps a description, label or properties; dated KML
|
||||
* page for it; no drawing keeps a description, label or properties; dated KML
|
||||
* and GPX leave the app's clock alone and still draw now; a GPX track keeps
|
||||
* its two segments apart; keyboard focus survives hide/show; a dropped file
|
||||
* imports like a picked one; a file that is not geodata is refused with a
|
||||
* reason and leaves nothing behind; Remove gives every data source back; no
|
||||
* reason and leaves nothing behind; Remove releases every drawing set; no
|
||||
* page errors.
|
||||
*
|
||||
* Usage: dev server up (`npm run dev`), then `node scripts/qa-geo-import.mjs`.
|
||||
@@ -23,9 +23,13 @@ import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import puppeteer from 'puppeteer';
|
||||
import { installImportProbe } from './qa-geo-import-probe.mjs';
|
||||
import { hookRenderErrors, readRenderErrors } from './qa-browserEvidence.mjs';
|
||||
|
||||
const BASE_URL = process.env.QA_BASE_URL || 'http://localhost:4173';
|
||||
const BASE_URL =
|
||||
process.argv.find((arg) => /^https?:/.test(arg)) ||
|
||||
process.env.QA_BASE_URL ||
|
||||
'http://localhost:4173';
|
||||
const SHOT_DIR = process.env.QA_SHOT_DIR || 'qa-shots/geo-import';
|
||||
const HEADFUL = process.env.QA_HEADFUL === '1';
|
||||
// Golden Gate Park: a big, flat, green rectangle the test polygon can cover.
|
||||
@@ -233,7 +237,8 @@ async function cyanPixels() {
|
||||
const state = () =>
|
||||
page.evaluate(() => ({
|
||||
files: window.__gevGeoImport.list(),
|
||||
dataSources: window.__godsEyeView.viewer.dataSources.length,
|
||||
drawing: window.__qaImports.list(),
|
||||
groups: window.__qaImports.groups().length,
|
||||
rows: document.querySelectorAll('#geo-import-list .geo-import-row').length,
|
||||
hint: document.getElementById('geo-import-hint')?.textContent || '',
|
||||
}));
|
||||
@@ -283,6 +288,7 @@ try {
|
||||
.width > 0,
|
||||
{ timeout: 10_000 },
|
||||
);
|
||||
await installImportProbe(page);
|
||||
const before = await state();
|
||||
check(
|
||||
'Import is in the UTILITIES panel with an empty, hidden list',
|
||||
@@ -313,7 +319,6 @@ try {
|
||||
};
|
||||
});
|
||||
const clockBefore = await clock();
|
||||
const sourcesBefore = before.dataSources;
|
||||
|
||||
// Picked through the real <input type=file>.
|
||||
const input = await page.$('#geo-import-input');
|
||||
@@ -328,10 +333,8 @@ try {
|
||||
);
|
||||
const after = await state();
|
||||
check(
|
||||
'four picked files become four rows and four data sources',
|
||||
imported &&
|
||||
after.rows === 4 &&
|
||||
after.dataSources === before.dataSources + 4,
|
||||
'four picked files become four rows and four groups of drawing sets',
|
||||
imported && after.rows === 4 && after.groups === before.groups + 4,
|
||||
JSON.stringify(
|
||||
after.files.map((f) => `${f.name}:${f.format}:${f.entityCount}`),
|
||||
),
|
||||
@@ -361,85 +364,70 @@ try {
|
||||
)),
|
||||
`${JSON.stringify(clockBefore)} → ${JSON.stringify(clockAfter)}`,
|
||||
);
|
||||
const inside = await page.evaluate((from) => {
|
||||
const viewer = window.__godsEyeView.viewer;
|
||||
const now = viewer.clock.currentTime;
|
||||
const sources = [];
|
||||
for (let i = from; i < viewer.dataSources.length; i++)
|
||||
sources.push(viewer.dataSources.get(i));
|
||||
const entities = sources.flatMap(
|
||||
(ds) => ds.importedEntities || ds.entities.values,
|
||||
);
|
||||
const named = (name) => entities.find((e) => e.name === name);
|
||||
const gpx = sources[3].importedEntities || sources[3].entities.values;
|
||||
const kmz = sources[2].importedEntities || sources[2].entities.values;
|
||||
const inside = await page.evaluate(() => {
|
||||
const groups = window.__qaImports.groups();
|
||||
const pins = (group) =>
|
||||
group.filter((r) => r.kind === 'movers').flatMap((r) => r.items);
|
||||
const paths = (group) =>
|
||||
group
|
||||
.filter((r) => r.kind === 'ground' && r.options.kind === 'corridor')
|
||||
.flatMap((r) => r.items);
|
||||
const kml = groups[1],
|
||||
kmz = groups[2],
|
||||
gpx = groups[3];
|
||||
const drawings = groups.flat().flatMap((r) => [r.options, ...r.items]);
|
||||
return {
|
||||
sources: sources.length,
|
||||
clocks: sources.filter((ds) => ds.clock !== undefined).length,
|
||||
described: entities.filter((e) => e.description !== undefined).length,
|
||||
labelled: entities.filter((e) => e.label !== undefined).length,
|
||||
withProperties: entities.filter((e) => e.properties !== undefined).length,
|
||||
withKml: entities.filter((e) => e.kml !== undefined).length,
|
||||
hidden: entities.filter((e) => !e.isAvailable(now)).length,
|
||||
datedPinNow: Boolean(named('QA KML pin')?.isAvailable(now)),
|
||||
fallbackPins: ['QA KML pin', 'QA palette pin'].filter((name) =>
|
||||
named(name)?.billboard?.image?.getValue(now),
|
||||
).length,
|
||||
movingIsLine: Boolean(
|
||||
named('QA moving')?.polyline && !named('QA moving')?.position,
|
||||
groups: groups.length,
|
||||
metadata: drawings.some((r) =>
|
||||
[
|
||||
'description',
|
||||
'label',
|
||||
'properties',
|
||||
'kml',
|
||||
'availability',
|
||||
'clock',
|
||||
].some((key) => key in r),
|
||||
),
|
||||
movingVertices:
|
||||
named('QA moving')?.polyline?.positions?.getValue(now)?.length,
|
||||
splitTracks: (sources[1].importedEntities || sources[1].entities.values)
|
||||
.filter((e) => e.parent?.name === 'QA split tracks' && e.polyline)
|
||||
.map((e) => e.polyline.positions.getValue(now).length),
|
||||
gpxLines: gpx.filter((e) => e.polyline).length,
|
||||
gpxPoints: gpx.filter((e) => e.billboard || e.point).length,
|
||||
kmzLocalImage: kmz.some((e) =>
|
||||
String(e.billboard?.image?.getValue(now) || '').startsWith('blob:'),
|
||||
fallbackPins: pins(kml).length,
|
||||
datedPinsShown: pins(kml).every((pin) => pin.show),
|
||||
kmlVertices: paths(kml).map((line) => line.positions.length),
|
||||
gpxLines: paths(gpx).length,
|
||||
gpxPoints: pins(gpx).length,
|
||||
kmzPins: pins(kmz).length,
|
||||
kmzOverlay: kmz.some(
|
||||
(r) => r.kind === 'imagery' && r.options.source.url.startsWith('blob:'),
|
||||
),
|
||||
groundedLines: [kml, kmz].every(
|
||||
(group) =>
|
||||
paths(group).length === 4 && !group.some((r) => r.kind === 'lines'),
|
||||
),
|
||||
xyzPolygon: groups[0].some(
|
||||
(r) =>
|
||||
r.kind === 'ground' &&
|
||||
r.options.kind === 'polygon' &&
|
||||
r.items[0].rings.flat().every((p) => p.length === 2),
|
||||
),
|
||||
kmzOverlay: kmz.some((e) => e.rectangle),
|
||||
groundedLines: sources
|
||||
.slice(1, 3)
|
||||
.every((ds) =>
|
||||
(ds.importedEntities || ds.entities.values)
|
||||
.filter((e) => e.polyline)
|
||||
.every((e) => e.polyline.clampToGround?.getValue(now)),
|
||||
),
|
||||
xyzPolygon:
|
||||
(sources[0].importedEntities || sources[0].entities.values)
|
||||
.find((e) => e.polygon)
|
||||
?.polygon.perPositionHeight?.getValue(now) !== true,
|
||||
};
|
||||
}, sourcesBefore);
|
||||
});
|
||||
check(
|
||||
'no imported entity keeps a description, label, properties or KML data',
|
||||
inside.sources === 4 &&
|
||||
inside.described +
|
||||
inside.labelled +
|
||||
inside.withProperties +
|
||||
inside.withKml ===
|
||||
0,
|
||||
'import drawing contains no descriptions, labels, properties or time data',
|
||||
inside.groups === 4 && !inside.metadata,
|
||||
JSON.stringify(inside),
|
||||
);
|
||||
check(
|
||||
'no imported source has a clock, and dated features draw now',
|
||||
inside.clocks === 0 &&
|
||||
inside.hidden === 0 &&
|
||||
inside.datedPinNow &&
|
||||
inside.movingIsLine &&
|
||||
inside.movingVertices === 2,
|
||||
'dated KML pins draw now and its moving track is a two-vertex line',
|
||||
inside.datedPinsShown && inside.kmlVertices[0] === 2,
|
||||
JSON.stringify(inside),
|
||||
);
|
||||
check(
|
||||
'blocked KML icon sources use drawable local fallback pins',
|
||||
'blocked KML icons use drawable local fallback pins',
|
||||
inside.fallbackPins === 2,
|
||||
JSON.stringify({ fallbackPins: inside.fallbackPins }),
|
||||
JSON.stringify(inside),
|
||||
);
|
||||
check(
|
||||
'KML MultiTrack keeps its segments and original vertex count',
|
||||
inside.splitTracks.join() === '2,2',
|
||||
JSON.stringify(inside.splitTracks),
|
||||
inside.kmlVertices.slice(1, 3).join() === '2,2',
|
||||
JSON.stringify(inside),
|
||||
);
|
||||
check(
|
||||
'the GPX track keeps its two segments apart',
|
||||
@@ -447,18 +435,13 @@ try {
|
||||
JSON.stringify(inside),
|
||||
);
|
||||
check(
|
||||
'KMZ retains its own packed image and overlay',
|
||||
inside.kmzLocalImage && inside.kmzOverlay,
|
||||
'KMZ retains its packed pin and imagery overlay',
|
||||
inside.kmzPins === 2 && inside.kmzOverlay && after.files[2].undrawn === 0,
|
||||
);
|
||||
check(
|
||||
'XYZ polygons and KML/KMZ lines use ground geometry',
|
||||
inside.xyzPolygon && inside.groundedLines,
|
||||
);
|
||||
await page.evaluate((from) => {
|
||||
for (const entity of window.__godsEyeView.viewer.dataSources.get(from)
|
||||
.entities.values)
|
||||
if (entity.billboard) entity.show = false;
|
||||
}, sourcesBefore);
|
||||
await page.evaluate((view) => {
|
||||
const viewer = window.__godsEyeView.viewer;
|
||||
viewer.camera.cancelFlight?.();
|
||||
@@ -574,9 +557,10 @@ try {
|
||||
);
|
||||
const cleared = await state();
|
||||
check(
|
||||
'Remove gives every data source back',
|
||||
cleared.rows === 0 && cleared.dataSources === before.dataSources,
|
||||
`${cleared.dataSources} vs ${before.dataSources}`,
|
||||
'Remove releases every import drawing set',
|
||||
cleared.rows === 0 &&
|
||||
Object.values(cleared.drawing).every((list) => list.length === 0),
|
||||
JSON.stringify(cleared.drawing),
|
||||
);
|
||||
await wait(1500);
|
||||
check(
|
||||
|
||||
@@ -160,7 +160,7 @@ await strict.run({}, async (browser) => {
|
||||
{ engine: off.engine, diff: diff(normal, offFrame) },
|
||||
);
|
||||
|
||||
// Cesium keeps its scene bloom stage; its engine has no bloom.
|
||||
// Cesium drives its scene bloom stage through the render contract.
|
||||
await strict.switchTo(page, 'cesium');
|
||||
await freeze(page);
|
||||
await setBloom(page, false, 200);
|
||||
@@ -178,9 +178,9 @@ await strict.run({}, async (browser) => {
|
||||
const cesiumGlow = await capture(page);
|
||||
save('cesium-bloom-200', cesiumGlow);
|
||||
check(
|
||||
"Cesium: its scene's bloom stage glows, the engine draws none",
|
||||
cesium.engine === null &&
|
||||
cesiumStage.setBloom === 'undefined' &&
|
||||
"Cesium: the engine drives its scene's bloom stage",
|
||||
cesium.engine?.enabled === true &&
|
||||
cesiumStage.setBloom === 'function' &&
|
||||
cesiumStage.enabled !== false &&
|
||||
diff(cesiumNormal, cesiumGlow).mean > 1,
|
||||
{ stage: cesiumStage, diff: diff(cesiumNormal, cesiumGlow) },
|
||||
|
||||
@@ -238,20 +238,18 @@ await strict.run(
|
||||
imported.diagnostics.drawn === 4,
|
||||
{ files: imported.files.map((f) => f.format), rows: imported.rows },
|
||||
);
|
||||
// The KMZ's packed ground overlay is a rectangle Navara has no drawing
|
||||
// for; the KML's remote one was left out of the file on import.
|
||||
// Packed overlays draw through imagery; remote overlays are removed on import.
|
||||
const undrawn = Object.fromEntries(
|
||||
imported.files.map((f) => [f.name, f.undrawn]),
|
||||
);
|
||||
await strict.check(
|
||||
page,
|
||||
'geo-import',
|
||||
'the ground overlay Navara cannot draw is counted and reported in the hint',
|
||||
undrawn['packed.kmz'] === 1 &&
|
||||
'all supported geometry, including the packed overlay, draws on Navara',
|
||||
undrawn['packed.kmz'] === 0 &&
|
||||
undrawn['park.geojson'] === 0 &&
|
||||
undrawn['remote-bits.kml'] === 0 &&
|
||||
undrawn['walk.gpx'] === 0 &&
|
||||
/leaves out of packed\.kmz: 1/.test(imported.hint),
|
||||
undrawn['walk.gpx'] === 0,
|
||||
{ undrawn, hint: imported.hint },
|
||||
);
|
||||
await strict.check(
|
||||
|
||||
@@ -252,7 +252,7 @@ await strict.run({}, async (browser) => {
|
||||
);
|
||||
await page.evaluate(() => window.__qaSonarSet.destroy());
|
||||
|
||||
// Cesium keeps its command adapter; its engine draws no sonar.
|
||||
// Cesium draws contact sonar through its engine command adapter.
|
||||
await strict.switchTo(page, 'cesium');
|
||||
await page.evaluate(() => {
|
||||
const { styleManager } = window.__godsEyeView;
|
||||
@@ -267,8 +267,8 @@ await strict.run({}, async (browser) => {
|
||||
gpu: document.documentElement.dataset.cyberSonarGpu,
|
||||
}));
|
||||
check(
|
||||
'Cesium: the command adapter treats contacts, the engine none',
|
||||
cesium.setContactSonar === 'undefined' && cesium.gpu === 'supported',
|
||||
'Cesium: the engine command adapter treats contacts',
|
||||
cesium.setContactSonar === 'function' && cesium.gpu === 'supported',
|
||||
cesium,
|
||||
);
|
||||
await page.evaluate(() =>
|
||||
|
||||
@@ -279,7 +279,7 @@ await strict.run({}, async (browser) => {
|
||||
stageName: stage.name,
|
||||
enabled: stage.enabled,
|
||||
intensity: stage.uniforms.intensity,
|
||||
inScene: a.viewer?.scene?.postProcessStages?.contains?.(stage) ?? null,
|
||||
inScene: a.engine.render.style('thermal').intensity > 0,
|
||||
};
|
||||
});
|
||||
const cesiumThermal = await capture(page);
|
||||
@@ -290,9 +290,9 @@ await strict.run({}, async (browser) => {
|
||||
path: path.join(outDir, 'cesium-thermal-page.png'),
|
||||
});
|
||||
check(
|
||||
'Cesium: FLIR is its own post stage, the engine draws none',
|
||||
'Cesium: the engine owns the FLIR post stage',
|
||||
cesium.pending === 0 &&
|
||||
cesiumState.setStyle === 'undefined' &&
|
||||
cesiumState.setStyle === 'function' &&
|
||||
cesiumState.enabled === true &&
|
||||
cesiumState.intensity === 1 &&
|
||||
cesiumState.inScene !== false,
|
||||
|
||||
@@ -32,13 +32,15 @@ try {
|
||||
const results = await page.evaluate(async () => {
|
||||
window.CESIUM_BASE_URL = '/cesium/';
|
||||
// Use StarrySky's exact Vite dependency URL, including its version query.
|
||||
const source = await fetch('/src/sky/starrySky.js').then((r) => r.text());
|
||||
const source = await fetch('/src/globe/cesium/starrySky.js').then((r) =>
|
||||
r.text(),
|
||||
);
|
||||
const cesiumUrl = source.match(
|
||||
/from ["']([^"']*\/cesium\.js[^"']*)["']/,
|
||||
)?.[1];
|
||||
if (!cesiumUrl) throw new Error('run against the Vite dev server');
|
||||
const C = await import(cesiumUrl);
|
||||
const { StarrySky } = await import('/src/sky/starrySky.js');
|
||||
const { StarrySky } = await import('/src/globe/cesium/starrySky.js');
|
||||
const time = C.JulianDate.fromIso8601('2026-10-19T00:00:00Z');
|
||||
const results = {};
|
||||
for (const kind of ['black', 'starry', 'white']) {
|
||||
|
||||
@@ -169,7 +169,7 @@ try {
|
||||
result.bloom.intensityPct === 98 &&
|
||||
snapshot.bloom.intensity === 98 &&
|
||||
manager._visualEffects.bloomIntensity === 98 &&
|
||||
manager._bloomStage.enabled
|
||||
window.__godsEyeView.viewer.scene.postProcessStages.bloom.enabled
|
||||
);
|
||||
}),
|
||||
);
|
||||
@@ -183,7 +183,8 @@ try {
|
||||
result.sharpen.intensityPct === 61 &&
|
||||
manager.getVisualState().sharpen.intensity === 61 &&
|
||||
manager._visualEffects.sharpenIntensity === 0.61 &&
|
||||
Math.abs(manager._sharpenStage.uniforms.amount - 1.32) < 1e-9
|
||||
Math.abs(window.__godsEyeView.engine.render.sharpen().amount - 1.32) <
|
||||
1e-9
|
||||
);
|
||||
}),
|
||||
);
|
||||
@@ -199,9 +200,7 @@ try {
|
||||
const manager = window.__godsEyeView.styleManager;
|
||||
const effects = manager._visualEffects;
|
||||
manager.setStyle('retro');
|
||||
const owned = [...Object.values(effects.stages), effects.sharpenStage];
|
||||
const collection = manager.viewer.scene.postProcessStages;
|
||||
const previousBloom = effects.previousBloom;
|
||||
const engine = window.__godsEyeView.engine;
|
||||
const pending = manager.dispose();
|
||||
const stoppedBeforeAwait =
|
||||
effects.stopped &&
|
||||
@@ -210,22 +209,19 @@ try {
|
||||
await pending;
|
||||
return {
|
||||
stoppedBeforeAwait,
|
||||
removed: owned.every((stage) => !collection.contains(stage)),
|
||||
restored:
|
||||
effects.bloomStage.enabled === previousBloom.enabled &&
|
||||
Object.entries(previousBloom.uniforms).every(
|
||||
([name, value]) => effects.bloomStage.uniforms[name] === value,
|
||||
),
|
||||
disabled:
|
||||
engine.render.visualStyles.every(
|
||||
(name) => engine.render.style(name).intensity === 0,
|
||||
) &&
|
||||
!engine.render.bloom().enabled &&
|
||||
!engine.render.sharpen().enabled,
|
||||
};
|
||||
});
|
||||
check(
|
||||
'UI teardown stops effect animation before its first await',
|
||||
teardown.stoppedBeforeAwait,
|
||||
);
|
||||
check(
|
||||
'UI teardown removes owned stages and restores borrowed bloom',
|
||||
teardown.removed && teardown.restored,
|
||||
);
|
||||
check('UI teardown disables owned effects on the engine', teardown.disabled);
|
||||
check(
|
||||
'effects do not introduce uncaught browser errors',
|
||||
errors.length === 0,
|
||||
|
||||
@@ -36,7 +36,8 @@ try {
|
||||
);
|
||||
const renderer = await page.evaluate(async () => {
|
||||
const C = await import('/node_modules/cesium/Build/Cesium/index.js');
|
||||
const { maxPitchForHeight } = await import('/src/app/zoomOutGuard.js');
|
||||
const { maxPitchForHeight } =
|
||||
await import('/src/globe/cesium/zoomOutGuard.js');
|
||||
const { readCameraTargetFrame } =
|
||||
await import('/src/ui/cameraOrientationControls.js');
|
||||
const { viewer, engine } = window.__godsEyeView;
|
||||
@@ -86,7 +87,7 @@ try {
|
||||
};
|
||||
viewer.camera.setView = (options) => {
|
||||
// Cesium itself also calls setView while processing touch movement.
|
||||
if (new Error().stack.includes('/src/app/zoomOutGuard.js'))
|
||||
if (new Error().stack.includes('/src/globe/cesium/zoomOutGuard.js'))
|
||||
qa.guardWrites++;
|
||||
return original(options);
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user