refactor(launches,cctv,alpr): take vector maths from src/geo

Launches selection/replay/panel/policy/policyHelpers/state/camera, CCTV geometry/model/projection/navigation/cards/geometryQueue/state and ALPR model/visuals now use src/geo for Math, Cartesian2/3, Cartographic, Matrix3/4, Quaternion and ENU frames. launches/paths.js keeps Cesium (EllipsoidGeodesic). In camera.js, EllipsoidGeodesic, BoundingSphere.fromPoints/union and the BoundingSphere constructor stay on Cesium; the geo Cartographics passed into EllipsoidGeodesic are only read field by field and cloned there (computeProperties). The CCTV plane orientation stays a Cesium Quaternion and plane dimensions a Cesium Cartesian2 (entity ConstantProperty values). launches-layer and alpr-cameras own the src/geo files.
This commit is contained in:
German Khokhlov committed 2026-10-15 00:18:35 +04:00
1 parent ee8b94d987
commit a915bdb3e5
17 files changed
+215 -214

No files matched your search

+30 -2
View File
@@ -1145,7 +1145,21 @@
"src/layers/launches/source.js", "src/layers/launches/source.js",
"src/layers/launches/state.js", "src/layers/launches/state.js",
"src/layers/launches/testing.js", "src/layers/launches/testing.js",
"src/data/inputOwnership.js" "src/data/inputOwnership.js",
"src/geo/index.js",
"src/geo/math.js",
"src/geo/ellipsoid.js",
"src/geo/cartographic.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/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
"src/geo/developerError.js"
], ],
"external": ["cesium"] "external": ["cesium"]
}, },
@@ -1406,7 +1420,21 @@
"src/sources/httpBody.js", "src/sources/httpBody.js",
"src/sources/vectorTiles.js", "src/sources/vectorTiles.js",
"src/sources/phaseTiming.js", "src/sources/phaseTiming.js",
"src/layers/alpr/floors.js" "src/layers/alpr/floors.js",
"src/geo/index.js",
"src/geo/math.js",
"src/geo/ellipsoid.js",
"src/geo/cartographic.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/scaleToGeodeticSurface.js",
"src/geo/check.js",
"src/geo/debug.js",
"src/geo/developerError.js"
], ],
"external": ["cesium", "@mapbox/vector-tile", "pbf"] "external": ["cesium", "@mapbox/vector-tile", "pbf"]
}, },
+6 -6
View File
@@ -1,4 +1,4 @@
import * as Cesium from 'cesium'; import * as geo from '../../geo/index.js';
import { EARTH_MEAN_RADIUS_M } from './policy.js'; import { EARTH_MEAN_RADIUS_M } from './policy.js';
export * from './records.js'; export * from './records.js';
@@ -12,9 +12,9 @@ export * from './records.js';
*/ */
export function destinationPointDeg(latDeg, lonDeg, bearingDeg, distanceM) { export function destinationPointDeg(latDeg, lonDeg, bearingDeg, distanceM) {
const angularDistance = distanceM / EARTH_MEAN_RADIUS_M; const angularDistance = distanceM / EARTH_MEAN_RADIUS_M;
const bearing = Cesium.Math.toRadians(bearingDeg); const bearing = geo.math.toRadians(bearingDeg);
const lat1 = Cesium.Math.toRadians(latDeg); const lat1 = geo.math.toRadians(latDeg);
const lon1 = Cesium.Math.toRadians(lonDeg); const lon1 = geo.math.toRadians(lonDeg);
const lat2 = Math.asin( const lat2 = Math.asin(
Math.sin(lat1) * Math.cos(angularDistance) + Math.sin(lat1) * Math.cos(angularDistance) +
Math.cos(lat1) * Math.sin(angularDistance) * Math.cos(bearing), Math.cos(lat1) * Math.sin(angularDistance) * Math.cos(bearing),
@@ -26,8 +26,8 @@ export function destinationPointDeg(latDeg, lonDeg, bearingDeg, distanceM) {
Math.cos(angularDistance) - Math.sin(lat1) * Math.sin(lat2), Math.cos(angularDistance) - Math.sin(lat1) * Math.sin(lat2),
); );
return { return {
latitude: Cesium.Math.toDegrees(lat2), latitude: geo.math.toDegrees(lat2),
longitude: Cesium.Math.toDegrees(lon2), longitude: geo.math.toDegrees(lon2),
}; };
} }
+4 -8
View File
@@ -1,4 +1,4 @@
import * as Cesium from 'cesium'; import * as geo from '../../geo/index.js';
import { destinationPointDeg } from './model.js'; import { destinationPointDeg } from './model.js';
import { import {
DIRECTION_CONE_M, DIRECTION_CONE_M,
@@ -95,13 +95,9 @@ export function directionWedgePositions(record, heightM = 0, heightAt = null) {
return Number.isFinite(height) ? height : heightM; return Number.isFinite(height) ? height : heightM;
}; };
return [ return [
Cesium.Cartesian3.fromDegrees(record.longitude, record.latitude, heightM), geo.cartesian3.fromDegrees(record.longitude, record.latitude, heightM),
Cesium.Cartesian3.fromDegrees(left.longitude, left.latitude, corner(left)), geo.cartesian3.fromDegrees(left.longitude, left.latitude, corner(left)),
Cesium.Cartesian3.fromDegrees( geo.cartesian3.fromDegrees(right.longitude, right.latitude, corner(right)),
right.longitude,
right.latitude,
corner(right),
),
]; ];
} }
+3 -3
View File
@@ -8,7 +8,7 @@ import {
CCTV_FRAME_CANVAS_H, CCTV_FRAME_CANVAS_H,
applyFrameResult, applyFrameResult,
} from '../../data/cctvCards.js'; } from '../../data/cctvCards.js';
import * as Cesium from 'cesium'; import * as geo from '../../geo/index.js';
import { horizonOccluder } from '../../data/iconOrientation.js'; import { horizonOccluder } from '../../data/iconOrientation.js';
import { selectCctvLod, applyEvictionGrace } from '../../data/cctvLod.js'; import { selectCctvLod, applyEvictionGrace } from '../../data/cctvLod.js';
import { import {
@@ -85,8 +85,8 @@ export function createCards({ state: layerState, services, parts, source }) {
} }
const scene = layerState._viewer.scene; const scene = layerState._viewer.scene;
const carto = layerState._viewer.camera.positionCartographic; const carto = layerState._viewer.camera.positionCartographic;
const viewerLat = carto ? Cesium.Math.toDegrees(carto.latitude) : 0; const viewerLat = carto ? geo.math.toDegrees(carto.latitude) : 0;
const viewerLon = carto ? Cesium.Math.toDegrees(carto.longitude) : 0; const viewerLon = carto ? geo.math.toDegrees(carto.longitude) : 0;
// The active camera is excluded from ambient selection ALWAYS (not just // The active camera is excluded from ambient selection ALWAYS (not just
// after its async activation settles). It is published separately in the // after its async activation settles). It is published separately in the
// protected lane below, and grace never applies to it. // protected lane below, and grace never applies to it.
+13 -16
View File
@@ -1,4 +1,5 @@
import * as Cesium from 'cesium'; import * as Cesium from 'cesium';
import * as geo from '../../geo/index.js';
import { createFrustumVolumePrimitive } from '../../data/cctvViewshed.js'; import { createFrustumVolumePrimitive } from '../../data/cctvViewshed.js';
import { import {
PROJECTION_VERT_ASPECT, PROJECTION_VERT_ASPECT,
@@ -272,7 +273,7 @@ export function createGeometry({ state: layerState, services, parts, source }) {
*/ */
function frustumCartesians(geometry) { function frustumCartesians(geometry) {
const at = (p) => Cesium.Cartesian3.fromDegrees(p.lon, p.lat, p.alt); const at = (p) => geo.cartesian3.fromDegrees(p.lon, p.lat, p.alt);
return { return {
mount: at(geometry.mount), mount: at(geometry.mount),
capCenter: at(geometry.capCenter), capCenter: at(geometry.capCenter),
@@ -280,7 +281,7 @@ export function createGeometry({ state: layerState, services, parts, source }) {
tr: at(geometry.corners.tr), tr: at(geometry.corners.tr),
br: at(geometry.corners.br), br: at(geometry.corners.br),
bl: at(geometry.corners.bl), bl: at(geometry.corners.bl),
label: Cesium.Cartesian3.fromDegrees( label: geo.cartesian3.fromDegrees(
geometry.topCenter.lon, geometry.topCenter.lon,
geometry.topCenter.lat, geometry.topCenter.lat,
geometry.topCenter.alt + 1.2, geometry.topCenter.alt + 1.2,
@@ -301,26 +302,22 @@ export function createGeometry({ state: layerState, services, parts, source }) {
function frustumFrameEcef(camera, atPos) { function frustumFrameEcef(camera, atPos) {
const h = parts.model.toRad(camera.headingDeg); const h = parts.model.toRad(camera.headingDeg);
const p = parts.model.toRad(camera.pitchDeg); const p = parts.model.toRad(camera.pitchDeg);
const enu = Cesium.Transforms.eastNorthUpToFixedFrame(atPos); const enu = geo.transforms.eastNorthUpToFixedFrame(atPos);
const rot = Cesium.Matrix4.getMatrix3(enu, new Cesium.Matrix3()); const rot = geo.matrix4.getMatrix3(enu, geo.matrix3.create());
// ENU components: view axis + the perpendicular "up" of the pitched cap. // ENU components: view axis + the perpendicular "up" of the pitched cap.
const dirEnu = new Cesium.Cartesian3( const dirEnu = geo.cartesian3.create(
Math.sin(h) * Math.cos(p), Math.sin(h) * Math.cos(p),
Math.cos(h) * Math.cos(p), Math.cos(h) * Math.cos(p),
Math.sin(p), Math.sin(p),
); );
const upEnu = new Cesium.Cartesian3( const upEnu = geo.cartesian3.create(
-Math.sin(p) * Math.sin(h), -Math.sin(p) * Math.sin(h),
-Math.sin(p) * Math.cos(h), -Math.sin(p) * Math.cos(h),
Math.cos(p), Math.cos(p),
); );
return { return {
dir: Cesium.Matrix3.multiplyByVector( dir: geo.matrix3.multiplyByVector(rot, dirEnu, geo.cartesian3.create()),
rot, up: geo.matrix3.multiplyByVector(rot, upEnu, geo.cartesian3.create()),
dirEnu,
new Cesium.Cartesian3(),
),
up: Cesium.Matrix3.multiplyByVector(rot, upEnu, new Cesium.Cartesian3()),
}; };
} }
@@ -462,10 +459,10 @@ export function createGeometry({ state: layerState, services, parts, source }) {
sampleMeshFloorCells(layerState._viewer?.scene, [point], { sampleMeshFloorCells(layerState._viewer?.scene, [point], {
excludeObjects: excludeObjects.filter(Boolean), excludeObjects: excludeObjects.filter(Boolean),
viewerLat: viewerCarto viewerLat: viewerCarto
? Cesium.Math.toDegrees(viewerCarto.latitude) ? geo.math.toDegrees(viewerCarto.latitude)
: undefined, : undefined,
viewerLon: viewerCarto viewerLon: viewerCarto
? Cesium.Math.toDegrees(viewerCarto.longitude) ? geo.math.toDegrees(viewerCarto.longitude)
: undefined, : undefined,
}); });
} }
@@ -623,7 +620,7 @@ export function createGeometry({ state: layerState, services, parts, source }) {
if (Math.abs(rangeScale - 1) > 0.0001) return; // slider overrides the clamp if (Math.abs(rangeScale - 1) > 0.0001) return; // slider overrides the clamp
try { try {
const mountAlt = parts.ground.groundAltFor(record) + camera.mountHeightM; const mountAlt = parts.ground.groundAltFor(record) + camera.mountHeightM;
const mountPos = Cesium.Cartesian3.fromDegrees( const mountPos = geo.cartesian3.fromDegrees(
camera.lon, camera.lon,
camera.lat, camera.lat,
mountAlt, mountAlt,
@@ -637,7 +634,7 @@ export function createGeometry({ state: layerState, services, parts, source }) {
} }
const hit = scene.pickFromRay(new Cesium.Ray(mountPos, dir), exclude); const hit = scene.pickFromRay(new Cesium.Ray(mountPos, dir), exclude);
if (!hit?.position) return; if (!hit?.position) return;
const dist = Cesium.Cartesian3.distance(mountPos, hit.position); const dist = geo.cartesian3.distance(mountPos, hit.position);
record.probeClampRangeM = activationProbeClampRange(camera.rangeM, dist); record.probeClampRangeM = activationProbeClampRange(camera.rangeM, dist);
} catch { } catch {
// probe failure → keep the unclamped range // probe failure → keep the unclamped range
+3 -3
View File
@@ -1,4 +1,4 @@
import * as Cesium from 'cesium'; import * as geo from '../../geo/index.js';
import { import {
GEO_PROGRESS_NOTIFY_INTERVAL_MS, GEO_PROGRESS_NOTIFY_INTERVAL_MS,
GEO_PROGRESS_NOTIFY_BATCH_LIMIT, GEO_PROGRESS_NOTIFY_BATCH_LIMIT,
@@ -298,10 +298,10 @@ export function createGeometryQueue({
const active = parts.selection.getActiveRecord(); const active = parts.selection.getActiveRecord();
const carto = layerState._viewer?.camera?.positionCartographic; const carto = layerState._viewer?.camera?.positionCartographic;
const refLat = carto const refLat = carto
? Cesium.Math.toDegrees(carto.latitude) ? geo.math.toDegrees(carto.latitude)
: (active?.camera.lat ?? 0); : (active?.camera.lat ?? 0);
const refLon = carto const refLon = carto
? Cesium.Math.toDegrees(carto.longitude) ? geo.math.toDegrees(carto.longitude)
: (active?.camera.lon ?? 0); : (active?.camera.lon ?? 0);
const pending = layerState._records const pending = layerState._records
.filter((record) => record !== active) .filter((record) => record !== active)
+23 -20
View File
@@ -1,4 +1,5 @@
import * as Cesium from 'cesium'; import * as Cesium from 'cesium';
import * as geo from '../../geo/index.js';
import { staticFrameRefreshMs } from '../../data/cctvLod.js'; import { staticFrameRefreshMs } from '../../data/cctvLod.js';
import { frameFetchDue, cardFetchPolicy } from '../../data/cctvCards.js'; import { frameFetchDue, cardFetchPolicy } from '../../data/cctvCards.js';
import { DEFAULT_CAMERA_CALIBRATION } from './policy.js'; import { DEFAULT_CAMERA_CALIBRATION } from './policy.js';
@@ -13,7 +14,7 @@ export function createModel({ state: layerState, services, parts, source }) {
*/ */
function toRad(deg) { function toRad(deg) {
return Cesium.Math.toRadians(deg); return geo.math.toRadians(deg);
} }
/** /**
@@ -283,8 +284,8 @@ export function createModel({ state: layerState, services, parts, source }) {
const lon2 = lon1 + Math.atan2(y, x); const lon2 = lon1 + Math.atan2(y, x);
return { return {
lat: Cesium.Math.toDegrees(lat2), lat: geo.math.toDegrees(lat2),
lon: Cesium.Math.toDegrees(lon2), lon: geo.math.toDegrees(lon2),
}; };
} }
@@ -328,8 +329,8 @@ export function createModel({ state: layerState, services, parts, source }) {
function currentViewContext() { function currentViewContext() {
const carto = layerState._viewer?.camera?.positionCartographic; const carto = layerState._viewer?.camera?.positionCartographic;
if (!carto) return 'none'; if (!carto) return 'none';
const lat = Cesium.Math.toDegrees(carto.latitude); const lat = geo.math.toDegrees(carto.latitude);
const lon = Cesium.Math.toDegrees(carto.longitude); const lon = geo.math.toDegrees(carto.longitude);
const alt = carto.height || 0; const alt = carto.height || 0;
const zoomBucket = const zoomBucket =
alt < 1500 alt < 1500
@@ -404,24 +405,26 @@ export function createModel({ state: layerState, services, parts, source }) {
function planeOrientationFor(camera, capCenterPos) { function planeOrientationFor(camera, capCenterPos) {
const frame = parts.geometry.frustumFrameEcef(camera, capCenterPos); const frame = parts.geometry.frustumFrameEcef(camera, capCenterPos);
const right = Cesium.Cartesian3.cross( const right = geo.cartesian3.cross(
frame.dir, frame.dir,
frame.up, frame.up,
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
const normal = Cesium.Cartesian3.negate(frame.dir, new Cesium.Cartesian3()); const normal = geo.cartesian3.negate(frame.dir, geo.cartesian3.create());
const m = new Cesium.Matrix3( // Column-major rotation whose columns are right, up and normal.
right.x, const m = geo.matrix3.create();
frame.up.x, m[0] = right.x;
normal.x, m[1] = right.y;
right.y, m[2] = right.z;
frame.up.y, m[3] = frame.up.x;
normal.y, m[4] = frame.up.y;
right.z, m[5] = frame.up.z;
frame.up.z, m[6] = normal.x;
normal.z, m[7] = normal.y;
); m[8] = normal.z;
return Cesium.Quaternion.fromRotationMatrix(m); // The quaternion becomes an entity's ConstantProperty value, so it stays a
// Cesium Quaternion there (that property clones and compares its value).
return geo.quaternion.fromRotationMatrix(m, new Cesium.Quaternion());
} }
/** /**
+3 -2
View File
@@ -1,4 +1,5 @@
import * as Cesium from 'cesium'; import * as Cesium from 'cesium';
import * as geo from '../../geo/index.js';
import { CCTV_FOCUS_RESULT } from './policy.js'; import { CCTV_FOCUS_RESULT } from './policy.js';
export function createNavigation({ export function createNavigation({
@@ -15,8 +16,8 @@ export function createNavigation({
function nearestCameraIdToViewer() { function nearestCameraIdToViewer() {
const carto = layerState._viewer?.camera?.positionCartographic; const carto = layerState._viewer?.camera?.positionCartographic;
if (!carto || !layerState._records.length) return null; if (!carto || !layerState._records.length) return null;
const lat = Cesium.Math.toDegrees(carto.latitude); const lat = geo.math.toDegrees(carto.latitude);
const lon = Cesium.Math.toDegrees(carto.longitude); const lon = geo.math.toDegrees(carto.longitude);
let best = null; let best = null;
for (const record of layerState._records) { for (const record of layerState._records) {
+6 -5
View File
@@ -1,5 +1,6 @@
import { attachCctvVideo } from './videoPlayback.js'; import { attachCctvVideo } from './videoPlayback.js';
import * as Cesium from 'cesium'; import * as Cesium from 'cesium';
import * as geo from '../../geo/index.js';
import { import {
CCTV_PROJECTION_OVERLAY_SOURCE_ID, CCTV_PROJECTION_OVERLAY_SOURCE_ID,
CCTV_PROJECTION_OVERLAY_SOURCE_OPTIONS, CCTV_PROJECTION_OVERLAY_SOURCE_OPTIONS,
@@ -77,7 +78,7 @@ export function createProjection({
); );
} }
if (runtime.labelPosition) { if (runtime.labelPosition) {
Cesium.Cartesian3.clone(positions.label, runtime.labelPosition); geo.cartesian3.clone(positions.label, runtime.labelPosition);
} }
} }
@@ -124,8 +125,8 @@ export function createProjection({
/** Create the native monitor plane plus its cached host-label presentation. */ /** Create the native monitor plane plus its cached host-label presentation. */
function createProjectionPlane(record, runtime, geometry, positions) { function createProjectionPlane(record, runtime, geometry, positions) {
runtime.labelPosition ||= new Cesium.Cartesian3(); runtime.labelPosition ||= geo.cartesian3.create();
Cesium.Cartesian3.clone(positions.label, runtime.labelPosition); geo.cartesian3.clone(positions.label, runtime.labelPosition);
runtime.cameraId = String(record.camera.id); runtime.cameraId = String(record.camera.id);
runtime.overlayEntry = createCctvProjectionOverlayEntry({ runtime.overlayEntry = createCctvProjectionOverlayEntry({
cameraId: runtime.cameraId, cameraId: runtime.cameraId,
@@ -142,7 +143,7 @@ export function createProjection({
positions.capCenter, positions.capCenter,
), ),
plane: { plane: {
plane: new Cesium.Plane(Cesium.Cartesian3.UNIT_Z, 0.0), plane: new Cesium.Plane(geo.cartesian3.UNIT_Z, 0.0),
dimensions: new Cesium.Cartesian2( dimensions: new Cesium.Cartesian2(
geometry.halfW * 2, geometry.halfW * 2,
geometry.halfH * 2, geometry.halfH * 2,
@@ -186,7 +187,7 @@ export function createProjection({
video: null, video: null,
planeEntity: null, planeEntity: null,
cameraId: String(record.camera.id), cameraId: String(record.camera.id),
labelPosition: new Cesium.Cartesian3(), labelPosition: geo.cartesian3.create(),
overlayEntry: null, overlayEntry: null,
planeMaterial: null, planeMaterial: null,
buffers: null, buffers: null,
+2 -2
View File
@@ -1,4 +1,4 @@
import * as Cesium from 'cesium'; import * as geo from '../../geo/index.js';
export function createState({ services }) { export function createState({ services }) {
const { const {
@@ -74,7 +74,7 @@ export function createState({ services }) {
layerState._activeFocusStyleCount = 0; layerState._activeFocusStyleCount = 0;
layerState._scratchFocusScreen = new Cesium.Cartesian2(); layerState._scratchFocusScreen = geo.cartesian2.create();
// Staggered geometry-load queue state (see startGeometryLoadQueue). // Staggered geometry-load queue state (see startGeometryLoadQueue).
+72 -84
View File
@@ -1,4 +1,5 @@
import * as Cesium from 'cesium'; import * as Cesium from 'cesium';
import * as geo from '../../geo/index.js';
import { import {
REPLAY_ASCENT_CAMERA_OFFSET_RAD, REPLAY_ASCENT_CAMERA_OFFSET_RAD,
REPLAY_ORBIT_CAMERA_OFFSET_RAD, REPLAY_ORBIT_CAMERA_OFFSET_RAD,
@@ -18,12 +19,12 @@ export function createCamera({ state: layerState, services, parts, source }) {
if (!path?.length) return fallback; if (!path?.length) return fallback;
const current = parts.paths.samplePath(path, progress); const current = parts.paths.samplePath(path, progress);
const currentCartographic = const currentCartographic =
Cesium.Ellipsoid.WGS84.cartesianToCartographic(current); geo.ellipsoid.WGS84.cartesianToCartographic(current);
if (!currentCartographic) return fallback; if (!currentCartographic) return fallback;
for (const step of [0.01, 0.025, 0.05, 0.1, 0.2]) { for (const step of [0.01, 0.025, 0.05, 0.1, 0.2]) {
const next = parts.paths.samplePath(path, Math.min(1, progress + step)); const next = parts.paths.samplePath(path, Math.min(1, progress + step));
const nextCartographic = const nextCartographic =
Cesium.Ellipsoid.WGS84.cartesianToCartographic(next); geo.ellipsoid.WGS84.cartesianToCartographic(next);
if (!nextCartographic) continue; if (!nextCartographic) continue;
const geodesic = new Cesium.EllipsoidGeodesic( const geodesic = new Cesium.EllipsoidGeodesic(
currentCartographic, currentCartographic,
@@ -36,7 +37,7 @@ export function createCamera({ state: layerState, services, parts, source }) {
// HeadingPitchRange positions the camera opposite its heading vector. // HeadingPitchRange positions the camera opposite its heading vector.
// Passing the forward path heading therefore keeps the camera behind // Passing the forward path heading therefore keeps the camera behind
// the vehicle, with the remaining ascent receding into the scene. // the vehicle, with the remaining ascent receding into the scene.
return Cesium.Math.zeroToTwoPi(geodesic.startHeading); return geo.math.zeroToTwoPi(geodesic.startHeading);
} }
} }
return fallback; return fallback;
@@ -49,8 +50,8 @@ export function createCamera({ state: layerState, services, parts, source }) {
*/ */
function replayInitialCameraHeading(path) { function replayInitialCameraHeading(path) {
return Cesium.Math.zeroToTwoPi( return geo.math.zeroToTwoPi(
cameraHeadingForPath(path, 0, Math.PI) + Cesium.Math.PI_OVER_TWO, cameraHeadingForPath(path, 0, Math.PI) + geo.math.PI_OVER_TWO,
); );
} }
@@ -63,10 +64,10 @@ export function createCamera({ state: layerState, services, parts, source }) {
*/ */
function replayChaseCameraHeading(pathHeading, orbitBlend = 0) { function replayChaseCameraHeading(pathHeading, orbitBlend = 0) {
const blend = Cesium.Math.clamp(Number(orbitBlend) || 0, 0, 1); const blend = geo.math.clamp(Number(orbitBlend) || 0, 0, 1);
return Cesium.Math.zeroToTwoPi( return geo.math.zeroToTwoPi(
pathHeading + pathHeading +
Cesium.Math.lerp( geo.math.lerp(
REPLAY_ASCENT_CAMERA_OFFSET_RAD, REPLAY_ASCENT_CAMERA_OFFSET_RAD,
REPLAY_ORBIT_CAMERA_OFFSET_RAD, REPLAY_ORBIT_CAMERA_OFFSET_RAD,
blend, blend,
@@ -86,17 +87,17 @@ export function createCamera({ state: layerState, services, parts, source }) {
function smoothReplayCameraHeading( function smoothReplayCameraHeading(
previous, previous,
desired, desired,
maxStepRad = Cesium.Math.toRadians(2), maxStepRad = geo.math.toRadians(2),
) { ) {
if (!Number.isFinite(previous)) return Cesium.Math.zeroToTwoPi(desired); if (!Number.isFinite(previous)) return geo.math.zeroToTwoPi(desired);
if (!Number.isFinite(desired)) return Cesium.Math.zeroToTwoPi(previous); if (!Number.isFinite(desired)) return geo.math.zeroToTwoPi(previous);
const delta = Cesium.Math.negativePiToPi(desired - previous); const delta = geo.math.negativePiToPi(desired - previous);
const step = Cesium.Math.clamp( const step = geo.math.clamp(
delta, delta,
-Math.abs(maxStepRad), -Math.abs(maxStepRad),
Math.abs(maxStepRad), Math.abs(maxStepRad),
); );
return Cesium.Math.zeroToTwoPi(previous + step); return geo.math.zeroToTwoPi(previous + step);
} }
/** /**
@@ -108,7 +109,7 @@ export function createCamera({ state: layerState, services, parts, source }) {
function missionZoomPitch(rangeM) { function missionZoomPitch(rangeM) {
const range = Math.max(0, Number(rangeM) || 0); const range = Math.max(0, Number(rangeM) || 0);
const blend = Cesium.Math.clamp( const blend = geo.math.clamp(
(Math.log(Math.max(range, MISSION_CLOSE_VIEW_RANGE_M)) - (Math.log(Math.max(range, MISSION_CLOSE_VIEW_RANGE_M)) -
Math.log(MISSION_CLOSE_VIEW_RANGE_M)) / Math.log(MISSION_CLOSE_VIEW_RANGE_M)) /
(Math.log(MISSION_GLOBE_VIEW_RANGE_M) - (Math.log(MISSION_GLOBE_VIEW_RANGE_M) -
@@ -116,11 +117,7 @@ export function createCamera({ state: layerState, services, parts, source }) {
0, 0,
1, 1,
); );
return Cesium.Math.lerp( return geo.math.lerp(geo.math.toRadians(-42), -geo.math.PI_OVER_TWO, blend);
Cesium.Math.toRadians(-42),
-Cesium.Math.PI_OVER_TWO,
blend,
);
} }
/** /**
@@ -133,48 +130,44 @@ export function createCamera({ state: layerState, services, parts, source }) {
function replayCameraView(state, altitudeM) { function replayCameraView(state, altitudeM) {
const altitude = Math.max(0, Number(altitudeM) || 0); const altitude = Math.max(0, Number(altitudeM) || 0);
const localRange = Cesium.Math.clamp( const localRange = geo.math.clamp(
REPLAY_INITIAL_RANGE_M + altitude * 0.7, REPLAY_INITIAL_RANGE_M + altitude * 0.7,
REPLAY_INITIAL_RANGE_M, REPLAY_INITIAL_RANGE_M,
REPLAY_LOCAL_MAX_RANGE_M, REPLAY_LOCAL_MAX_RANGE_M,
); );
const rawContextBlend = Cesium.Math.clamp( const rawContextBlend = geo.math.clamp(
(altitude - 20000) / (REPLAY_CONTEXT_ALTITUDE_END_M - 20000), (altitude - 20000) / (REPLAY_CONTEXT_ALTITUDE_END_M - 20000),
0, 0,
1, 1,
); );
const contextBlend = const contextBlend =
rawContextBlend * rawContextBlend * (3 - 2 * rawContextBlend); rawContextBlend * rawContextBlend * (3 - 2 * rawContextBlend);
const contextRange = Cesium.Math.clamp( const contextRange = geo.math.clamp(
180000 + altitude * 3.8, 180000 + altitude * 3.8,
MISSION_CLOSE_VIEW_RANGE_M, MISSION_CLOSE_VIEW_RANGE_M,
REPLAY_CONTEXT_MAX_RANGE_M, REPLAY_CONTEXT_MAX_RANGE_M,
); );
const ascentRange = Cesium.Math.lerp( const ascentRange = geo.math.lerp(localRange, contextRange, contextBlend);
localRange, const ascentPitch = geo.math.lerp(
contextRange, geo.math.toRadians(-20),
contextBlend, geo.math.toRadians(-34),
);
const ascentPitch = Cesium.Math.lerp(
Cesium.Math.toRadians(-20),
Cesium.Math.toRadians(-34),
contextBlend, contextBlend,
); );
if (state?.ascending) { if (state?.ascending) {
return { range: ascentRange, pitch: ascentPitch }; return { range: ascentRange, pitch: ascentPitch };
} }
const rawBlend = Cesium.Math.clamp( const rawBlend = geo.math.clamp(
(Number(state?.phaseProgress) || 0) / REPLAY_ORBIT_PULLBACK_FRACTION, (Number(state?.phaseProgress) || 0) / REPLAY_ORBIT_PULLBACK_FRACTION,
0, 0,
1, 1,
); );
const blend = rawBlend * rawBlend * (3 - 2 * rawBlend); const blend = rawBlend * rawBlend * (3 - 2 * rawBlend);
return { return {
range: Cesium.Math.lerp(ascentRange, REPLAY_ORBIT_GLOBE_RANGE_M, blend), range: geo.math.lerp(ascentRange, REPLAY_ORBIT_GLOBE_RANGE_M, blend),
// Keep an oblique tactical view of the complete orbit rather than ending // Keep an oblique tactical view of the complete orbit rather than ending
// in a nadir view. This also prevents the camera from appearing to // in a nadir view. This also prevents the camera from appearing to
// return toward the launch site after insertion. // return toward the launch site after insertion.
pitch: Cesium.Math.lerp(ascentPitch, Cesium.Math.toRadians(-45), blend), pitch: geo.math.lerp(ascentPitch, geo.math.toRadians(-45), blend),
}; };
} }
@@ -188,16 +181,15 @@ export function createCamera({ state: layerState, services, parts, source }) {
function replayOrbitGlobeAnchor(position, orbitBlend = 0) { function replayOrbitGlobeAnchor(position, orbitBlend = 0) {
if (!position) return position; if (!position) return position;
const blend = Cesium.Math.clamp(Number(orbitBlend) || 0, 0, 1); const blend = geo.math.clamp(Number(orbitBlend) || 0, 0, 1);
if (blend <= 0) return Cesium.Cartesian3.clone(position); if (blend <= 0) return geo.cartesian3.clone(position);
const cartographic = const cartographic = geo.ellipsoid.WGS84.cartesianToCartographic(position);
Cesium.Ellipsoid.WGS84.cartesianToCartographic(position); if (!cartographic) return geo.cartesian3.clone(position);
if (!cartographic) return Cesium.Cartesian3.clone(position);
const altitude = Math.max(0, cartographic.height || 0); const altitude = Math.max(0, cartographic.height || 0);
return Cesium.Cartesian3.fromRadians( return geo.cartesian3.fromRadians(
cartographic.longitude, cartographic.longitude,
cartographic.latitude, cartographic.latitude,
Cesium.Math.lerp(altitude, altitude * 0.1, blend), geo.math.lerp(altitude, altitude * 0.1, blend),
); );
} }
@@ -216,15 +208,15 @@ export function createCamera({ state: layerState, services, parts, source }) {
function replayOrbitCameraTarget(vehicleAnchor, frameCenter, orbitBlend = 0) { function replayOrbitCameraTarget(vehicleAnchor, frameCenter, orbitBlend = 0) {
if (!vehicleAnchor) return vehicleAnchor; if (!vehicleAnchor) return vehicleAnchor;
if (!frameCenter) return Cesium.Cartesian3.clone(vehicleAnchor); if (!frameCenter) return geo.cartesian3.clone(vehicleAnchor);
const blend = const blend =
Cesium.Math.clamp(Number(orbitBlend) || 0, 0, 1) * geo.math.clamp(Number(orbitBlend) || 0, 0, 1) *
REPLAY_ORBIT_FRAME_CENTER_BLEND; REPLAY_ORBIT_FRAME_CENTER_BLEND;
return Cesium.Cartesian3.lerp( return geo.cartesian3.lerp(
vehicleAnchor, vehicleAnchor,
frameCenter, frameCenter,
blend, blend,
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
} }
@@ -249,63 +241,59 @@ export function createCamera({ state: layerState, services, parts, source }) {
pitch, pitch,
) { ) {
if (!position || !tangentPosition || !target) return null; if (!position || !tangentPosition || !target) return null;
const radial = Cesium.Cartesian3.normalize( const radial = geo.cartesian3.normalize(
Cesium.Cartesian3.clone(position), geo.cartesian3.clone(position),
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
const tangent = Cesium.Cartesian3.subtract( const tangent = geo.cartesian3.subtract(
tangentPosition, tangentPosition,
position, position,
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
if (Cesium.Cartesian3.magnitudeSquared(tangent) < 1) return null; if (geo.cartesian3.magnitudeSquared(tangent) < 1) return null;
Cesium.Cartesian3.normalize(tangent, tangent); geo.cartesian3.normalize(tangent, tangent);
const orbitNormal = Cesium.Cartesian3.cross( const orbitNormal = geo.cartesian3.cross(
radial, radial,
tangent, tangent,
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
if (Cesium.Cartesian3.magnitudeSquared(orbitNormal) < 1e-12) return null; if (geo.cartesian3.magnitudeSquared(orbitNormal) < 1e-12) return null;
Cesium.Cartesian3.normalize(orbitNormal, orbitNormal); geo.cartesian3.normalize(orbitNormal, orbitNormal);
const distance = Math.max(1, Number(range) || 1); const distance = Math.max(1, Number(range) || 1);
const elevation = Cesium.Math.clamp( const elevation = geo.math.clamp(
Math.abs(Number(pitch) || 0), Math.abs(Number(pitch) || 0),
Cesium.Math.toRadians(5), geo.math.toRadians(5),
Cesium.Math.toRadians(80), geo.math.toRadians(80),
); );
const planeOffset = Cesium.Cartesian3.multiplyByScalar( const planeOffset = geo.cartesian3.multiplyByScalar(
orbitNormal, orbitNormal,
Math.cos(elevation) * distance, Math.cos(elevation) * distance,
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
const radialOffset = Cesium.Cartesian3.multiplyByScalar( const radialOffset = geo.cartesian3.multiplyByScalar(
radial, radial,
Math.sin(elevation) * distance, Math.sin(elevation) * distance,
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
const destination = Cesium.Cartesian3.add( const destination = geo.cartesian3.add(
target, target,
planeOffset, planeOffset,
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
Cesium.Cartesian3.add(destination, radialOffset, destination); geo.cartesian3.add(destination, radialOffset, destination);
const direction = Cesium.Cartesian3.normalize( const direction = geo.cartesian3.normalize(
Cesium.Cartesian3.subtract(target, destination, new Cesium.Cartesian3()), geo.cartesian3.subtract(target, destination, geo.cartesian3.create()),
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
const radialAlongView = Cesium.Cartesian3.multiplyByScalar( const radialAlongView = geo.cartesian3.multiplyByScalar(
direction, direction,
Cesium.Cartesian3.dot(radial, direction), geo.cartesian3.dot(radial, direction),
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
const up = Cesium.Cartesian3.normalize( const up = geo.cartesian3.normalize(
Cesium.Cartesian3.subtract( geo.cartesian3.subtract(radial, radialAlongView, geo.cartesian3.create()),
radial, geo.cartesian3.create(),
radialAlongView,
new Cesium.Cartesian3(),
),
new Cesium.Cartesian3(),
); );
return { destination, direction, up }; return { destination, direction, up };
} }
@@ -321,8 +309,8 @@ export function createCamera({ state: layerState, services, parts, source }) {
function replayOrbitFrameSphere(orbitPath = []) { function replayOrbitFrameSphere(orbitPath = []) {
const earth = new Cesium.BoundingSphere( const earth = new Cesium.BoundingSphere(
Cesium.Cartesian3.ZERO, geo.cartesian3.ZERO,
Cesium.Ellipsoid.WGS84.maximumRadius, geo.ellipsoid.WGS84.maximumRadius,
); );
if (!Array.isArray(orbitPath) || orbitPath.length < 2) return earth; if (!Array.isArray(orbitPath) || orbitPath.length < 2) return earth;
const orbit = Cesium.BoundingSphere.fromPoints(orbitPath); const orbit = Cesium.BoundingSphere.fromPoints(orbitPath);
@@ -351,13 +339,13 @@ export function createCamera({ state: layerState, services, parts, source }) {
const range = Math.max(0, Number(baseRange) || 0); const range = Math.max(0, Number(baseRange) || 0);
const altitude = Math.max(0, Number(altitudeM) || 0); const altitude = Math.max(0, Number(altitudeM) || 0);
const frameRadius = Math.max(0, Number(frameRadiusM) || 0); const frameRadius = Math.max(0, Number(frameRadiusM) || 0);
const blend = Cesium.Math.clamp(Number(orbitBlend) || 0, 0, 1); const blend = geo.math.clamp(Number(orbitBlend) || 0, 0, 1);
const globeAndVehicleRange = Math.max( const globeAndVehicleRange = Math.max(
range, range,
altitude + Cesium.Ellipsoid.WGS84.maximumRadius * 2.4, altitude + geo.ellipsoid.WGS84.maximumRadius * 2.4,
frameRadius * 3, frameRadius * 3,
); );
return Cesium.Math.lerp(range, globeAndVehicleRange, blend); return geo.math.lerp(range, globeAndVehicleRange, blend);
} }
return { return {
cameraHeadingForPath, cameraHeadingForPath,
+2 -1
View File
@@ -1,4 +1,5 @@
import * as Cesium from 'cesium'; import * as Cesium from 'cesium';
import * as geo from '../../geo/index.js';
import { import {
MISSION_CLOSE_VIEW_RANGE_M, MISSION_CLOSE_VIEW_RANGE_M,
MISSION_GLOBE_VIEW_RANGE_M, MISSION_GLOBE_VIEW_RANGE_M,
@@ -360,7 +361,7 @@ export function createPanel({ state: layerState, services, parts, source }) {
Cesium.JulianDate.now(layerState._declutterTime), Cesium.JulianDate.now(layerState._declutterTime),
); );
const altitudeM = position const altitudeM = position
? Cesium.Cartographic.fromCartesian(position)?.height ? geo.cartographic.fromCartesian(position)?.height
: null; : null;
setMissionPanelField( setMissionPanelField(
'[data-mission-distance]', '[data-mission-distance]',
+4 -4
View File
@@ -1,4 +1,4 @@
import * as Cesium from 'cesium'; import * as geo from '../../geo/index.js';
export const WINDOW_DAYS = 30; export const WINDOW_DAYS = 30;
@@ -48,9 +48,9 @@ export const REPLAY_ORBIT_GLOBE_RANGE_M = 18000000;
export const REPLAY_ORBIT_PULLBACK_FRACTION = 0.2; export const REPLAY_ORBIT_PULLBACK_FRACTION = 0.2;
export const REPLAY_ASCENT_CAMERA_OFFSET_RAD = Cesium.Math.toRadians(30); export const REPLAY_ASCENT_CAMERA_OFFSET_RAD = geo.math.toRadians(30);
export const REPLAY_ORBIT_CAMERA_OFFSET_RAD = Cesium.Math.toRadians(45); export const REPLAY_ORBIT_CAMERA_OFFSET_RAD = geo.math.toRadians(45);
export const REPLAY_ORBIT_FRAME_CENTER_BLEND = 0.45; export const REPLAY_ORBIT_FRAME_CENTER_BLEND = 0.45;
@@ -64,7 +64,7 @@ export const MAX_POST_TLE_RETRIES = 1;
export const POST_TLE_RETRY_DELAY_MS = 1500; export const POST_TLE_RETRY_DELAY_MS = 1500;
export const EARTH_ROTATION_RAD_PER_SEC = Cesium.Math.TWO_PI / 86164.0905; export const EARTH_ROTATION_RAD_PER_SEC = geo.math.TWO_PI / 86164.0905;
export const PROJECTED_ASCENT_ROTATION_SEC = 600; export const PROJECTED_ASCENT_ROTATION_SEC = 600;
+3 -6
View File
@@ -1,4 +1,5 @@
import * as Cesium from 'cesium'; import * as Cesium from 'cesium';
import * as geo from '../../geo/index.js';
import { import {
SATELLITE_STANDALONE_DEFAULTS, SATELLITE_STANDALONE_DEFAULTS,
LAUNCH_PAD_ZONE_MAX_CAMERA_HEIGHT_M, LAUNCH_PAD_ZONE_MAX_CAMERA_HEIGHT_M,
@@ -226,15 +227,11 @@ export function createPolicyHelpers({
const range = parts.panel.missionHoverPreviewRange( const range = parts.panel.missionHoverPreviewRange(
layerState._viewer.camera.positionCartographic?.height, layerState._viewer.camera.positionCartographic?.height,
); );
const position = Cesium.Cartesian3.fromDegrees(launch.lon, launch.lat); const position = geo.cartesian3.fromDegrees(launch.lon, launch.lat);
layerState._viewer.camera.flyToBoundingSphere( layerState._viewer.camera.flyToBoundingSphere(
new Cesium.BoundingSphere(position, 0), new Cesium.BoundingSphere(position, 0),
{ {
offset: new Cesium.HeadingPitchRange( offset: new Cesium.HeadingPitchRange(0, -geo.math.PI_OVER_TWO, range),
0,
-Cesium.Math.PI_OVER_TWO,
range,
),
duration: 0.8, duration: 0.8,
easingFunction: Cesium.EasingFunction.CUBIC_IN_OUT, easingFunction: Cesium.EasingFunction.CUBIC_IN_OUT,
}, },
+12 -15
View File
@@ -1,4 +1,5 @@
import * as Cesium from 'cesium'; import * as Cesium from 'cesium';
import * as geo from '../../geo/index.js';
import { import {
REPLAY_ASCENT_FALLBACK_SEC, REPLAY_ASCENT_FALLBACK_SEC,
REPLAY_ASCENT_MIN_SEC, REPLAY_ASCENT_MIN_SEC,
@@ -52,10 +53,10 @@ export function createReplay({ state: layerState, services, parts, source }) {
if (!previous || !next) return next; if (!previous || !next) return next;
const distance = Math.hypot(next.x - previous.x, next.y - previous.y); const distance = Math.hypot(next.x - previous.x, next.y - previous.y);
if (!Number.isFinite(distance) || distance > snapDistance) return next; if (!Number.isFinite(distance) || distance > snapDistance) return next;
const amount = Cesium.Math.clamp(Number(alpha) || 0, 0, 1); const amount = geo.math.clamp(Number(alpha) || 0, 0, 1);
return { return {
x: Cesium.Math.lerp(previous.x, next.x, amount), x: geo.math.lerp(previous.x, next.x, amount),
y: Cesium.Math.lerp(previous.y, next.y, amount), y: geo.math.lerp(previous.y, next.y, amount),
}; };
} }
@@ -77,13 +78,13 @@ export function createReplay({ state: layerState, services, parts, source }) {
.slice(1) .slice(1)
.reduce( .reduce(
(total, point, index) => (total, point, index) =>
total + Cesium.Cartesian3.distance(ascentPath[index], point), total + geo.cartesian3.distance(ascentPath[index], point),
0, 0,
); );
realAscentSeconds = Math.max(180, pathLength / 9000); realAscentSeconds = Math.max(180, pathLength / 9000);
} }
if (!realAscentSeconds) realAscentSeconds = REPLAY_ASCENT_FALLBACK_SEC * 50; if (!realAscentSeconds) realAscentSeconds = REPLAY_ASCENT_FALLBACK_SEC * 50;
return Cesium.Math.clamp( return geo.math.clamp(
REPLAY_ASCENT_FALLBACK_SEC * (realAscentSeconds / 600), REPLAY_ASCENT_FALLBACK_SEC * (realAscentSeconds / 600),
REPLAY_ASCENT_MIN_SEC, REPLAY_ASCENT_MIN_SEC,
REPLAY_ASCENT_MAX_SEC, REPLAY_ASCENT_MAX_SEC,
@@ -99,11 +100,7 @@ export function createReplay({ state: layerState, services, parts, source }) {
function normalizeReplaySpeed(value) { function normalizeReplaySpeed(value) {
const numeric = Number(value); const numeric = Number(value);
if (!Number.isFinite(numeric)) return 1; if (!Number.isFinite(numeric)) return 1;
const clamped = Cesium.Math.clamp( const clamped = geo.math.clamp(numeric, REPLAY_SPEED_MIN, REPLAY_SPEED_MAX);
numeric,
REPLAY_SPEED_MIN,
REPLAY_SPEED_MAX,
);
return Math.round(clamped / REPLAY_SPEED_STEP) * REPLAY_SPEED_STEP; return Math.round(clamped / REPLAY_SPEED_STEP) * REPLAY_SPEED_STEP;
} }
@@ -414,7 +411,7 @@ export function createReplay({ state: layerState, services, parts, source }) {
track.lastCameraHeading, track.lastCameraHeading,
); );
const orbitBlend = !state.ascending const orbitBlend = !state.ascending
? Cesium.Math.clamp( ? geo.math.clamp(
(Number(state.phaseProgress) || 0) / (Number(state.phaseProgress) || 0) /
REPLAY_ORBIT_PULLBACK_FRACTION, REPLAY_ORBIT_PULLBACK_FRACTION,
0, 0,
@@ -428,16 +425,16 @@ export function createReplay({ state: layerState, services, parts, source }) {
if (state.ascending) track.orbitCameraWorldFrame = false; if (state.ascending) track.orbitCameraWorldFrame = false;
const nowMs = performance.now(); const nowMs = performance.now();
const frameDurationMs = Number.isFinite(lastCameraUpdateMs) const frameDurationMs = Number.isFinite(lastCameraUpdateMs)
? Cesium.Math.clamp(nowMs - lastCameraUpdateMs, 4, 50) ? geo.math.clamp(nowMs - lastCameraUpdateMs, 4, 50)
: 1000 / 60; : 1000 / 60;
lastCameraUpdateMs = nowMs; lastCameraUpdateMs = nowMs;
track.lastCameraHeading = parts.camera.smoothReplayCameraHeading( track.lastCameraHeading = parts.camera.smoothReplayCameraHeading(
track.lastCameraHeading, track.lastCameraHeading,
desiredHeading, desiredHeading,
(Cesium.Math.toRadians(2) * frameDurationMs) / (1000 / 60), (geo.math.toRadians(2) * frameDurationMs) / (1000 / 60),
); );
const cartographic = const cartographic =
Cesium.Ellipsoid.WGS84.cartesianToCartographic(position); geo.ellipsoid.WGS84.cartesianToCartographic(position);
const altitude = Math.max(0, cartographic?.height || 0); const altitude = Math.max(0, cartographic?.height || 0);
const cameraView = parts.camera.replayCameraView(state, altitude); const cameraView = parts.camera.replayCameraView(state, altitude);
const defaultOrbitTarget = !state.ascending const defaultOrbitTarget = !state.ascending
@@ -518,7 +515,7 @@ export function createReplay({ state: layerState, services, parts, source }) {
{ {
offset: new Cesium.HeadingPitchRange( offset: new Cesium.HeadingPitchRange(
initialHeading, initialHeading,
Cesium.Math.toRadians(-20), geo.math.toRadians(-20),
REPLAY_INITIAL_RANGE_M, REPLAY_INITIAL_RANGE_M,
), ),
duration: 1.4, duration: 1.4,
+27 -36
View File
@@ -1,4 +1,5 @@
import * as Cesium from 'cesium'; import * as Cesium from 'cesium';
import * as geo from '../../geo/index.js';
import { MISSION_GLOBE_VIEW_RANGE_M, MISSION_FOCUS_RANGE_M } from './policy.js'; import { MISSION_GLOBE_VIEW_RANGE_M, MISSION_FOCUS_RANGE_M } from './policy.js';
export function createSelection({ export function createSelection({
@@ -24,7 +25,7 @@ export function createSelection({
fovy >= Math.PI fovy >= Math.PI
) )
return; return;
const earthRadius = Cesium.Ellipsoid.WGS84.maximumRadius; const earthRadius = geo.ellipsoid.WGS84.maximumRadius;
const keyholeRadius = getKeyholeGeometry(width, height).radius; const keyholeRadius = getKeyholeGeometry(width, height).radius;
const targetScreenRadius = keyholeRadius * 0.61; const targetScreenRadius = keyholeRadius * 0.61;
const angularRadius = Math.atan( const angularRadius = Math.atan(
@@ -33,14 +34,14 @@ export function createSelection({
const distance = earthRadius / Math.max(Math.sin(angularRadius), 1e-4); const distance = earthRadius / Math.max(Math.sin(angularRadius), 1e-4);
const altitude = Math.max(earthRadius * 1.55, distance - earthRadius); const altitude = Math.max(earthRadius * 1.55, distance - earthRadius);
viewer.camera.flyTo({ viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromRadians( destination: geo.cartesian3.fromRadians(
cartographic.longitude, cartographic.longitude,
cartographic.latitude, cartographic.latitude,
altitude, altitude,
), ),
orientation: { orientation: {
heading: viewer.camera.heading, heading: viewer.camera.heading,
pitch: -Cesium.Math.PI_OVER_TWO, pitch: -geo.math.PI_OVER_TWO,
roll: 0, roll: 0,
}, },
duration, duration,
@@ -99,7 +100,7 @@ export function createSelection({
if (!layerState._viewer || !launch || layerState._replayCameraLaunchId) if (!layerState._viewer || !launch || layerState._replayCameraLaunchId)
return; return;
stopMissionZoomAnchor(); stopMissionZoomAnchor();
const target = Cesium.Cartesian3.fromDegrees(launch.lon, launch.lat); const target = geo.cartesian3.fromDegrees(launch.lon, launch.lat);
const range = Math.max( const range = Math.max(
2000, 2000,
Number(initialRange) || MISSION_GLOBE_VIEW_RANGE_M, Number(initialRange) || MISSION_GLOBE_VIEW_RANGE_M,
@@ -127,15 +128,12 @@ export function createSelection({
const orbitPositions = orbitEntity?.polyline?.positions?.getValue( const orbitPositions = orbitEntity?.polyline?.positions?.getValue(
Cesium.JulianDate.now(), Cesium.JulianDate.now(),
); );
const launchPosition = Cesium.Cartesian3.fromDegrees( const launchPosition = geo.cartesian3.fromDegrees(launch.lon, launch.lat);
launch.lon,
launch.lat,
);
let range = 18000000; let range = 18000000;
if (orbitPositions?.length > 1) { if (orbitPositions?.length > 1) {
const canvas = layerState._viewer.scene.canvas; const canvas = layerState._viewer.scene.canvas;
const fovy = const fovy =
layerState._viewer.camera.frustum?.fovy || Cesium.Math.toRadians(60); layerState._viewer.camera.frustum?.fovy || geo.math.toRadians(60);
const aspect = Math.max( const aspect = Math.max(
0.1, 0.1,
(canvas.clientWidth || canvas.width) / (canvas.clientWidth || canvas.width) /
@@ -145,7 +143,7 @@ export function createSelection({
const paddedHalfAngle = Math.min(fovy, fovx) * 0.5 * 0.62; const paddedHalfAngle = Math.min(fovy, fovx) * 0.5 * 0.62;
const orbitExtent = orbitPositions.reduce( const orbitExtent = orbitPositions.reduce(
(largest, point) => (largest, point) =>
Math.max(largest, Cesium.Cartesian3.distance(launchPosition, point)), Math.max(largest, geo.cartesian3.distance(launchPosition, point)),
0, 0,
); );
range = Math.max( range = Math.max(
@@ -153,39 +151,35 @@ export function createSelection({
18000000, 18000000,
); );
} }
const targetNormal = Cesium.Cartesian3.normalize( const targetNormal = geo.cartesian3.normalize(
launchPosition, launchPosition,
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
const destination = Cesium.Cartesian3.add( const destination = geo.cartesian3.add(
launchPosition, launchPosition,
Cesium.Cartesian3.multiplyByScalar( geo.cartesian3.multiplyByScalar(
targetNormal, targetNormal,
range, range,
new Cesium.Cartesian3(), geo.cartesian3.create(),
), ),
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
const direction = Cesium.Cartesian3.negate( const direction = geo.cartesian3.negate(
targetNormal, targetNormal,
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
let right = Cesium.Cartesian3.cross( let right = geo.cartesian3.cross(
direction, direction,
Cesium.Cartesian3.UNIT_Z, geo.cartesian3.UNIT_Z,
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
if (Cesium.Cartesian3.magnitudeSquared(right) < 1e-8) { if (geo.cartesian3.magnitudeSquared(right) < 1e-8) {
right = Cesium.Cartesian3.cross( right = geo.cartesian3.cross(direction, geo.cartesian3.UNIT_Y, right);
direction,
Cesium.Cartesian3.UNIT_Y,
right,
);
} }
Cesium.Cartesian3.normalize(right, right); geo.cartesian3.normalize(right, right);
const up = Cesium.Cartesian3.normalize( const up = geo.cartesian3.normalize(
Cesium.Cartesian3.cross(right, direction, new Cesium.Cartesian3()), geo.cartesian3.cross(right, direction, geo.cartesian3.create()),
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
layerState._viewer.camera.flyTo({ layerState._viewer.camera.flyTo({
destination, destination,
@@ -202,10 +196,7 @@ export function createSelection({
if (!layerState._viewer || !launch) return; if (!layerState._viewer || !launch) return;
parts.replay.stopMissionReplay(); parts.replay.stopMissionReplay();
stopMissionZoomAnchor(); stopMissionZoomAnchor();
const launchPosition = Cesium.Cartesian3.fromDegrees( const launchPosition = geo.cartesian3.fromDegrees(launch.lon, launch.lat);
launch.lon,
launch.lat,
);
layerState._viewer.selectedEntity = layerState._dataSource.entities.getById( layerState._viewer.selectedEntity = layerState._dataSource.entities.getById(
`rocket-launch:${launch.id}`, `rocket-launch:${launch.id}`,
); );
@@ -214,7 +205,7 @@ export function createSelection({
{ {
offset: new Cesium.HeadingPitchRange( offset: new Cesium.HeadingPitchRange(
0, 0,
Cesium.Math.toRadians(-42), geo.math.toRadians(-42),
MISSION_FOCUS_RANGE_M, MISSION_FOCUS_RANGE_M,
), ),
duration: 1.4, duration: 1.4,
+2 -1
View File
@@ -1,4 +1,5 @@
import * as Cesium from 'cesium'; import * as Cesium from 'cesium';
import * as geo from '../../geo/index.js';
export function createState({ services }) { export function createState({ services }) {
const { setOverlayEntries, setOverlaySourceVisible, clearOverlaySource } = const { setOverlayEntries, setOverlaySourceVisible, clearOverlaySource } =
@@ -125,7 +126,7 @@ export function createState({ services }) {
state._declutterOccluder = new Cesium.EllipsoidalOccluder( state._declutterOccluder = new Cesium.EllipsoidalOccluder(
Cesium.Ellipsoid.WGS84, Cesium.Ellipsoid.WGS84,
new Cesium.Cartesian3(), geo.cartesian3.create(),
); );
state._missionRingDate = new Date(0); state._missionRingDate = new Date(0);