mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
Normalize GeoJSON ground footprints, preserve explicit KML track heights, and tessellate ground paths before native loading. Validate coordinates, geometry types and decoded image budgets before allocating native resources; strip loader-sensitive metadata and settle deferred failures and cancellation. Render static pins in owned billboard collections instead of walking tens of thousands of point entities each frame. Refine nearby ground heights in batches, isolate imported pins from height picks, and release owned resources and point selections on removal. Use safe ray origins above a hidden coarse globe and retain valid mesh heights outside the current footprint. Frame complete geometry with viewport-aware nadir arrivals, including walls and rotated overlays; respect camera gestures and accept document file drops. Strengthen real-loader, native 3D Tiles, paint, clock and lifecycle coverage. Addresses both P1 and all 25 P2 findings in the follow-up review of #1122.
164 lines
5.5 KiB
JavaScript
164 lines
5.5 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import * as Cesium from 'cesium';
|
|
import { cancelCameraArrival } from '../data/cameraArrival.js';
|
|
import { dataSourcePoints, flyToDataSource } from './flyToDataSource.js';
|
|
|
|
function source() {
|
|
const ds = new Cesium.CustomDataSource('t');
|
|
ds.entities.add({
|
|
position: Cesium.Cartesian3.fromDegrees(-122.4862, 37.7694, -2667),
|
|
billboard: { heightReference: Cesium.HeightReference.CLAMP_TO_GROUND },
|
|
});
|
|
ds.entities.add({
|
|
polyline: {
|
|
positions: Cesium.Cartesian3.fromDegreesArray([
|
|
-122.49, 37.77, -122.48, 37.771,
|
|
]),
|
|
},
|
|
});
|
|
ds.entities.add({
|
|
polygon: {
|
|
hierarchy: Cesium.Cartesian3.fromDegreesArray([
|
|
-122.5, 37.76, -122.49, 37.76, -122.49, 37.765,
|
|
]),
|
|
},
|
|
});
|
|
return ds;
|
|
}
|
|
|
|
test('points come from positions, lines and polygons, as lon/lat only', () => {
|
|
const points = dataSourcePoints(source());
|
|
assert.equal(points.length, 6);
|
|
assert.ok(Math.abs(points[0][0] + 122.4862) < 1e-6);
|
|
assert.ok(Math.abs(points[0][1] - 37.7694) < 1e-6);
|
|
});
|
|
|
|
test('the camera frames the footprint at the surface, never a buried center', () => {
|
|
const calls = [];
|
|
const viewer = {
|
|
camera: {
|
|
heading: 0,
|
|
flyToBoundingSphere: (sphere, options) => calls.push({ sphere, options }),
|
|
},
|
|
flyTo: () => calls.push('viewer.flyTo'),
|
|
};
|
|
assert.equal(flyToDataSource(viewer, source()), true);
|
|
const center = Cesium.Cartographic.fromCartesian(calls[0].sphere.center);
|
|
assert.ok(Math.abs(center.height) < 50, `center height ${center.height}`);
|
|
assert.ok(calls[0].options.offset.range >= 800);
|
|
assert.equal(
|
|
typeof calls[0].options.complete,
|
|
'function',
|
|
'the ground guard runs on landing',
|
|
);
|
|
assert.equal(
|
|
flyToDataSource(viewer, new Cesium.CustomDataSource('empty')),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
calls.length,
|
|
1,
|
|
'empty sources never start deferred navigation',
|
|
);
|
|
});
|
|
|
|
test('a very long track is framed without spreading it into a call', () => {
|
|
// 400k vertices: `push(...line)` throws RangeError (too many arguments)
|
|
// well below this; the walk keeps a bounded sample and both ends.
|
|
const n = 400_000;
|
|
const line = new Array(n);
|
|
for (let i = 0; i < n; i++)
|
|
line[i] = Cesium.Cartesian3.fromDegrees(-120 + (i / n) * 2, 40);
|
|
const ds = new Cesium.CustomDataSource('long');
|
|
ds.entities.add({ polyline: { positions: line } });
|
|
ds.entities.add({ position: Cesium.Cartesian3.fromDegrees(-110, 41) });
|
|
const points = dataSourcePoints(ds);
|
|
assert.equal(points.length, n + 1, 'every vertex contributes to framing');
|
|
const lons = points.map(([lon]) => lon);
|
|
assert.ok(
|
|
lons.reduce((a, b) => Math.min(a, b), Infinity) < -119.99 &&
|
|
lons.reduce((a, b) => Math.max(a, b), -Infinity) > -110.01,
|
|
);
|
|
assert.ok(lons.some((lon) => Math.abs(lon - -118.000005) < 0.01));
|
|
});
|
|
|
|
test('framing includes a sparse interior outlier, walls and rotated overlays', () => {
|
|
const ds = new Cesium.CustomDataSource('bounds');
|
|
const line = Array.from({ length: 200031 }, () =>
|
|
Cesium.Cartesian3.fromDegrees(0, 0),
|
|
);
|
|
line[12345] = Cesium.Cartesian3.fromDegrees(20, 10);
|
|
ds.entities.add({ polyline: { positions: line } });
|
|
ds.entities.add({
|
|
wall: { positions: Cesium.Cartesian3.fromDegreesArray([30, 10, 31, 11]) },
|
|
});
|
|
ds.entities.add({
|
|
rectangle: {
|
|
coordinates: Cesium.Rectangle.fromDegrees(40, 0, 41, 1),
|
|
rotation: Math.PI / 4,
|
|
},
|
|
});
|
|
const points = dataSourcePoints(ds);
|
|
assert.ok(points.some(([lon]) => Math.abs(lon - 20) < 1e-6));
|
|
assert.ok(points.some(([lon]) => Math.abs(lon - 31) < 1e-6));
|
|
assert.ok(points.some(([lon]) => lon > 41));
|
|
});
|
|
|
|
test('portrait framing accounts for FOV, high ground and symmetric extents', () => {
|
|
const ds = new Cesium.CustomDataSource('view');
|
|
ds.entities.add({
|
|
polyline: {
|
|
positions: Cesium.Cartesian3.fromDegreesArray([-105, 39, -104.9, 39]),
|
|
},
|
|
});
|
|
const calls = [];
|
|
const viewer = {
|
|
scene: { globe: { getHeight: () => 2600 } },
|
|
camera: {
|
|
frustum: { fov: Math.PI / 3, aspectRatio: 390 / 844 },
|
|
flyToBoundingSphere: (sphere, options) => calls.push({ sphere, options }),
|
|
},
|
|
};
|
|
flyToDataSource(viewer, ds);
|
|
assert.ok(
|
|
Math.abs(
|
|
Cesium.Cartographic.fromCartesian(calls[0].sphere.center).height - 2600,
|
|
) < 1e-6,
|
|
);
|
|
assert.equal(calls[0].options.offset.pitch, -Math.PI / 2);
|
|
viewer.camera.frustum.aspectRatio = 16 / 9;
|
|
flyToDataSource(viewer, ds);
|
|
assert.ok(calls[0].options.offset.range > calls[1].options.offset.range);
|
|
const symmetric = {
|
|
entities: {
|
|
values: [
|
|
{ position: { getValue: () => new Cesium.Cartesian3(6378137, 0, 0) } },
|
|
{ position: { getValue: () => new Cesium.Cartesian3(-6378137, 0, 0) } },
|
|
],
|
|
},
|
|
};
|
|
assert.doesNotThrow(() => flyToDataSource(viewer, symmetric));
|
|
assert.ok(Number.isFinite(calls.at(-1).options.offset.range));
|
|
assert.doesNotThrow(() => calls.at(-1).options.complete());
|
|
cancelCameraArrival(viewer);
|
|
});
|
|
|
|
test('non-finite positions and hidden features never enter the camera footprint', () => {
|
|
const position = (p) => ({ position: { getValue: () => p } });
|
|
const points = dataSourcePoints({
|
|
entities: {
|
|
values: [
|
|
position(new Cesium.Cartesian3(NaN, 1, 2)),
|
|
{
|
|
...position(Cesium.Cartesian3.fromDegrees(10, 10)),
|
|
isShowing: false,
|
|
},
|
|
position(Cesium.Cartesian3.fromDegrees(20, 20)),
|
|
],
|
|
},
|
|
});
|
|
assert.equal(points.length, 1);
|
|
assert.ok(Math.abs(points[0][0] - 20) < 1e-6);
|
|
});
|