bruce-cesium 7.7.6 → 7.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bruce-cesium.es5.js +2313 -69
- package/dist/bruce-cesium.es5.js.map +1 -1
- package/dist/bruce-cesium.umd.js +2292 -66
- package/dist/bruce-cesium.umd.js.map +1 -1
- package/dist/lib/bruce-cesium.js +6 -1
- package/dist/lib/bruce-cesium.js.map +1 -1
- package/dist/lib/rendering/entity-render-engine-model3d.js +2 -2
- package/dist/lib/rendering/entity-render-engine-model3d.js.map +1 -1
- package/dist/lib/rendering/entity-render-engine-polygon.js +26 -4
- package/dist/lib/rendering/entity-render-engine-polygon.js.map +1 -1
- package/dist/lib/rendering/entity-render-engine-polyline.js +2 -1
- package/dist/lib/rendering/entity-render-engine-polyline.js.map +1 -1
- package/dist/lib/rendering/render-managers/common/entity-simulation.js +1 -1
- package/dist/lib/rendering/render-managers/common/entity-simulation.js.map +1 -1
- package/dist/lib/rendering/tileset-render-engine.js +47 -14
- package/dist/lib/rendering/tileset-render-engine.js.map +1 -1
- package/dist/lib/rendering/view-render-engine.js +30 -24
- package/dist/lib/rendering/view-render-engine.js.map +1 -1
- package/dist/lib/rendering/visuals-register.js +93 -8
- package/dist/lib/rendering/visuals-register.js.map +1 -1
- package/dist/lib/rendering/xgrids-render-engine.js +20 -3
- package/dist/lib/rendering/xgrids-render-engine.js.map +1 -1
- package/dist/lib/utils/cesium-map-grid.js +807 -0
- package/dist/lib/utils/cesium-map-grid.js.map +1 -0
- package/dist/lib/utils/device-profile.js +302 -0
- package/dist/lib/utils/device-profile.js.map +1 -0
- package/dist/lib/utils/gltf-generator.js +307 -0
- package/dist/lib/utils/gltf-generator.js.map +1 -0
- package/dist/lib/utils/gltf-gizmo.js +136 -0
- package/dist/lib/utils/gltf-gizmo.js.map +1 -0
- package/dist/lib/utils/view-utils.js +9 -0
- package/dist/lib/utils/view-utils.js.map +1 -1
- package/dist/lib/viewer/camera-controls.js +544 -0
- package/dist/lib/viewer/camera-controls.js.map +1 -0
- package/dist/lib/viewer/viewer-utils.js +9 -1
- package/dist/lib/viewer/viewer-utils.js.map +1 -1
- package/dist/types/bruce-cesium.d.ts +6 -1
- package/dist/types/rendering/tileset-render-engine.d.ts +10 -2
- package/dist/types/rendering/view-render-engine.d.ts +4 -0
- package/dist/types/rendering/visuals-register.d.ts +4 -0
- package/dist/types/rendering/xgrids-render-engine.d.ts +2 -0
- package/dist/types/utils/cesium-map-grid.d.ts +23 -0
- package/dist/types/utils/device-profile.d.ts +84 -0
- package/dist/types/utils/gltf-generator.d.ts +104 -0
- package/dist/types/utils/gltf-gizmo.d.ts +18 -0
- package/dist/types/utils/view-utils.d.ts +4 -0
- package/dist/types/viewer/camera-controls.d.ts +19 -0
- package/dist/types/viewer/viewer-utils.d.ts +1 -0
- package/package.json +3 -2
package/dist/bruce-cesium.umd.js
CHANGED
|
@@ -907,6 +907,542 @@
|
|
|
907
907
|
return true;
|
|
908
908
|
}
|
|
909
909
|
|
|
910
|
+
const CESIUM_CAMERA_CONTROLS_KEY = "_nextspace_camera_controls";
|
|
911
|
+
// How far a wheel notch or its drag equivalent moves the camera, as a share of the distance to what it points at.
|
|
912
|
+
const ZOOM_SHARE = 0.18;
|
|
913
|
+
// The distance a zoom step treats anything closer as, so the camera keeps moving through a surface instead of stalling.
|
|
914
|
+
const MIN_ZOOM_DISTANCE = 4;
|
|
915
|
+
// How far from the origin zooming out may take a model-space camera, beyond which the scene is a dot on a planet.
|
|
916
|
+
const MODEL_SPACE_MAX_RANGE = 250000;
|
|
917
|
+
// Above this height a geospatial scene is a planet, which CesiumJS's own controller navigates better.
|
|
918
|
+
const GEO_HANDOFF_HEIGHT = 15000;
|
|
919
|
+
// The furthest a depth pick is trusted from a camera close to the ground; further up the limit grows with height.
|
|
920
|
+
const PLAUSIBLE_PICK_FLOOR = 1000;
|
|
921
|
+
// Pixels of right drag that make one wheel notch.
|
|
922
|
+
const DRAG_PIXELS_PER_NOTCH = 40;
|
|
923
|
+
// Share of the outstanding wheel movement applied each animation frame.
|
|
924
|
+
const ZOOM_EASE = 0.35;
|
|
925
|
+
// A pan plane seen closer to edge on than this (sine of the angle) is swapped for one facing the camera.
|
|
926
|
+
const GRAZING_SINE = 0.12;
|
|
927
|
+
// Closest the view may get to straight up or down while orbiting, in radians, so the camera never flips over.
|
|
928
|
+
const ORBIT_POLE_MARGIN = Cesium.Math.toRadians(1);
|
|
929
|
+
var EDrag;
|
|
930
|
+
(function (EDrag) {
|
|
931
|
+
EDrag["Pan"] = "pan";
|
|
932
|
+
EDrag["Orbit"] = "orbit";
|
|
933
|
+
EDrag["Zoom"] = "zoom";
|
|
934
|
+
})(EDrag || (EDrag = {}));
|
|
935
|
+
(function (CameraControls) {
|
|
936
|
+
/**
|
|
937
|
+
* Takes over mouse drag and wheel navigation close to the ground; CesiumJS keeps planet scale, pinch and look.
|
|
938
|
+
*/
|
|
939
|
+
function Attach(viewer) {
|
|
940
|
+
var _a, _b, _c;
|
|
941
|
+
if (!viewer || viewer.isDestroyed() || viewer[CESIUM_CAMERA_CONTROLS_KEY]) {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
const scene = viewer.scene;
|
|
945
|
+
const controller = scene === null || scene === void 0 ? void 0 : scene.screenSpaceCameraController;
|
|
946
|
+
if (!controller || !scene.canvas || !scene.preUpdate) {
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
const state = {
|
|
950
|
+
viewer,
|
|
951
|
+
getModelSpaceFrame: null,
|
|
952
|
+
drag: null,
|
|
953
|
+
touches: new Set(),
|
|
954
|
+
pendingZoom: new Cesium.Cartesian3(),
|
|
955
|
+
zoomFrame: null,
|
|
956
|
+
zoomPosition: null,
|
|
957
|
+
lastDistance: 100,
|
|
958
|
+
saved: {
|
|
959
|
+
rotateEventTypes: controller.rotateEventTypes,
|
|
960
|
+
zoomEventTypes: controller.zoomEventTypes,
|
|
961
|
+
tiltEventTypes: controller.tiltEventTypes,
|
|
962
|
+
enableCollisionDetection: controller.enableCollisionDetection,
|
|
963
|
+
backFaceAlpha: (_b = (_a = scene.globe) === null || _a === void 0 ? void 0 : _a.translucency) === null || _b === void 0 ? void 0 : _b.backFaceAlpha,
|
|
964
|
+
showSkirts: (_c = scene.globe) === null || _c === void 0 ? void 0 : _c.showSkirts
|
|
965
|
+
},
|
|
966
|
+
ours: false,
|
|
967
|
+
removals: []
|
|
968
|
+
};
|
|
969
|
+
viewer[CESIUM_CAMERA_CONTROLS_KEY] = state;
|
|
970
|
+
// The camera may go below the ground; zooming slows near a surface, so it takes a deliberate push.
|
|
971
|
+
controller.enableCollisionDetection = false;
|
|
972
|
+
const canvas = scene.canvas;
|
|
973
|
+
const listen = (target, type, handler, options) => {
|
|
974
|
+
target.addEventListener(type, handler, options);
|
|
975
|
+
state.removals.push(() => target.removeEventListener(type, handler, options));
|
|
976
|
+
};
|
|
977
|
+
listen(canvas, "pointerdown", (e) => onPointerDown(state, e));
|
|
978
|
+
listen(window, "pointermove", (e) => onPointerMove(state, e));
|
|
979
|
+
listen(window, "pointerup", (e) => onPointerUp(state, e));
|
|
980
|
+
listen(window, "pointercancel", (e) => onPointerUp(state, e));
|
|
981
|
+
listen(canvas, "wheel", (e) => onWheel(state, e), { passive: true });
|
|
982
|
+
state.removals.push(scene.preUpdate.addEventListener(() => syncController(state)));
|
|
983
|
+
syncController(state);
|
|
984
|
+
scene.requestRender();
|
|
985
|
+
}
|
|
986
|
+
CameraControls.Attach = Attach;
|
|
987
|
+
/**
|
|
988
|
+
* Hands navigation back to CesiumJS's own camera controller, as it was before Attach.
|
|
989
|
+
*/
|
|
990
|
+
function Detach(viewer) {
|
|
991
|
+
const state = viewer === null || viewer === void 0 ? void 0 : viewer[CESIUM_CAMERA_CONTROLS_KEY];
|
|
992
|
+
if (!state) {
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
SetModelSpace(viewer, null);
|
|
996
|
+
viewer[CESIUM_CAMERA_CONTROLS_KEY] = null;
|
|
997
|
+
state.removals.forEach((removal) => removal());
|
|
998
|
+
state.removals = [];
|
|
999
|
+
if (state.zoomFrame != null) {
|
|
1000
|
+
cancelAnimationFrame(state.zoomFrame);
|
|
1001
|
+
}
|
|
1002
|
+
if (viewer.isDestroyed()) {
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
restoreCesiumGestures(state);
|
|
1006
|
+
viewer.scene.screenSpaceCameraController.enableCollisionDetection = state.saved.enableCollisionDetection;
|
|
1007
|
+
viewer.scene.requestRender();
|
|
1008
|
+
}
|
|
1009
|
+
CameraControls.Detach = Detach;
|
|
1010
|
+
function IsAttached(viewer) {
|
|
1011
|
+
return Boolean(viewer === null || viewer === void 0 ? void 0 : viewer[CESIUM_CAMERA_CONTROLS_KEY]);
|
|
1012
|
+
}
|
|
1013
|
+
CameraControls.IsAttached = IsAttached;
|
|
1014
|
+
/**
|
|
1015
|
+
* Switches to navigating model-space measured from the frame getFrame returns, or back to geospatial with null.
|
|
1016
|
+
*/
|
|
1017
|
+
function SetModelSpace(viewer, getFrame) {
|
|
1018
|
+
const state = viewer === null || viewer === void 0 ? void 0 : viewer[CESIUM_CAMERA_CONTROLS_KEY];
|
|
1019
|
+
if (!state || viewer.isDestroyed()) {
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
const wasModelSpace = Boolean(state.getModelSpaceFrame);
|
|
1023
|
+
state.getModelSpaceFrame = getFrame !== null && getFrame !== void 0 ? getFrame : null;
|
|
1024
|
+
syncController(state);
|
|
1025
|
+
if (wasModelSpace === Boolean(getFrame)) {
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
const globe = viewer.scene.globe;
|
|
1029
|
+
const translucency = globe === null || globe === void 0 ? void 0 : globe.translucency;
|
|
1030
|
+
if (getFrame) {
|
|
1031
|
+
// The ground is see-through from below, so what stands on it stays in view underground.
|
|
1032
|
+
if (translucency && typeof translucency.backFaceAlpha === "number") {
|
|
1033
|
+
translucency.backFaceAlpha = 0.5;
|
|
1034
|
+
}
|
|
1035
|
+
// Skirts hide seams flat model-space ground never has, and below it they are walls.
|
|
1036
|
+
if (typeof state.saved.showSkirts === "boolean") {
|
|
1037
|
+
globe.showSkirts = false;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
else {
|
|
1041
|
+
if (translucency && typeof state.saved.backFaceAlpha === "number") {
|
|
1042
|
+
translucency.backFaceAlpha = state.saved.backFaceAlpha;
|
|
1043
|
+
}
|
|
1044
|
+
if (typeof state.saved.showSkirts === "boolean") {
|
|
1045
|
+
globe.showSkirts = state.saved.showSkirts;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
viewer.scene.requestRender();
|
|
1049
|
+
}
|
|
1050
|
+
CameraControls.SetModelSpace = SetModelSpace;
|
|
1051
|
+
})(exports.CameraControls || (exports.CameraControls = {}));
|
|
1052
|
+
function restoreCesiumGestures(state) {
|
|
1053
|
+
const controller = state.viewer.scene.screenSpaceCameraController;
|
|
1054
|
+
controller.rotateEventTypes = state.saved.rotateEventTypes;
|
|
1055
|
+
controller.zoomEventTypes = state.saved.zoomEventTypes;
|
|
1056
|
+
controller.tiltEventTypes = state.saved.tiltEventTypes;
|
|
1057
|
+
}
|
|
1058
|
+
/*
|
|
1059
|
+
* Decides each frame who has the mouse: these controls near the ground, CesiumJS for planets, orthographic and tracking.
|
|
1060
|
+
*/
|
|
1061
|
+
function syncController(state) {
|
|
1062
|
+
const viewer = state.viewer;
|
|
1063
|
+
if (viewer.isDestroyed() || state.drag) {
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
const ours = shouldControl(state);
|
|
1067
|
+
if (ours === state.ours) {
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
state.ours = ours;
|
|
1071
|
+
if (ours) {
|
|
1072
|
+
const controller = viewer.scene.screenSpaceCameraController;
|
|
1073
|
+
controller.rotateEventTypes = [];
|
|
1074
|
+
controller.zoomEventTypes = [Cesium.CameraEventType.PINCH];
|
|
1075
|
+
controller.tiltEventTypes = [Cesium.CameraEventType.PINCH];
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
restoreCesiumGestures(state);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
function shouldControl(state) {
|
|
1082
|
+
const viewer = state.viewer;
|
|
1083
|
+
const camera = viewer.camera;
|
|
1084
|
+
if (viewer.scene.mode !== Cesium.SceneMode.SCENE3D || camera.frustum instanceof Cesium.OrthographicFrustum) {
|
|
1085
|
+
return false;
|
|
1086
|
+
}
|
|
1087
|
+
// A camera following something is CesiumJS's to move.
|
|
1088
|
+
if (!Cesium.Matrix4.equals(camera.transform, Cesium.Matrix4.IDENTITY)) {
|
|
1089
|
+
return false;
|
|
1090
|
+
}
|
|
1091
|
+
if (state.getModelSpaceFrame) {
|
|
1092
|
+
return true;
|
|
1093
|
+
}
|
|
1094
|
+
return heightOf(state, camera.positionWC) < GEO_HANDOFF_HEIGHT;
|
|
1095
|
+
}
|
|
1096
|
+
function ellipsoidOf(state) {
|
|
1097
|
+
var _a, _b;
|
|
1098
|
+
return (_b = (_a = state.viewer.scene.globe) === null || _a === void 0 ? void 0 : _a.ellipsoid) !== null && _b !== void 0 ? _b : Cesium.Ellipsoid.WGS84;
|
|
1099
|
+
}
|
|
1100
|
+
function heightOf(state, position) {
|
|
1101
|
+
const carto = ellipsoidOf(state).cartesianToCartographic(position);
|
|
1102
|
+
return carto ? carto.height : Number.POSITIVE_INFINITY;
|
|
1103
|
+
}
|
|
1104
|
+
function canvasPosition(viewer, e) {
|
|
1105
|
+
const rect = viewer.scene.canvas.getBoundingClientRect();
|
|
1106
|
+
return new Cesium.Cartesian2(e.clientX - rect.left, e.clientY - rect.top);
|
|
1107
|
+
}
|
|
1108
|
+
/*
|
|
1109
|
+
* Up at a point: the model-space frame's up in model-space, else the ellipsoid's surface normal there.
|
|
1110
|
+
*/
|
|
1111
|
+
function upAt(state, position) {
|
|
1112
|
+
if (state.getModelSpaceFrame) {
|
|
1113
|
+
const up = Cesium.Matrix4.multiplyByPointAsVector(state.getModelSpaceFrame(), Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3());
|
|
1114
|
+
return Cesium.Cartesian3.normalize(up, up);
|
|
1115
|
+
}
|
|
1116
|
+
return ellipsoidOf(state).geodeticSurfaceNormal(position, new Cesium.Cartesian3());
|
|
1117
|
+
}
|
|
1118
|
+
/*
|
|
1119
|
+
* The level plane standing in for the ground when nothing is drawn under the cursor.
|
|
1120
|
+
*/
|
|
1121
|
+
function groundPlane(state) {
|
|
1122
|
+
if (state.getModelSpaceFrame) {
|
|
1123
|
+
const frame = state.getModelSpaceFrame();
|
|
1124
|
+
return Cesium.Plane.fromPointNormal(Cesium.Matrix4.getTranslation(frame, new Cesium.Cartesian3()), upAt(state, null));
|
|
1125
|
+
}
|
|
1126
|
+
const camera = state.viewer.camera;
|
|
1127
|
+
const ellipsoid = ellipsoidOf(state);
|
|
1128
|
+
const surface = ellipsoid.scaleToGeodeticSurface(camera.positionWC, new Cesium.Cartesian3());
|
|
1129
|
+
if (!surface) {
|
|
1130
|
+
return null;
|
|
1131
|
+
}
|
|
1132
|
+
return Cesium.Plane.fromPointNormal(surface, ellipsoid.geodeticSurfaceNormal(surface, new Cesium.Cartesian3()));
|
|
1133
|
+
}
|
|
1134
|
+
/*
|
|
1135
|
+
* How far away a depth pick is believable, since below ground the depth buffer can hold the globe's inside.
|
|
1136
|
+
*/
|
|
1137
|
+
function plausibleDistance(state) {
|
|
1138
|
+
const camera = state.viewer.camera;
|
|
1139
|
+
if (state.getModelSpaceFrame) {
|
|
1140
|
+
const origin = Cesium.Matrix4.getTranslation(state.getModelSpaceFrame(), new Cesium.Cartesian3());
|
|
1141
|
+
return Math.max(PLAUSIBLE_PICK_FLOOR, Cesium.Cartesian3.distance(camera.positionWC, origin) * 10);
|
|
1142
|
+
}
|
|
1143
|
+
const height = Math.abs(heightOf(state, camera.positionWC) - (Number(state.viewer.scene.globeHeight) || 0));
|
|
1144
|
+
const horizon = Math.sqrt(2 * ellipsoidOf(state).maximumRadius * Math.max(height, 0));
|
|
1145
|
+
return Math.max(PLAUSIBLE_PICK_FLOOR, height * 10, horizon * 1.5);
|
|
1146
|
+
}
|
|
1147
|
+
/*
|
|
1148
|
+
* The point under a screen position: what is drawn there, else the ground plane, else a point at the last distance.
|
|
1149
|
+
*/
|
|
1150
|
+
function pickTarget(state, position) {
|
|
1151
|
+
const viewer = state.viewer;
|
|
1152
|
+
const scene = viewer.scene;
|
|
1153
|
+
const camera = viewer.camera;
|
|
1154
|
+
const ray = camera.getPickRay(position);
|
|
1155
|
+
if (!ray) {
|
|
1156
|
+
return null;
|
|
1157
|
+
}
|
|
1158
|
+
let picked;
|
|
1159
|
+
if (scene.pickPositionSupported) {
|
|
1160
|
+
try {
|
|
1161
|
+
picked = scene.pickPosition(position);
|
|
1162
|
+
}
|
|
1163
|
+
catch (e) {
|
|
1164
|
+
picked = null;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
const plausible = plausibleDistance(state);
|
|
1168
|
+
if (isAhead(camera, picked) && Cesium.Cartesian3.distance(picked, camera.positionWC) < plausible) {
|
|
1169
|
+
return picked;
|
|
1170
|
+
}
|
|
1171
|
+
const ground = groundPlane(state);
|
|
1172
|
+
const onGround = ground ? Cesium.IntersectionTests.rayPlane(ray, ground) : null;
|
|
1173
|
+
if (isAhead(camera, onGround) && Cesium.Cartesian3.distance(onGround, camera.positionWC) < plausible) {
|
|
1174
|
+
return onGround;
|
|
1175
|
+
}
|
|
1176
|
+
return Cesium.Ray.getPoint(ray, state.lastDistance);
|
|
1177
|
+
}
|
|
1178
|
+
function isAhead(camera, point) {
|
|
1179
|
+
if (!point || !Number.isFinite(point.x) || !Number.isFinite(point.y) || !Number.isFinite(point.z)) {
|
|
1180
|
+
return false;
|
|
1181
|
+
}
|
|
1182
|
+
const toPoint = Cesium.Cartesian3.subtract(point, camera.positionWC, new Cesium.Cartesian3());
|
|
1183
|
+
return Cesium.Cartesian3.dot(toPoint, camera.directionWC) > 0;
|
|
1184
|
+
}
|
|
1185
|
+
/*
|
|
1186
|
+
* Picks which gesture a button and its modifiers start, matching CesiumJS's own mapping.
|
|
1187
|
+
*/
|
|
1188
|
+
function dragKind(e) {
|
|
1189
|
+
if (e.shiftKey) {
|
|
1190
|
+
// Shift drag looks around, which CesiumJS still handles.
|
|
1191
|
+
return null;
|
|
1192
|
+
}
|
|
1193
|
+
if (e.button === 1 || (e.ctrlKey && (e.button === 0 || e.button === 2))) {
|
|
1194
|
+
return EDrag.Orbit;
|
|
1195
|
+
}
|
|
1196
|
+
if (e.button === 0) {
|
|
1197
|
+
return EDrag.Pan;
|
|
1198
|
+
}
|
|
1199
|
+
if (e.button === 2) {
|
|
1200
|
+
return EDrag.Zoom;
|
|
1201
|
+
}
|
|
1202
|
+
return null;
|
|
1203
|
+
}
|
|
1204
|
+
function allows(state, kind) {
|
|
1205
|
+
const controller = state.viewer.scene.screenSpaceCameraController;
|
|
1206
|
+
if (!controller.enableInputs) {
|
|
1207
|
+
return false;
|
|
1208
|
+
}
|
|
1209
|
+
switch (kind) {
|
|
1210
|
+
case EDrag.Pan:
|
|
1211
|
+
return controller.enableRotate;
|
|
1212
|
+
case EDrag.Orbit:
|
|
1213
|
+
return controller.enableTilt;
|
|
1214
|
+
case EDrag.Zoom:
|
|
1215
|
+
return controller.enableZoom;
|
|
1216
|
+
}
|
|
1217
|
+
return false;
|
|
1218
|
+
}
|
|
1219
|
+
function onPointerDown(state, e) {
|
|
1220
|
+
const viewer = state.viewer;
|
|
1221
|
+
if (viewer.isDestroyed() || !state.ours) {
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
if (e.pointerType === "touch") {
|
|
1225
|
+
state.touches.add(e.pointerId);
|
|
1226
|
+
// A second finger makes it a pinch, which CesiumJS handles.
|
|
1227
|
+
if (state.touches.size > 1) {
|
|
1228
|
+
endDrag(state);
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
const kind = dragKind(e);
|
|
1233
|
+
if (!kind || !allows(state, kind)) {
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
const position = canvasPosition(viewer, e);
|
|
1237
|
+
const target = pickTarget(state, position);
|
|
1238
|
+
if (!target) {
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
const camera = viewer.camera;
|
|
1242
|
+
const distance = Cesium.Cartesian3.distance(camera.positionWC, target);
|
|
1243
|
+
state.lastDistance = Math.max(distance, MIN_ZOOM_DISTANCE);
|
|
1244
|
+
const drag = { kind, pointerId: e.pointerId, last: position, target, up: upAt(state, target), distance };
|
|
1245
|
+
if (kind === EDrag.Pan) {
|
|
1246
|
+
drag.plane = panPlane(camera, target, drag.up);
|
|
1247
|
+
}
|
|
1248
|
+
else if (kind === EDrag.Zoom) {
|
|
1249
|
+
drag.direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(target, camera.positionWC, new Cesium.Cartesian3()), new Cesium.Cartesian3());
|
|
1250
|
+
}
|
|
1251
|
+
state.drag = drag;
|
|
1252
|
+
}
|
|
1253
|
+
/*
|
|
1254
|
+
* A level plane through the grabbed point, or one facing the camera when the level one is seen edge on.
|
|
1255
|
+
*/
|
|
1256
|
+
function panPlane(camera, target, up) {
|
|
1257
|
+
if (Math.abs(Cesium.Cartesian3.dot(camera.directionWC, up)) > GRAZING_SINE) {
|
|
1258
|
+
return Cesium.Plane.fromPointNormal(target, up);
|
|
1259
|
+
}
|
|
1260
|
+
return Cesium.Plane.fromPointNormal(target, Cesium.Cartesian3.negate(camera.directionWC, new Cesium.Cartesian3()));
|
|
1261
|
+
}
|
|
1262
|
+
function onPointerMove(state, e) {
|
|
1263
|
+
const drag = state.drag;
|
|
1264
|
+
const viewer = state.viewer;
|
|
1265
|
+
if (!drag || drag.pointerId !== e.pointerId || viewer.isDestroyed()) {
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
if (!allows(state, drag.kind)) {
|
|
1269
|
+
endDrag(state);
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
const position = canvasPosition(viewer, e);
|
|
1273
|
+
const dx = position.x - drag.last.x;
|
|
1274
|
+
const dy = position.y - drag.last.y;
|
|
1275
|
+
if (dx === 0 && dy === 0) {
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
switch (drag.kind) {
|
|
1279
|
+
case EDrag.Pan:
|
|
1280
|
+
pan(state, drag, position);
|
|
1281
|
+
break;
|
|
1282
|
+
case EDrag.Orbit:
|
|
1283
|
+
orbit(state, drag, dx, dy);
|
|
1284
|
+
break;
|
|
1285
|
+
case EDrag.Zoom:
|
|
1286
|
+
// Dragging down zooms in, as it does in CesiumJS.
|
|
1287
|
+
moveAlong(state, drag.direction, drag.target, dy / DRAG_PIXELS_PER_NOTCH);
|
|
1288
|
+
break;
|
|
1289
|
+
}
|
|
1290
|
+
drag.last = position;
|
|
1291
|
+
viewer.scene.requestRender();
|
|
1292
|
+
}
|
|
1293
|
+
function onPointerUp(state, e) {
|
|
1294
|
+
var _a;
|
|
1295
|
+
state.touches.delete(e.pointerId);
|
|
1296
|
+
if (((_a = state.drag) === null || _a === void 0 ? void 0 : _a.pointerId) === e.pointerId) {
|
|
1297
|
+
endDrag(state);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
function endDrag(state) {
|
|
1301
|
+
state.drag = null;
|
|
1302
|
+
}
|
|
1303
|
+
/*
|
|
1304
|
+
* Slides the camera so the grabbed point stays under the cursor.
|
|
1305
|
+
*/
|
|
1306
|
+
function pan(state, drag, position) {
|
|
1307
|
+
const camera = state.viewer.camera;
|
|
1308
|
+
const ray = camera.getPickRay(position);
|
|
1309
|
+
const hit = ray ? Cesium.IntersectionTests.rayPlane(ray, drag.plane) : null;
|
|
1310
|
+
if (!isAhead(camera, hit)) {
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
const delta = Cesium.Cartesian3.subtract(drag.target, hit, new Cesium.Cartesian3());
|
|
1314
|
+
// Near the horizon a pixel covers kilometres, so one move is capped at a few times the grab distance.
|
|
1315
|
+
const limit = Math.max(drag.distance, MIN_ZOOM_DISTANCE) * 3;
|
|
1316
|
+
const length = Cesium.Cartesian3.magnitude(delta);
|
|
1317
|
+
if (length > limit) {
|
|
1318
|
+
Cesium.Cartesian3.multiplyByScalar(delta, limit / length, delta);
|
|
1319
|
+
}
|
|
1320
|
+
Cesium.Cartesian3.add(camera.position, delta, camera.position);
|
|
1321
|
+
}
|
|
1322
|
+
/*
|
|
1323
|
+
* Turns the camera around the grabbed point: sideways about up there, and up or down about the camera's right.
|
|
1324
|
+
*/
|
|
1325
|
+
function orbit(state, drag, dx, dy) {
|
|
1326
|
+
const viewer = state.viewer;
|
|
1327
|
+
const camera = viewer.camera;
|
|
1328
|
+
const canvas = viewer.scene.canvas;
|
|
1329
|
+
const up = drag.up;
|
|
1330
|
+
const heading = -dx / Math.max(canvas.clientWidth, 1) * Math.PI * 2;
|
|
1331
|
+
// Dragging down looks further down, as CesiumJS's own tilt does.
|
|
1332
|
+
let pitch = -dy / Math.max(canvas.clientHeight, 1) * Math.PI;
|
|
1333
|
+
// Keeps the view from tipping past straight up or down, where it would flip over.
|
|
1334
|
+
const elevation = Math.asin(Cesium.Math.clamp(Cesium.Cartesian3.dot(camera.direction, up), -1, 1));
|
|
1335
|
+
const limit = Math.PI / 2 - ORBIT_POLE_MARGIN;
|
|
1336
|
+
pitch = Cesium.Math.clamp(elevation + pitch, -limit, limit) - elevation;
|
|
1337
|
+
rotateAbout(camera, drag.target, up, heading);
|
|
1338
|
+
const right = Cesium.Cartesian3.cross(camera.direction, up, new Cesium.Cartesian3());
|
|
1339
|
+
if (Cesium.Cartesian3.magnitude(right) > Cesium.Math.EPSILON6) {
|
|
1340
|
+
Cesium.Cartesian3.normalize(right, right);
|
|
1341
|
+
rotateAbout(camera, drag.target, right, pitch);
|
|
1342
|
+
}
|
|
1343
|
+
levelCamera(camera, upAt(state, camera.position));
|
|
1344
|
+
}
|
|
1345
|
+
function rotateAbout(camera, pivot, axis, angle) {
|
|
1346
|
+
if (!angle) {
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
const rotation = Cesium.Matrix3.fromQuaternion(Cesium.Quaternion.fromAxisAngle(axis, angle));
|
|
1350
|
+
const offset = Cesium.Cartesian3.subtract(camera.position, pivot, new Cesium.Cartesian3());
|
|
1351
|
+
Cesium.Matrix3.multiplyByVector(rotation, offset, offset);
|
|
1352
|
+
Cesium.Cartesian3.add(pivot, offset, camera.position);
|
|
1353
|
+
Cesium.Matrix3.multiplyByVector(rotation, camera.direction, camera.direction);
|
|
1354
|
+
Cesium.Matrix3.multiplyByVector(rotation, camera.up, camera.up);
|
|
1355
|
+
Cesium.Matrix3.multiplyByVector(rotation, camera.right, camera.right);
|
|
1356
|
+
}
|
|
1357
|
+
/*
|
|
1358
|
+
* Takes any roll out, so the horizon stays level however the camera has turned.
|
|
1359
|
+
*/
|
|
1360
|
+
function levelCamera(camera, up) {
|
|
1361
|
+
const direction = Cesium.Cartesian3.normalize(camera.direction, camera.direction);
|
|
1362
|
+
const right = Cesium.Cartesian3.cross(direction, up, new Cesium.Cartesian3());
|
|
1363
|
+
if (Cesium.Cartesian3.magnitude(right) < Cesium.Math.EPSILON6) {
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
Cesium.Cartesian3.normalize(right, camera.right);
|
|
1367
|
+
Cesium.Cartesian3.cross(camera.right, direction, camera.up);
|
|
1368
|
+
Cesium.Cartesian3.normalize(camera.up, camera.up);
|
|
1369
|
+
}
|
|
1370
|
+
function onWheel(state, e) {
|
|
1371
|
+
const viewer = state.viewer;
|
|
1372
|
+
if (viewer.isDestroyed() || !state.ours || !allows(state, EDrag.Zoom)) {
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
let notches = e.deltaMode === 1 ? e.deltaY / 3 : e.deltaMode === 2 ? e.deltaY : e.deltaY / 100;
|
|
1376
|
+
notches = Cesium.Math.clamp(-notches, -3, 3);
|
|
1377
|
+
if (!notches) {
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
const position = canvasPosition(viewer, e);
|
|
1381
|
+
const camera = viewer.camera;
|
|
1382
|
+
const target = pickTarget(state, position);
|
|
1383
|
+
const ray = camera.getPickRay(position);
|
|
1384
|
+
if (!target || !ray) {
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
const step = zoomStep(state, ray.direction, target, notches);
|
|
1388
|
+
Cesium.Cartesian3.add(state.pendingZoom, step, state.pendingZoom);
|
|
1389
|
+
state.zoomPosition = Cesium.Cartesian3.clone(camera.position);
|
|
1390
|
+
if (state.zoomFrame == null) {
|
|
1391
|
+
state.zoomFrame = requestAnimationFrame(() => easeZoom(state));
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
/*
|
|
1395
|
+
* Applies part of the outstanding wheel movement each frame, so a notch glides rather than jumps.
|
|
1396
|
+
*/
|
|
1397
|
+
function easeZoom(state) {
|
|
1398
|
+
state.zoomFrame = null;
|
|
1399
|
+
const viewer = state.viewer;
|
|
1400
|
+
if (viewer.isDestroyed() || !viewer[CESIUM_CAMERA_CONTROLS_KEY]) {
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
// A tool locking the camera mid glide (walkthrough, a camera lock) owns it from then on.
|
|
1404
|
+
if (!allows(state, EDrag.Zoom) ||
|
|
1405
|
+
!state.zoomPosition ||
|
|
1406
|
+
!Cesium.Cartesian3.equalsEpsilon(viewer.camera.position, state.zoomPosition, 0, 1e-3)) {
|
|
1407
|
+
state.pendingZoom = new Cesium.Cartesian3();
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
const remaining = Cesium.Cartesian3.magnitude(state.pendingZoom);
|
|
1411
|
+
const share = remaining < 0.01 ? 1 : ZOOM_EASE;
|
|
1412
|
+
const step = Cesium.Cartesian3.multiplyByScalar(state.pendingZoom, share, new Cesium.Cartesian3());
|
|
1413
|
+
Cesium.Cartesian3.subtract(state.pendingZoom, step, state.pendingZoom);
|
|
1414
|
+
Cesium.Cartesian3.add(viewer.camera.position, step, viewer.camera.position);
|
|
1415
|
+
state.zoomPosition = Cesium.Cartesian3.clone(viewer.camera.position);
|
|
1416
|
+
viewer.scene.requestRender();
|
|
1417
|
+
if (share < 1) {
|
|
1418
|
+
state.zoomFrame = requestAnimationFrame(() => easeZoom(state));
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
function moveAlong(state, direction, target, notches) {
|
|
1422
|
+
const step = zoomStep(state, direction, target, notches);
|
|
1423
|
+
Cesium.Cartesian3.add(state.viewer.camera.position, step, state.viewer.camera.position);
|
|
1424
|
+
}
|
|
1425
|
+
/*
|
|
1426
|
+
* The move for some notches toward a target along a direction, a share of the distance with a floor so it never stalls.
|
|
1427
|
+
*/
|
|
1428
|
+
function zoomStep(state, direction, target, notches) {
|
|
1429
|
+
const camera = state.viewer.camera;
|
|
1430
|
+
const distance = Math.max(Cesium.Cartesian3.distance(camera.positionWC, target), MIN_ZOOM_DISTANCE);
|
|
1431
|
+
state.lastDistance = distance;
|
|
1432
|
+
let length = distance * ZOOM_SHARE * notches;
|
|
1433
|
+
// A geospatial camera zooming out hands over to CesiumJS once it is high enough, so only model-space is capped.
|
|
1434
|
+
if (length < 0 && state.getModelSpaceFrame) {
|
|
1435
|
+
const origin = Cesium.Matrix4.getTranslation(state.getModelSpaceFrame(), new Cesium.Cartesian3());
|
|
1436
|
+
const pending = Cesium.Cartesian3.add(camera.positionWC, state.pendingZoom, new Cesium.Cartesian3());
|
|
1437
|
+
const range = Cesium.Cartesian3.distance(pending, origin);
|
|
1438
|
+
length = Math.max(length, range - MODEL_SPACE_MAX_RANGE);
|
|
1439
|
+
if (length >= 0) {
|
|
1440
|
+
return new Cesium.Cartesian3();
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
return Cesium.Cartesian3.multiplyByScalar(Cesium.Cartesian3.normalize(direction, new Cesium.Cartesian3()), length, new Cesium.Cartesian3());
|
|
1444
|
+
}
|
|
1445
|
+
|
|
910
1446
|
const CESIUM_INSPECTOR_KEY = "_nextspace_inspector";
|
|
911
1447
|
const CESIUM_TIMELINE_KEY = "_nextspace_timeline";
|
|
912
1448
|
const CESIUM_TIMELINE_LIVE_KEY = "_nextspace_timeline_live";
|
|
@@ -1383,6 +1919,13 @@
|
|
|
1383
1919
|
return overriddenImageBasedLighting || null;
|
|
1384
1920
|
}
|
|
1385
1921
|
ViewUtils.GetDefaultImageBasedLighting = GetDefaultImageBasedLighting;
|
|
1922
|
+
/**
|
|
1923
|
+
* Shadow mode for the Entity graphics this library draws, following the scene's shadow switch.
|
|
1924
|
+
*/
|
|
1925
|
+
function GetEntityShadowMode(viewer) {
|
|
1926
|
+
return (viewer === null || viewer === void 0 ? void 0 : viewer.shadows) ? Cesium.ShadowMode.ENABLED : Cesium.ShadowMode.DISABLED;
|
|
1927
|
+
}
|
|
1928
|
+
ViewUtils.GetEntityShadowMode = GetEntityShadowMode;
|
|
1386
1929
|
function EngineShadesModelsDarker() {
|
|
1387
1930
|
return typeof Cesium.DynamicEnvironmentMapManager !== "undefined";
|
|
1388
1931
|
}
|
|
@@ -1454,6 +1997,7 @@
|
|
|
1454
1997
|
viewer[CESIUM_MODEL_SPACE_KEY] = modelSpace;
|
|
1455
1998
|
// Reload globe since we display it differently between the two modes.
|
|
1456
1999
|
SetGlobeDetails({ viewer });
|
|
2000
|
+
exports.CameraControls.SetModelSpace(viewer, modelSpace ? () => GetModelSpaceFrame(viewer) : null);
|
|
1457
2001
|
}
|
|
1458
2002
|
ViewUtils.SetModelSpace = SetModelSpace;
|
|
1459
2003
|
function GetModelSpace(viewer) {
|
|
@@ -6166,7 +6710,7 @@
|
|
|
6166
6710
|
const byTime = new Map();
|
|
6167
6711
|
for (const set of this.sets.values()) {
|
|
6168
6712
|
for (const [ms, record] of set.records.get(entityId) || []) {
|
|
6169
|
-
if (attrKey && record.attrKey && record.attrKey
|
|
6713
|
+
if (attrKey && record.attrKey && !BModels.PathUtils.Equals(record.attrKey, attrKey)) {
|
|
6170
6714
|
continue;
|
|
6171
6715
|
}
|
|
6172
6716
|
const held = byTime.get(ms);
|
|
@@ -11965,6 +12509,28 @@
|
|
|
11965
12509
|
return highest;
|
|
11966
12510
|
}
|
|
11967
12511
|
const MAX_SHOW_DEPTH = 10;
|
|
12512
|
+
// Entity graphics whose shadows follow the scene's shadow switch.
|
|
12513
|
+
const SHADOWED_GRAPHICS = ["model", "polygon", "polyline", "corridor"];
|
|
12514
|
+
/*
|
|
12515
|
+
* Points a registered Entity graphic, and the graphics drawn beside it, at the given shadow mode.
|
|
12516
|
+
*/
|
|
12517
|
+
function updateCEntityShadows(viewer, visual, mode, depth = 0) {
|
|
12518
|
+
if (!visual || depth > MAX_SHOW_DEPTH) {
|
|
12519
|
+
return;
|
|
12520
|
+
}
|
|
12521
|
+
for (const key of SHADOWED_GRAPHICS) {
|
|
12522
|
+
const graphic = visual[key];
|
|
12523
|
+
// Only graphics drawn with a mode; one left at Cesium's default was never meant to shadow.
|
|
12524
|
+
if ((graphic === null || graphic === void 0 ? void 0 : graphic.shadows) != null) {
|
|
12525
|
+
SetCPropIfChanged({ viewer, graphic, property: "shadows", value: mode });
|
|
12526
|
+
}
|
|
12527
|
+
}
|
|
12528
|
+
if (visual._siblingGraphics) {
|
|
12529
|
+
for (let i = 0; i < visual._siblingGraphics.length; i++) {
|
|
12530
|
+
updateCEntityShadows(viewer, visual._siblingGraphics[i], mode, depth + 1);
|
|
12531
|
+
}
|
|
12532
|
+
}
|
|
12533
|
+
}
|
|
11968
12534
|
function updateCEntityShow(viewer, visual, rego, show, ignoreParent, depth = 0) {
|
|
11969
12535
|
if (depth > MAX_SHOW_DEPTH) {
|
|
11970
12536
|
console.warn("updateCEntityShow(): Max show depth reached. EntityId = " + rego.entityId);
|
|
@@ -12297,7 +12863,7 @@
|
|
|
12297
12863
|
}
|
|
12298
12864
|
get OnLabelGroupsChanged() { return this.labelGroupsChanged; }
|
|
12299
12865
|
constructor(params) {
|
|
12300
|
-
var _a, _b;
|
|
12866
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
12301
12867
|
this.id = BModels.ObjectUtils.UId();
|
|
12302
12868
|
this.disposed = false;
|
|
12303
12869
|
this.rego = {};
|
|
@@ -12347,6 +12913,7 @@
|
|
|
12347
12913
|
this.sourceOrderDirty = false;
|
|
12348
12914
|
// Stops following what is simulated, so labels can read it again when it changes.
|
|
12349
12915
|
this.simulationRemoval = null;
|
|
12916
|
+
this.shadowsRemoval = null;
|
|
12350
12917
|
this.labelGroupsChanged = new BModels.BruceEvent();
|
|
12351
12918
|
this.hoverRingState = {};
|
|
12352
12919
|
this.retiringHoverLabels = new Map();
|
|
@@ -12382,6 +12949,27 @@
|
|
|
12382
12949
|
register: this
|
|
12383
12950
|
});
|
|
12384
12951
|
this.simulationRemoval = (_b = (_a = exports.EntitySimulation.Get(this.viewer)) === null || _a === void 0 ? void 0 : _a.OnChanged.Subscribe((change) => this.reloadLabels(change.entityIds))) !== null && _b !== void 0 ? _b : null;
|
|
12952
|
+
this.entityShadows = Boolean((_c = this.viewer) === null || _c === void 0 ? void 0 : _c.shadows);
|
|
12953
|
+
this.shadowsRemoval = (_g = (_f = (_e = (_d = this.viewer) === null || _d === void 0 ? void 0 : _d.scene) === null || _e === void 0 ? void 0 : _e.preUpdate) === null || _f === void 0 ? void 0 : _f.addEventListener(() => this.syncEntityShadows())) !== null && _g !== void 0 ? _g : null;
|
|
12954
|
+
}
|
|
12955
|
+
/*
|
|
12956
|
+
* Follows the scene's shadow switch on every registered Entity, whichever app or bookmark flipped it.
|
|
12957
|
+
*/
|
|
12958
|
+
syncEntityShadows() {
|
|
12959
|
+
const viewer = this.viewer;
|
|
12960
|
+
const enabled = Boolean(viewer === null || viewer === void 0 ? void 0 : viewer.shadows);
|
|
12961
|
+
if (enabled === this.entityShadows || this.disposed || !viewer || viewer.isDestroyed()) {
|
|
12962
|
+
return;
|
|
12963
|
+
}
|
|
12964
|
+
this.entityShadows = enabled;
|
|
12965
|
+
const mode = enabled ? Cesium.ShadowMode.ENABLED : Cesium.ShadowMode.DISABLED;
|
|
12966
|
+
for (const regos of Object.values(this.rego)) {
|
|
12967
|
+
for (const rego of regos) {
|
|
12968
|
+
if (rego.visual instanceof Cesium.Entity) {
|
|
12969
|
+
updateCEntityShadows(viewer, rego.visual, mode);
|
|
12970
|
+
}
|
|
12971
|
+
}
|
|
12972
|
+
}
|
|
12385
12973
|
}
|
|
12386
12974
|
/*
|
|
12387
12975
|
* Tells labels to read their Entity again, since they keep what they read when drawn.
|
|
@@ -13310,9 +13898,11 @@
|
|
|
13310
13898
|
(_b = this.viewer.scene) === null || _b === void 0 ? void 0 : _b.requestRender();
|
|
13311
13899
|
}
|
|
13312
13900
|
Dispose() {
|
|
13313
|
-
var _a, _b, _c, _d, _e, _f;
|
|
13901
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
13314
13902
|
(_a = this.simulationRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
13315
13903
|
this.simulationRemoval = null;
|
|
13904
|
+
(_b = this.shadowsRemoval) === null || _b === void 0 ? void 0 : _b.call(this);
|
|
13905
|
+
this.shadowsRemoval = null;
|
|
13316
13906
|
exports.CesiumEntityStyler.SetDimming(this.viewer, this.id, false);
|
|
13317
13907
|
this.ClearLabelHover();
|
|
13318
13908
|
clearTimeout(this.hoverRingReset);
|
|
@@ -13322,14 +13912,14 @@
|
|
|
13322
13912
|
exports.EntityLabel.Detatch({ label });
|
|
13323
13913
|
}
|
|
13324
13914
|
this.retiringHoverLabels.clear();
|
|
13325
|
-
(
|
|
13915
|
+
(_c = this.labelGroups) === null || _c === void 0 ? void 0 : _c.Dispose();
|
|
13326
13916
|
this.labelGroupsChanged.Clear();
|
|
13327
13917
|
for (const regos of Object.values(this.rego)) {
|
|
13328
13918
|
for (const rego of regos)
|
|
13329
13919
|
exports.EntityLabel.Detatch({ rego, force: true });
|
|
13330
13920
|
}
|
|
13331
13921
|
exports.EntityLabel.SetContainer(this.viewer);
|
|
13332
|
-
(
|
|
13922
|
+
(_d = this.cameraCullerDispose) === null || _d === void 0 ? void 0 : _d.call(this, {
|
|
13333
13923
|
uncull: false
|
|
13334
13924
|
});
|
|
13335
13925
|
this.cameraCullerDispose = null;
|
|
@@ -13346,7 +13936,7 @@
|
|
|
13346
13936
|
this.disposed = true;
|
|
13347
13937
|
for (const record of Array.from(this.menuItemSources.values())) {
|
|
13348
13938
|
try {
|
|
13349
|
-
(
|
|
13939
|
+
(_f = (_e = this.viewer) === null || _e === void 0 ? void 0 : _e.dataSources) === null || _f === void 0 ? void 0 : _f.remove(record.source, true);
|
|
13350
13940
|
}
|
|
13351
13941
|
catch (e) {
|
|
13352
13942
|
console.error(e);
|
|
@@ -13355,7 +13945,7 @@
|
|
|
13355
13945
|
this.menuItemSources.clear();
|
|
13356
13946
|
// Hand every tileset's show flag back to its owner, holding whatever the owner last asked for.
|
|
13357
13947
|
for (const rego of Array.from(this.tilesets.values())) {
|
|
13358
|
-
(
|
|
13948
|
+
(_g = rego.gate) === null || _g === void 0 ? void 0 : _g.Release();
|
|
13359
13949
|
}
|
|
13360
13950
|
this.tilesets.clear();
|
|
13361
13951
|
}
|
|
@@ -14079,8 +14669,11 @@
|
|
|
14079
14669
|
}
|
|
14080
14670
|
}
|
|
14081
14671
|
// Add the new visual to the register.
|
|
14672
|
+
// Tileset managers add the same rego again for each tile holding more of the Entity.
|
|
14082
14673
|
const entityRegos = (_a = this.rego[entityId]) !== null && _a !== void 0 ? _a : [];
|
|
14083
|
-
entityRegos.
|
|
14674
|
+
if (!entityRegos.includes(rego)) {
|
|
14675
|
+
entityRegos.push(rego);
|
|
14676
|
+
}
|
|
14084
14677
|
this.rego[entityId] = entityRegos;
|
|
14085
14678
|
this.indexMenuItemEntity(rego.menuItemId, entityId);
|
|
14086
14679
|
// Mark the visual as part of this register so selection works.
|
|
@@ -14514,6 +15107,28 @@
|
|
|
14514
15107
|
}
|
|
14515
15108
|
}
|
|
14516
15109
|
}
|
|
15110
|
+
/*
|
|
15111
|
+
* Hands a tile feature rego to a sibling feature from another tile when its own tile unloads.
|
|
15112
|
+
* An Entity split across tiles keeps drawing from those, so it must stay registered for them.
|
|
15113
|
+
*/
|
|
15114
|
+
promoteTileSibling(rego, removing) {
|
|
15115
|
+
var _a;
|
|
15116
|
+
const departing = rego.visual;
|
|
15117
|
+
if (!(departing instanceof Cesium.Cesium3DTileFeature) || !((_a = departing._siblingGraphics) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
15118
|
+
return false;
|
|
15119
|
+
}
|
|
15120
|
+
const remaining = departing._siblingGraphics.filter(s => !removing.has(s));
|
|
15121
|
+
if (!remaining.length) {
|
|
15122
|
+
return false;
|
|
15123
|
+
}
|
|
15124
|
+
departing._siblingGraphics = undefined;
|
|
15125
|
+
unmarkEntity(departing, true);
|
|
15126
|
+
const promoted = remaining[0];
|
|
15127
|
+
promoted._siblingGraphics = remaining.slice(1);
|
|
15128
|
+
rego.visual = promoted;
|
|
15129
|
+
markEntity(this, rego, promoted, false);
|
|
15130
|
+
return true;
|
|
15131
|
+
}
|
|
14517
15132
|
/**
|
|
14518
15133
|
* Removes regos matching given visuals.
|
|
14519
15134
|
* This is used when we cannot unpack properties from the visual any longer to determine its ID.
|
|
@@ -14524,6 +15139,7 @@
|
|
|
14524
15139
|
var _a, _b, _c;
|
|
14525
15140
|
const { doRemove = true, requestRender = true, source, doUpdate, menuItemId } = params;
|
|
14526
15141
|
const removedEntityIds = new Set();
|
|
15142
|
+
const removing = new Set(params.visuals);
|
|
14527
15143
|
for (const visual of params.visuals) {
|
|
14528
15144
|
const visExt = visual;
|
|
14529
15145
|
const rego = (visExt === null || visExt === void 0 ? void 0 : visExt._register) === this ? visExt._rego : null;
|
|
@@ -14535,7 +15151,20 @@
|
|
|
14535
15151
|
if (!entityRegos) {
|
|
14536
15152
|
continue;
|
|
14537
15153
|
}
|
|
14538
|
-
if (rego.visual === visual) {
|
|
15154
|
+
if (rego.visual === visual && this.promoteTileSibling(rego, removing)) {
|
|
15155
|
+
if (doRemove != false) {
|
|
15156
|
+
removeEntity(this.viewer, visual);
|
|
15157
|
+
}
|
|
15158
|
+
// The label was placed against the departing feature, the refresh attaches it again.
|
|
15159
|
+
exports.EntityLabel.Detatch({
|
|
15160
|
+
rego, force: true
|
|
15161
|
+
});
|
|
15162
|
+
this.queueUpdate({
|
|
15163
|
+
entityId: entityId,
|
|
15164
|
+
refresh: true
|
|
15165
|
+
});
|
|
15166
|
+
}
|
|
15167
|
+
else if (rego.visual === visual) {
|
|
14539
15168
|
exports.EntityLabel.Detatch({
|
|
14540
15169
|
rego, force: true
|
|
14541
15170
|
});
|
|
@@ -16187,6 +16816,300 @@
|
|
|
16187
16816
|
}
|
|
16188
16817
|
}
|
|
16189
16818
|
|
|
16819
|
+
// viewer[VIEWER_WATCH_KEY] is true once the viewer's canvas reports context loss to DeviceProfile.
|
|
16820
|
+
const VIEWER_WATCH_KEY = "_bruceDeviceProfileWatch";
|
|
16821
|
+
// localStorage[DEMOTION_KEY] holds an IDemotion written after this browser lost a WebGL context.
|
|
16822
|
+
const DEMOTION_KEY = "bruce-cesium-device-demotion";
|
|
16823
|
+
// A lost context demotes for this long, so one driver reset does not cap a strong device forever.
|
|
16824
|
+
// Measured on the wall clock, not the viewer's: it spans sessions, and a scene clock can be historic.
|
|
16825
|
+
const DEMOTION_MS = 14 * 24 * 60 * 60 * 1000;
|
|
16826
|
+
// Each loss demotes one more tier, up to this many.
|
|
16827
|
+
const DEMOTION_MAX = 2;
|
|
16828
|
+
/*
|
|
16829
|
+
* Reads the demotion left by earlier context losses, ignoring an expired or unreadable one.
|
|
16830
|
+
*/
|
|
16831
|
+
function readDemotion(now) {
|
|
16832
|
+
try {
|
|
16833
|
+
const value = JSON.parse(window.localStorage.getItem(DEMOTION_KEY));
|
|
16834
|
+
if (value &&
|
|
16835
|
+
Number.isFinite(value.count) &&
|
|
16836
|
+
Number.isFinite(value.at) &&
|
|
16837
|
+
now - value.at < DEMOTION_MS) {
|
|
16838
|
+
return value;
|
|
16839
|
+
}
|
|
16840
|
+
}
|
|
16841
|
+
catch (e) {
|
|
16842
|
+
// Storage is blocked (private mode, sandboxed frame) or holds junk; treat as no demotion.
|
|
16843
|
+
}
|
|
16844
|
+
return null;
|
|
16845
|
+
}
|
|
16846
|
+
/*
|
|
16847
|
+
* Stores or clears the demotion, carrying on without it where storage is blocked.
|
|
16848
|
+
*/
|
|
16849
|
+
function writeDemotion(value) {
|
|
16850
|
+
try {
|
|
16851
|
+
if (value) {
|
|
16852
|
+
window.localStorage.setItem(DEMOTION_KEY, JSON.stringify(value));
|
|
16853
|
+
}
|
|
16854
|
+
else {
|
|
16855
|
+
window.localStorage.removeItem(DEMOTION_KEY);
|
|
16856
|
+
}
|
|
16857
|
+
}
|
|
16858
|
+
catch (e) {
|
|
16859
|
+
// Without storage the demotion lasts only for this page.
|
|
16860
|
+
}
|
|
16861
|
+
}
|
|
16862
|
+
/*
|
|
16863
|
+
* Reads the GPU name, preferring the unmasked one, which browsers that hide it simply do not offer.
|
|
16864
|
+
*/
|
|
16865
|
+
function readRenderer(gl) {
|
|
16866
|
+
try {
|
|
16867
|
+
const ext = gl.getExtension("WEBGL_debug_renderer_info");
|
|
16868
|
+
const name = ext ? gl.getParameter(ext.UNMASKED_RENDERER_WEBGL) : gl.getParameter(gl.RENDERER);
|
|
16869
|
+
return typeof name === "string" && name ? name : undefined;
|
|
16870
|
+
}
|
|
16871
|
+
catch (e) {
|
|
16872
|
+
return undefined;
|
|
16873
|
+
}
|
|
16874
|
+
}
|
|
16875
|
+
(function (DeviceProfile) {
|
|
16876
|
+
let ETier;
|
|
16877
|
+
(function (ETier) {
|
|
16878
|
+
ETier["Low"] = "LOW";
|
|
16879
|
+
ETier["Mid"] = "MID";
|
|
16880
|
+
ETier["High"] = "HIGH";
|
|
16881
|
+
})(ETier = DeviceProfile.ETier || (DeviceProfile.ETier = {}));
|
|
16882
|
+
DeviceProfile.BUDGETS = {
|
|
16883
|
+
[ETier.Low]: { tilesetMb: 384, splats: 1000000 },
|
|
16884
|
+
[ETier.Mid]: { tilesetMb: 1024, splats: 3000000 },
|
|
16885
|
+
[ETier.High]: { tilesetMb: 2048, splats: 6000000 }
|
|
16886
|
+
};
|
|
16887
|
+
const ORDER = [ETier.Low, ETier.Mid, ETier.High];
|
|
16888
|
+
/**
|
|
16889
|
+
* Raised after the override or a demotion changes the tier, so live budgets can be redistributed.
|
|
16890
|
+
*/
|
|
16891
|
+
DeviceProfile.Changed = new Cesium.Event();
|
|
16892
|
+
let override = null;
|
|
16893
|
+
let detected = null;
|
|
16894
|
+
let logged = false;
|
|
16895
|
+
/*
|
|
16896
|
+
* Sorts a GPU name into a class. Order matters: Intel Arc is discrete, SwiftShader names any vendor.
|
|
16897
|
+
*/
|
|
16898
|
+
function ClassifyGpu(renderer) {
|
|
16899
|
+
const name = (renderer || "").toLowerCase();
|
|
16900
|
+
if (!name) {
|
|
16901
|
+
return "unknown";
|
|
16902
|
+
}
|
|
16903
|
+
if (/swiftshader|llvmpipe|softpipe|lavapipe|software|basic render/.test(name)) {
|
|
16904
|
+
return "software";
|
|
16905
|
+
}
|
|
16906
|
+
if (/mali|adreno|powervr|apple gpu|videocore|immortalis|xclipse|tegra/.test(name)) {
|
|
16907
|
+
return "mobile";
|
|
16908
|
+
}
|
|
16909
|
+
if (/apple m\d+ (pro|max|ultra)/.test(name)) {
|
|
16910
|
+
return "discrete";
|
|
16911
|
+
}
|
|
16912
|
+
if (/nvidia|geforce|quadro|rtx|radeon (rx|pro|r9|vii)|firepro|arc(\(tm\))? a\d/.test(name)) {
|
|
16913
|
+
return "discrete";
|
|
16914
|
+
}
|
|
16915
|
+
if (/intel|apple m\d|radeon(\(tm\))? graphics|vega \d/.test(name)) {
|
|
16916
|
+
return "integrated";
|
|
16917
|
+
}
|
|
16918
|
+
return "unknown";
|
|
16919
|
+
}
|
|
16920
|
+
DeviceProfile.ClassifyGpu = ClassifyGpu;
|
|
16921
|
+
/**
|
|
16922
|
+
* Guesses a tier from the hardware alone. Unknown hardware stays mid; only clear evidence moves it.
|
|
16923
|
+
*/
|
|
16924
|
+
function Classify(env) {
|
|
16925
|
+
const ua = (env.userAgent || "").toLowerCase();
|
|
16926
|
+
const mobile = /android|iphone|ipad|ipod|mobile/.test(ua) || (/macintosh/.test(ua) && env.maxTouchPoints > 1);
|
|
16927
|
+
const gpuClass = ClassifyGpu(env.gpuRenderer);
|
|
16928
|
+
const memory = env.deviceMemory;
|
|
16929
|
+
const reasons = [];
|
|
16930
|
+
let tier = ETier.Mid;
|
|
16931
|
+
if (gpuClass === "software") {
|
|
16932
|
+
reasons.push("software renderer");
|
|
16933
|
+
tier = ETier.Low;
|
|
16934
|
+
}
|
|
16935
|
+
if (mobile) {
|
|
16936
|
+
reasons.push("mobile device");
|
|
16937
|
+
tier = ETier.Low;
|
|
16938
|
+
}
|
|
16939
|
+
if (Number.isFinite(memory) && memory < 4) {
|
|
16940
|
+
reasons.push(`${memory} GB device memory`);
|
|
16941
|
+
tier = ETier.Low;
|
|
16942
|
+
}
|
|
16943
|
+
if (Number.isFinite(env.maxTextureSize) && env.maxTextureSize < 8192) {
|
|
16944
|
+
reasons.push(`max texture ${env.maxTextureSize}`);
|
|
16945
|
+
tier = ETier.Low;
|
|
16946
|
+
}
|
|
16947
|
+
if (tier === ETier.Mid && gpuClass === "discrete") {
|
|
16948
|
+
if (!Number.isFinite(memory) || memory >= 8) {
|
|
16949
|
+
reasons.push("discrete GPU");
|
|
16950
|
+
tier = ETier.High;
|
|
16951
|
+
}
|
|
16952
|
+
else {
|
|
16953
|
+
reasons.push(`discrete GPU held to mid by ${memory} GB device memory`);
|
|
16954
|
+
}
|
|
16955
|
+
}
|
|
16956
|
+
if (!reasons.length) {
|
|
16957
|
+
reasons.push(`${gpuClass} GPU`);
|
|
16958
|
+
}
|
|
16959
|
+
return { tier, gpuClass, mobile, reasons };
|
|
16960
|
+
}
|
|
16961
|
+
DeviceProfile.Classify = Classify;
|
|
16962
|
+
/*
|
|
16963
|
+
* Reads the browser environment, from the viewer's GL context when given rather than a spare one.
|
|
16964
|
+
*/
|
|
16965
|
+
function readEnvironment(viewer) {
|
|
16966
|
+
var _a, _b, _c;
|
|
16967
|
+
const nav = typeof navigator !== "undefined" ? navigator : {};
|
|
16968
|
+
const env = {
|
|
16969
|
+
userAgent: nav.userAgent,
|
|
16970
|
+
maxTouchPoints: nav.maxTouchPoints,
|
|
16971
|
+
deviceMemory: nav.deviceMemory,
|
|
16972
|
+
hardwareConcurrency: nav.hardwareConcurrency
|
|
16973
|
+
};
|
|
16974
|
+
let gl = (_b = (_a = viewer === null || viewer === void 0 ? void 0 : viewer.scene) === null || _a === void 0 ? void 0 : _a.context) === null || _b === void 0 ? void 0 : _b._gl;
|
|
16975
|
+
let spare = false;
|
|
16976
|
+
if (!gl && typeof document !== "undefined") {
|
|
16977
|
+
try {
|
|
16978
|
+
gl = document.createElement("canvas").getContext("webgl");
|
|
16979
|
+
spare = Boolean(gl);
|
|
16980
|
+
}
|
|
16981
|
+
catch (e) {
|
|
16982
|
+
gl = null;
|
|
16983
|
+
}
|
|
16984
|
+
}
|
|
16985
|
+
// Detection must never stop a scene loading, so anything odd leaves the GPU unknown.
|
|
16986
|
+
if (gl &&
|
|
16987
|
+
typeof gl.getParameter === "function" &&
|
|
16988
|
+
typeof gl.getExtension === "function") {
|
|
16989
|
+
env.gpuRenderer = readRenderer(gl);
|
|
16990
|
+
try {
|
|
16991
|
+
env.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE) || undefined;
|
|
16992
|
+
if (spare) {
|
|
16993
|
+
// Browsers cap live contexts per page; give this one back now rather than at GC.
|
|
16994
|
+
(_c = gl.getExtension("WEBGL_lose_context")) === null || _c === void 0 ? void 0 : _c.loseContext();
|
|
16995
|
+
}
|
|
16996
|
+
}
|
|
16997
|
+
catch (e) {
|
|
16998
|
+
// Leave it unknown.
|
|
16999
|
+
}
|
|
17000
|
+
}
|
|
17001
|
+
return env;
|
|
17002
|
+
}
|
|
17003
|
+
/**
|
|
17004
|
+
* Returns this device's profile, detecting it once per page. Pass the viewer to read its GL context.
|
|
17005
|
+
*/
|
|
17006
|
+
function Get(viewer) {
|
|
17007
|
+
var _a, _b;
|
|
17008
|
+
if (!detected) {
|
|
17009
|
+
const environment = readEnvironment(viewer);
|
|
17010
|
+
detected = { ...Classify(environment), environment };
|
|
17011
|
+
}
|
|
17012
|
+
const demotion = readDemotion(Date.now());
|
|
17013
|
+
const demoted = Math.min((_a = demotion === null || demotion === void 0 ? void 0 : demotion.count) !== null && _a !== void 0 ? _a : 0, DEMOTION_MAX);
|
|
17014
|
+
let tier = ORDER[Math.max(0, ORDER.indexOf(detected.tier) - demoted)];
|
|
17015
|
+
if (override) {
|
|
17016
|
+
tier = override;
|
|
17017
|
+
}
|
|
17018
|
+
const profile = {
|
|
17019
|
+
tier,
|
|
17020
|
+
detectedTier: detected.tier,
|
|
17021
|
+
gpuClass: detected.gpuClass,
|
|
17022
|
+
mobile: detected.mobile,
|
|
17023
|
+
demoted,
|
|
17024
|
+
overridden: Boolean(override),
|
|
17025
|
+
reasons: [...detected.reasons],
|
|
17026
|
+
environment: { ...detected.environment }
|
|
17027
|
+
};
|
|
17028
|
+
if (!logged) {
|
|
17029
|
+
logged = true;
|
|
17030
|
+
console.info(`[DeviceProfile] tier ${profile.tier} (${profile.reasons.join(", ")}` +
|
|
17031
|
+
`${demoted ? `, demoted ${demoted} after lost WebGL context` : ""}` +
|
|
17032
|
+
`${override ? ", overridden" : ""}). GPU: ${(_b = profile.environment.gpuRenderer) !== null && _b !== void 0 ? _b : "unknown"}.`);
|
|
17033
|
+
}
|
|
17034
|
+
return profile;
|
|
17035
|
+
}
|
|
17036
|
+
DeviceProfile.Get = Get;
|
|
17037
|
+
/**
|
|
17038
|
+
* Returns the memory budgets for this device's tier, or for a given tier.
|
|
17039
|
+
*/
|
|
17040
|
+
function GetBudget(tier) {
|
|
17041
|
+
return DeviceProfile.BUDGETS[tier !== null && tier !== void 0 ? tier : Get().tier];
|
|
17042
|
+
}
|
|
17043
|
+
DeviceProfile.GetBudget = GetBudget;
|
|
17044
|
+
/**
|
|
17045
|
+
* Forces a tier, for a user who knows better than the guess. Null returns to detection.
|
|
17046
|
+
*/
|
|
17047
|
+
function SetOverride(tier) {
|
|
17048
|
+
const next = tier && ORDER.includes(tier) ? tier : null;
|
|
17049
|
+
if (next === override) {
|
|
17050
|
+
return;
|
|
17051
|
+
}
|
|
17052
|
+
override = next;
|
|
17053
|
+
DeviceProfile.Changed.raiseEvent();
|
|
17054
|
+
}
|
|
17055
|
+
DeviceProfile.SetOverride = SetOverride;
|
|
17056
|
+
/**
|
|
17057
|
+
* Returns the forced tier, or null while detection decides.
|
|
17058
|
+
*/
|
|
17059
|
+
function GetOverride() {
|
|
17060
|
+
return override;
|
|
17061
|
+
}
|
|
17062
|
+
DeviceProfile.GetOverride = GetOverride;
|
|
17063
|
+
/**
|
|
17064
|
+
* Records that a WebGL context was lost, so this and later sessions start a tier lower.
|
|
17065
|
+
*/
|
|
17066
|
+
function RecordContextLost() {
|
|
17067
|
+
var _a, _b;
|
|
17068
|
+
const now = Date.now();
|
|
17069
|
+
const count = Math.min(((_b = (_a = readDemotion(now)) === null || _a === void 0 ? void 0 : _a.count) !== null && _b !== void 0 ? _b : 0) + 1, DEMOTION_MAX);
|
|
17070
|
+
writeDemotion({ count, at: now });
|
|
17071
|
+
console.warn(`[DeviceProfile] WebGL context lost; demoting the memory tier by ${count}.`);
|
|
17072
|
+
DeviceProfile.Changed.raiseEvent();
|
|
17073
|
+
}
|
|
17074
|
+
DeviceProfile.RecordContextLost = RecordContextLost;
|
|
17075
|
+
/**
|
|
17076
|
+
* Forgets earlier context losses, for a host that offers a reset.
|
|
17077
|
+
*/
|
|
17078
|
+
function ClearDemotion() {
|
|
17079
|
+
writeDemotion(null);
|
|
17080
|
+
DeviceProfile.Changed.raiseEvent();
|
|
17081
|
+
}
|
|
17082
|
+
DeviceProfile.ClearDemotion = ClearDemotion;
|
|
17083
|
+
/**
|
|
17084
|
+
* Reports the viewer's context losses. Safe to call repeatedly.
|
|
17085
|
+
*/
|
|
17086
|
+
function WatchViewer(viewer) {
|
|
17087
|
+
const canvas = viewer === null || viewer === void 0 ? void 0 : viewer.canvas;
|
|
17088
|
+
if (!canvas ||
|
|
17089
|
+
viewer[VIEWER_WATCH_KEY] ||
|
|
17090
|
+
typeof canvas.addEventListener !== "function") {
|
|
17091
|
+
return;
|
|
17092
|
+
}
|
|
17093
|
+
viewer[VIEWER_WATCH_KEY] = true;
|
|
17094
|
+
canvas.addEventListener("webglcontextlost", () => {
|
|
17095
|
+
// Ignore a loss caused by tearing the viewer down.
|
|
17096
|
+
if (!viewer.isDestroyed()) {
|
|
17097
|
+
RecordContextLost();
|
|
17098
|
+
}
|
|
17099
|
+
});
|
|
17100
|
+
}
|
|
17101
|
+
DeviceProfile.WatchViewer = WatchViewer;
|
|
17102
|
+
/**
|
|
17103
|
+
* Forgets the detected hardware and override, for tests.
|
|
17104
|
+
*/
|
|
17105
|
+
function Reset() {
|
|
17106
|
+
detected = null;
|
|
17107
|
+
override = null;
|
|
17108
|
+
logged = false;
|
|
17109
|
+
}
|
|
17110
|
+
DeviceProfile.Reset = Reset;
|
|
17111
|
+
})(exports.DeviceProfile || (exports.DeviceProfile = {}));
|
|
17112
|
+
|
|
16190
17113
|
/**
|
|
16191
17114
|
* Returns if a given tileset is alive and in the scene.
|
|
16192
17115
|
* @param viewer
|
|
@@ -16205,7 +17128,7 @@
|
|
|
16205
17128
|
}
|
|
16206
17129
|
return viewer.scene.primitives.contains(cTileset);
|
|
16207
17130
|
}
|
|
16208
|
-
const VIEWER_WATCH_KEY = "bruce-viewer-watch";
|
|
17131
|
+
const VIEWER_WATCH_KEY$1 = "bruce-viewer-watch";
|
|
16209
17132
|
const WATCH_KEY = "bruce-tileset-watch";
|
|
16210
17133
|
const VIEWER_DATE_RANGE_WATCH_KEY = "bruce-viewer-date-range-watch";
|
|
16211
17134
|
const DATE_RANGE_WATCH_KEY = "bruce-tileset-date-range-watch";
|
|
@@ -16267,6 +17190,22 @@
|
|
|
16267
17190
|
}
|
|
16268
17191
|
renderObject._bruceDateRangeVisible = visible;
|
|
16269
17192
|
}
|
|
17193
|
+
// A tileset may pass its cache share by this fraction before Cesium coarsens it to fit.
|
|
17194
|
+
const TILESET_OVERFLOW_RATIO = 0.25;
|
|
17195
|
+
/*
|
|
17196
|
+
* Returns the options capping a tileset's tile cache at the given MB, in the form the running Cesium reads.
|
|
17197
|
+
*/
|
|
17198
|
+
function getTilesetMemoryProps(mb) {
|
|
17199
|
+
const prototype = Cesium.Cesium3DTileset.prototype;
|
|
17200
|
+
if ("cacheBytes" in prototype) {
|
|
17201
|
+
const props = { cacheBytes: Math.round(mb * 1024 * 1024) };
|
|
17202
|
+
if ("maximumCacheOverflowBytes" in prototype) {
|
|
17203
|
+
props.maximumCacheOverflowBytes = Math.round(mb * TILESET_OVERFLOW_RATIO * 1024 * 1024);
|
|
17204
|
+
}
|
|
17205
|
+
return props;
|
|
17206
|
+
}
|
|
17207
|
+
return { maximumMemoryUsage: mb };
|
|
17208
|
+
}
|
|
16270
17209
|
/**
|
|
16271
17210
|
* Creates and returns a tileset instance from a given url.
|
|
16272
17211
|
* This is made to handle the difference between old and new cesium versions.
|
|
@@ -16283,8 +17222,8 @@
|
|
|
16283
17222
|
};
|
|
16284
17223
|
Object.assign(defaultProps, exports.ViewUtils.DefaultLightingProps());
|
|
16285
17224
|
if (noMemoryLimit != true) {
|
|
16286
|
-
//
|
|
16287
|
-
defaultProps.
|
|
17225
|
+
// The memory watcher narrows this to a share once the tileset joins it.
|
|
17226
|
+
Object.assign(defaultProps, getTilesetMemoryProps(exports.TilesetRenderEngine.GetTilesetMemoryBudget()));
|
|
16288
17227
|
}
|
|
16289
17228
|
const C3DT = Cesium.Cesium3DTileset;
|
|
16290
17229
|
if (C3DT.fromUrl) {
|
|
@@ -16835,10 +17774,23 @@
|
|
|
16835
17774
|
}
|
|
16836
17775
|
TilesetRenderEngine.UnwatchDateRange = UnwatchDateRange;
|
|
16837
17776
|
/**
|
|
16838
|
-
*
|
|
16839
|
-
|
|
17777
|
+
* Total MB all tilesets may use, split evenly; null leaves it to the DeviceProfile tier.
|
|
17778
|
+
*/
|
|
17779
|
+
TilesetRenderEngine.MAX_TILESET_MEMORY = null;
|
|
17780
|
+
/**
|
|
17781
|
+
* Smallest MB share a tileset gets; small, since Cesium coarsens one that needs more.
|
|
16840
17782
|
*/
|
|
16841
|
-
TilesetRenderEngine.
|
|
17783
|
+
TilesetRenderEngine.MIN_TILESET_MEMORY = 32;
|
|
17784
|
+
/**
|
|
17785
|
+
* Returns the total MB all tilesets may use, from MAX_TILESET_MEMORY or else the device tier.
|
|
17786
|
+
*/
|
|
17787
|
+
function GetTilesetMemoryBudget() {
|
|
17788
|
+
if (Number.isFinite(TilesetRenderEngine.MAX_TILESET_MEMORY) && TilesetRenderEngine.MAX_TILESET_MEMORY > 0) {
|
|
17789
|
+
return TilesetRenderEngine.MAX_TILESET_MEMORY;
|
|
17790
|
+
}
|
|
17791
|
+
return exports.DeviceProfile.GetBudget().tilesetMb;
|
|
17792
|
+
}
|
|
17793
|
+
TilesetRenderEngine.GetTilesetMemoryBudget = GetTilesetMemoryBudget;
|
|
16842
17794
|
/**
|
|
16843
17795
|
* Watches tileset date cutoffs and toggles their primitive visibility based on the viewer clock.
|
|
16844
17796
|
*/
|
|
@@ -16929,23 +17881,26 @@
|
|
|
16929
17881
|
constructor(viewer) {
|
|
16930
17882
|
this.watched = [];
|
|
16931
17883
|
this.viewer = viewer;
|
|
17884
|
+
exports.DeviceProfile.Get(viewer);
|
|
17885
|
+
exports.DeviceProfile.WatchViewer(viewer);
|
|
17886
|
+
this.removeChanged = exports.DeviceProfile.Changed.addEventListener(() => this.distributeMemory());
|
|
16932
17887
|
}
|
|
16933
17888
|
distributeMemory() {
|
|
16934
17889
|
this.clean();
|
|
16935
|
-
|
|
16936
|
-
|
|
16937
|
-
|
|
16938
|
-
|
|
16939
|
-
const
|
|
16940
|
-
const totalPerTileset = Math.max(this.watched.length ? maxMemory / this.watched.length : maxMemory, minMemory);
|
|
17890
|
+
if (!this.watched.length) {
|
|
17891
|
+
return;
|
|
17892
|
+
}
|
|
17893
|
+
const share = Math.max(GetTilesetMemoryBudget() / this.watched.length, TilesetRenderEngine.MIN_TILESET_MEMORY);
|
|
17894
|
+
const props = getTilesetMemoryProps(share);
|
|
16941
17895
|
this.watched.forEach(x => {
|
|
16942
|
-
|
|
16943
|
-
// TODO: Check if it's needed then.
|
|
16944
|
-
x["maximumMemoryUsage"] = totalPerTileset;
|
|
17896
|
+
Object.assign(x, props);
|
|
16945
17897
|
});
|
|
16946
17898
|
}
|
|
16947
17899
|
destroy() {
|
|
17900
|
+
var _a;
|
|
16948
17901
|
this.watched = [];
|
|
17902
|
+
(_a = this.removeChanged) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
17903
|
+
this.removeChanged = null;
|
|
16949
17904
|
}
|
|
16950
17905
|
/**
|
|
16951
17906
|
* Remove all dead tilesets.
|
|
@@ -16994,10 +17949,10 @@
|
|
|
16994
17949
|
if (!viewer || viewer.isDestroyed()) {
|
|
16995
17950
|
return null;
|
|
16996
17951
|
}
|
|
16997
|
-
if (!viewer[VIEWER_WATCH_KEY]) {
|
|
16998
|
-
viewer[VIEWER_WATCH_KEY] = new MemoryWatcher(viewer);
|
|
17952
|
+
if (!viewer[VIEWER_WATCH_KEY$1]) {
|
|
17953
|
+
viewer[VIEWER_WATCH_KEY$1] = new MemoryWatcher(viewer);
|
|
16999
17954
|
}
|
|
17000
|
-
return viewer[VIEWER_WATCH_KEY];
|
|
17955
|
+
return viewer[VIEWER_WATCH_KEY$1];
|
|
17001
17956
|
}
|
|
17002
17957
|
TilesetRenderEngine.GetMemoryWatcher = GetMemoryWatcher;
|
|
17003
17958
|
function GetDateRangeWatcher(viewer) {
|
|
@@ -28159,7 +29114,9 @@
|
|
|
28159
29114
|
if (loadUrl.indexOf("?") !== -1) {
|
|
28160
29115
|
loadUrl = loadUrl.split("?")[0];
|
|
28161
29116
|
}
|
|
28162
|
-
|
|
29117
|
+
const profile = exports.DeviceProfile.Get(viewer);
|
|
29118
|
+
exports.DeviceProfile.WatchViewer(viewer);
|
|
29119
|
+
let maxSplats = exports.DeviceProfile.GetBudget(profile.tier).splats;
|
|
28163
29120
|
/*
|
|
28164
29121
|
* Configures optional LOD capabilities once metadata or initial geometry is ready.
|
|
28165
29122
|
*/
|
|
@@ -28244,8 +29201,7 @@
|
|
|
28244
29201
|
}
|
|
28245
29202
|
try {
|
|
28246
29203
|
const sdkBudget = typeof obj.getCurrentConfig === "function" ? (_d = obj.getCurrentConfig()) === null || _d === void 0 ? void 0 : _d.maxLoadSplatCount : undefined;
|
|
28247
|
-
|
|
28248
|
-
maxSplats = Number.isFinite(sdkBudget) && sdkBudget > 0 && sdkBudget <= 2000000 ? sdkBudget : 6000000;
|
|
29204
|
+
maxSplats = GetSplatBudget(sdkBudget, profile);
|
|
28249
29205
|
if (LCCRender.nextspaceMetadataReady === 1 &&
|
|
28250
29206
|
typeof obj.setNextspaceMetadataCallback === "function" &&
|
|
28251
29207
|
typeof obj.setMaxSplats === "function") {
|
|
@@ -28286,6 +29242,21 @@
|
|
|
28286
29242
|
return obj;
|
|
28287
29243
|
}
|
|
28288
29244
|
XGridsRenderEngine.Render = Render;
|
|
29245
|
+
/*
|
|
29246
|
+
* The most splats a scan may load: the tier's, lowered by the SDK's GPU limit unless overridden.
|
|
29247
|
+
*/
|
|
29248
|
+
function GetSplatBudget(sdkBudget, profile) {
|
|
29249
|
+
const budget = exports.DeviceProfile.GetBudget(profile.tier).splats;
|
|
29250
|
+
// Above 2M the SDK is reporting its flat default, not a limit it chose for this GPU.
|
|
29251
|
+
if (!profile.overridden &&
|
|
29252
|
+
Number.isFinite(sdkBudget) &&
|
|
29253
|
+
sdkBudget > 0 &&
|
|
29254
|
+
sdkBudget <= 2000000) {
|
|
29255
|
+
return Math.min(sdkBudget, budget);
|
|
29256
|
+
}
|
|
29257
|
+
return budget;
|
|
29258
|
+
}
|
|
29259
|
+
XGridsRenderEngine.GetSplatBudget = GetSplatBudget;
|
|
28289
29260
|
/*
|
|
28290
29261
|
* Detaches optional tuning before unloading the scan.
|
|
28291
29262
|
*/
|
|
@@ -38712,6 +39683,1228 @@
|
|
|
38712
39683
|
})(Terrain = TileRenderEngine.Terrain || (TileRenderEngine.Terrain = {}));
|
|
38713
39684
|
})(exports.TileRenderEngine || (exports.TileRenderEngine = {}));
|
|
38714
39685
|
|
|
39686
|
+
// Builds simple meshes into binary glTF in code. Nothing here imports Cesium, so it can move to bruce-models as is.
|
|
39687
|
+
const GLB_MAGIC = 0x46546C67;
|
|
39688
|
+
const GLB_CHUNK_JSON = 0x4E4F534A;
|
|
39689
|
+
const GLB_CHUNK_BIN = 0x004E4942;
|
|
39690
|
+
const GL_ARRAY_BUFFER = 34962;
|
|
39691
|
+
const GL_ELEMENT_ARRAY_BUFFER = 34963;
|
|
39692
|
+
const GL_UNSIGNED_SHORT = 5123;
|
|
39693
|
+
const GL_UNSIGNED_INT = 5125;
|
|
39694
|
+
const GL_FLOAT = 5126;
|
|
39695
|
+
const GL_TRIANGLES = 4;
|
|
39696
|
+
(function (GltfGenerator) {
|
|
39697
|
+
/**
|
|
39698
|
+
* Vertices and triangles collected by the shape functions, one material's worth.
|
|
39699
|
+
*/
|
|
39700
|
+
class Mesh {
|
|
39701
|
+
constructor() {
|
|
39702
|
+
this.positions = [];
|
|
39703
|
+
this.normals = [];
|
|
39704
|
+
this.indices = [];
|
|
39705
|
+
}
|
|
39706
|
+
AddVertex(position, normal) {
|
|
39707
|
+
this.positions.push(position[0], position[1], position[2]);
|
|
39708
|
+
this.normals.push(normal[0], normal[1], normal[2]);
|
|
39709
|
+
return this.positions.length / 3 - 1;
|
|
39710
|
+
}
|
|
39711
|
+
/**
|
|
39712
|
+
* Adds a triangle wound so its front faces along normal, whichever order the corners come in.
|
|
39713
|
+
* Per corner normals smooth it; without them every corner takes the face normal.
|
|
39714
|
+
*/
|
|
39715
|
+
AddTriangle(a, b, c, normal, corners) {
|
|
39716
|
+
var _a, _b, _c;
|
|
39717
|
+
const ia = this.AddVertex(a, (_a = corners === null || corners === void 0 ? void 0 : corners[0]) !== null && _a !== void 0 ? _a : normal);
|
|
39718
|
+
const ib = this.AddVertex(b, (_b = corners === null || corners === void 0 ? void 0 : corners[1]) !== null && _b !== void 0 ? _b : normal);
|
|
39719
|
+
const ic = this.AddVertex(c, (_c = corners === null || corners === void 0 ? void 0 : corners[2]) !== null && _c !== void 0 ? _c : normal);
|
|
39720
|
+
if (Dot(Cross(Sub(b, a), Sub(c, a)), normal) >= 0) {
|
|
39721
|
+
this.indices.push(ia, ib, ic);
|
|
39722
|
+
}
|
|
39723
|
+
else {
|
|
39724
|
+
this.indices.push(ia, ic, ib);
|
|
39725
|
+
}
|
|
39726
|
+
}
|
|
39727
|
+
/**
|
|
39728
|
+
* Joins three added vertices into a triangle facing along normal, so shapes can share corners.
|
|
39729
|
+
*/
|
|
39730
|
+
AddIndexedTriangle(ia, ib, ic, normal) {
|
|
39731
|
+
const at = (i) => [this.positions[i * 3], this.positions[i * 3 + 1], this.positions[i * 3 + 2]];
|
|
39732
|
+
const a = at(ia);
|
|
39733
|
+
if (Dot(Cross(Sub(at(ib), a), Sub(at(ic), a)), normal) >= 0) {
|
|
39734
|
+
this.indices.push(ia, ib, ic);
|
|
39735
|
+
}
|
|
39736
|
+
else {
|
|
39737
|
+
this.indices.push(ia, ic, ib);
|
|
39738
|
+
}
|
|
39739
|
+
}
|
|
39740
|
+
get VertexCount() {
|
|
39741
|
+
return this.positions.length / 3;
|
|
39742
|
+
}
|
|
39743
|
+
}
|
|
39744
|
+
GltfGenerator.Mesh = Mesh;
|
|
39745
|
+
function Add(a, b) {
|
|
39746
|
+
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
|
39747
|
+
}
|
|
39748
|
+
GltfGenerator.Add = Add;
|
|
39749
|
+
function Sub(a, b) {
|
|
39750
|
+
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
39751
|
+
}
|
|
39752
|
+
GltfGenerator.Sub = Sub;
|
|
39753
|
+
function Scale(a, s) {
|
|
39754
|
+
return [a[0] * s, a[1] * s, a[2] * s];
|
|
39755
|
+
}
|
|
39756
|
+
GltfGenerator.Scale = Scale;
|
|
39757
|
+
function Dot(a, b) {
|
|
39758
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
39759
|
+
}
|
|
39760
|
+
GltfGenerator.Dot = Dot;
|
|
39761
|
+
function Cross(a, b) {
|
|
39762
|
+
return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
|
39763
|
+
}
|
|
39764
|
+
GltfGenerator.Cross = Cross;
|
|
39765
|
+
function Normalize(a) {
|
|
39766
|
+
const length = Math.hypot(a[0], a[1], a[2]);
|
|
39767
|
+
return length ? Scale(a, 1 / length) : [0, 0, 0];
|
|
39768
|
+
}
|
|
39769
|
+
GltfGenerator.Normalize = Normalize;
|
|
39770
|
+
/*
|
|
39771
|
+
* Two unit vectors at right angles to an axis and each other, the first along start where one is given.
|
|
39772
|
+
*/
|
|
39773
|
+
function perpendiculars(axis, start) {
|
|
39774
|
+
const n = Normalize(axis);
|
|
39775
|
+
let u = start ? Sub(start, Scale(n, Dot(start, n))) : null;
|
|
39776
|
+
if (!u || Math.hypot(u[0], u[1], u[2]) < 1e-9) {
|
|
39777
|
+
u = Math.abs(n[0]) < 0.9 ? Cross(n, [1, 0, 0]) : Cross(n, [0, 1, 0]);
|
|
39778
|
+
}
|
|
39779
|
+
u = Normalize(u);
|
|
39780
|
+
return [u, Cross(n, u)];
|
|
39781
|
+
}
|
|
39782
|
+
/*
|
|
39783
|
+
* The point on a circle around centre at a given turn, in the plane the two perpendiculars span.
|
|
39784
|
+
*/
|
|
39785
|
+
function onCircle(centre, u, v, radius, angle) {
|
|
39786
|
+
return Add(centre, Add(Scale(u, Math.cos(angle) * radius), Scale(v, Math.sin(angle) * radius)));
|
|
39787
|
+
}
|
|
39788
|
+
/**
|
|
39789
|
+
* Adds a flat disc facing along normal.
|
|
39790
|
+
*/
|
|
39791
|
+
function AddDisc(mesh, params) {
|
|
39792
|
+
const { centre, radius, sides } = params;
|
|
39793
|
+
const normal = Normalize(params.normal);
|
|
39794
|
+
const [u, v] = perpendiculars(normal, params.start);
|
|
39795
|
+
for (let i = 0; i < sides; i++) {
|
|
39796
|
+
const a = onCircle(centre, u, v, radius, i / sides * Math.PI * 2);
|
|
39797
|
+
const b = onCircle(centre, u, v, radius, (i + 1) / sides * Math.PI * 2);
|
|
39798
|
+
mesh.AddTriangle(centre, a, b, normal);
|
|
39799
|
+
}
|
|
39800
|
+
}
|
|
39801
|
+
GltfGenerator.AddDisc = AddDisc;
|
|
39802
|
+
/**
|
|
39803
|
+
* Adds a smooth sided cylinder between two points, closed at the start by default.
|
|
39804
|
+
*/
|
|
39805
|
+
function AddCylinder(mesh, params) {
|
|
39806
|
+
const { start, end, radius, sides } = params;
|
|
39807
|
+
const axis = Normalize(Sub(end, start));
|
|
39808
|
+
const [u, v] = perpendiculars(axis);
|
|
39809
|
+
for (let i = 0; i < sides; i++) {
|
|
39810
|
+
const angleA = i / sides * Math.PI * 2;
|
|
39811
|
+
const angleB = (i + 1) / sides * Math.PI * 2;
|
|
39812
|
+
const outA = onCircle([0, 0, 0], u, v, 1, angleA);
|
|
39813
|
+
const outB = onCircle([0, 0, 0], u, v, 1, angleB);
|
|
39814
|
+
const a0 = onCircle(start, u, v, radius, angleA);
|
|
39815
|
+
const b0 = onCircle(start, u, v, radius, angleB);
|
|
39816
|
+
const a1 = onCircle(end, u, v, radius, angleA);
|
|
39817
|
+
const b1 = onCircle(end, u, v, radius, angleB);
|
|
39818
|
+
const face = Normalize(Add(outA, outB));
|
|
39819
|
+
mesh.AddTriangle(a0, b0, b1, face, [outA, outB, outB]);
|
|
39820
|
+
mesh.AddTriangle(a0, b1, a1, face, [outA, outB, outA]);
|
|
39821
|
+
}
|
|
39822
|
+
if (params.capStart != false) {
|
|
39823
|
+
AddDisc(mesh, { centre: start, normal: Scale(axis, -1), radius, sides });
|
|
39824
|
+
}
|
|
39825
|
+
if (params.capEnd) {
|
|
39826
|
+
AddDisc(mesh, { centre: end, normal: axis, radius, sides });
|
|
39827
|
+
}
|
|
39828
|
+
}
|
|
39829
|
+
GltfGenerator.AddCylinder = AddCylinder;
|
|
39830
|
+
/**
|
|
39831
|
+
* Adds a cone from a base circle to a tip, closed at the base by default.
|
|
39832
|
+
*/
|
|
39833
|
+
function AddCone(mesh, params) {
|
|
39834
|
+
const { base, tip, radius, sides } = params;
|
|
39835
|
+
const toTip = Sub(tip, base);
|
|
39836
|
+
const height = Math.hypot(toTip[0], toTip[1], toTip[2]);
|
|
39837
|
+
const axis = Normalize(toTip);
|
|
39838
|
+
const [u, v] = perpendiculars(axis, params.start);
|
|
39839
|
+
// A side normal leans toward the tip by the slope of the side.
|
|
39840
|
+
const sideNormal = (angle) => Normalize(Add(Scale(onCircle([0, 0, 0], u, v, 1, angle), height), Scale(axis, radius)));
|
|
39841
|
+
for (let i = 0; i < sides; i++) {
|
|
39842
|
+
const angleA = i / sides * Math.PI * 2;
|
|
39843
|
+
const angleB = (i + 1) / sides * Math.PI * 2;
|
|
39844
|
+
const a = onCircle(base, u, v, radius, angleA);
|
|
39845
|
+
const b = onCircle(base, u, v, radius, angleB);
|
|
39846
|
+
const face = sideNormal((angleA + angleB) / 2);
|
|
39847
|
+
mesh.AddTriangle(a, b, tip, face, params.faceted ? undefined : [sideNormal(angleA), sideNormal(angleB), face]);
|
|
39848
|
+
}
|
|
39849
|
+
if (params.cap != false) {
|
|
39850
|
+
AddDisc(mesh, { centre: base, normal: Scale(axis, -1), radius, sides, start: params.start });
|
|
39851
|
+
}
|
|
39852
|
+
}
|
|
39853
|
+
GltfGenerator.AddCone = AddCone;
|
|
39854
|
+
/**
|
|
39855
|
+
* Adds a smooth ring of tube around an axis through centre.
|
|
39856
|
+
*/
|
|
39857
|
+
function AddTorus(mesh, params) {
|
|
39858
|
+
const { centre, radius, tube, segments, sides } = params;
|
|
39859
|
+
const axis = Normalize(params.axis);
|
|
39860
|
+
const [u, v] = perpendiculars(axis);
|
|
39861
|
+
// A grid of shared corners, wrapping round both ways.
|
|
39862
|
+
const first = mesh.VertexCount;
|
|
39863
|
+
for (let i = 0; i < segments; i++) {
|
|
39864
|
+
const out = onCircle([0, 0, 0], u, v, 1, i / segments * Math.PI * 2);
|
|
39865
|
+
for (let j = 0; j < sides; j++) {
|
|
39866
|
+
const turn = j / sides * Math.PI * 2;
|
|
39867
|
+
const normal = Add(Scale(out, Math.cos(turn)), Scale(axis, Math.sin(turn)));
|
|
39868
|
+
mesh.AddVertex(Add(Add(centre, Scale(out, radius)), Scale(normal, tube)), normal);
|
|
39869
|
+
}
|
|
39870
|
+
}
|
|
39871
|
+
const at = (i, j) => first + (i % segments) * sides + (j % sides);
|
|
39872
|
+
const normalAt = (i) => [mesh.normals[i * 3], mesh.normals[i * 3 + 1], mesh.normals[i * 3 + 2]];
|
|
39873
|
+
for (let i = 0; i < segments; i++) {
|
|
39874
|
+
for (let j = 0; j < sides; j++) {
|
|
39875
|
+
const quad = [at(i, j), at(i + 1, j), at(i + 1, j + 1), at(i, j + 1)];
|
|
39876
|
+
const normal = Normalize(Add(normalAt(quad[0]), normalAt(quad[2])));
|
|
39877
|
+
mesh.AddIndexedTriangle(quad[0], quad[1], quad[2], normal);
|
|
39878
|
+
mesh.AddIndexedTriangle(quad[0], quad[2], quad[3], normal);
|
|
39879
|
+
}
|
|
39880
|
+
}
|
|
39881
|
+
}
|
|
39882
|
+
GltfGenerator.AddTorus = AddTorus;
|
|
39883
|
+
/**
|
|
39884
|
+
* Adds a flat convex outline, given in a plane's own across and up coordinates, facing along normal.
|
|
39885
|
+
*/
|
|
39886
|
+
function AddConvexOutline(mesh, params) {
|
|
39887
|
+
const { points, origin, across, up } = params;
|
|
39888
|
+
const normal = Normalize(params.normal);
|
|
39889
|
+
const at = ([x, y]) => Add(origin, Add(Scale(across, x), Scale(up, y)));
|
|
39890
|
+
for (let i = 1; i < points.length - 1; i++) {
|
|
39891
|
+
mesh.AddTriangle(at(points[0]), at(points[i]), at(points[i + 1]), normal);
|
|
39892
|
+
}
|
|
39893
|
+
}
|
|
39894
|
+
GltfGenerator.AddConvexOutline = AddConvexOutline;
|
|
39895
|
+
function align4(length) {
|
|
39896
|
+
return Math.ceil(length / 4) * 4;
|
|
39897
|
+
}
|
|
39898
|
+
function bounds(positions) {
|
|
39899
|
+
const min = [Infinity, Infinity, Infinity];
|
|
39900
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
39901
|
+
for (let i = 0; i < positions.length; i++) {
|
|
39902
|
+
const k = i % 3;
|
|
39903
|
+
min[k] = Math.min(min[k], positions[i]);
|
|
39904
|
+
max[k] = Math.max(max[k], positions[i]);
|
|
39905
|
+
}
|
|
39906
|
+
return { min, max };
|
|
39907
|
+
}
|
|
39908
|
+
/**
|
|
39909
|
+
* Packs a mesh into GLB bytes, under a chain of node matrices (outermost first) when given.
|
|
39910
|
+
*/
|
|
39911
|
+
function ToGlb(params) {
|
|
39912
|
+
var _a, _b, _c;
|
|
39913
|
+
const { mesh, material } = params;
|
|
39914
|
+
const count = mesh.VertexCount;
|
|
39915
|
+
const wide = count > 0xFFFF;
|
|
39916
|
+
const IndexArray = wide ? Uint32Array : Uint16Array;
|
|
39917
|
+
const indexLength = mesh.indices.length * IndexArray.BYTES_PER_ELEMENT;
|
|
39918
|
+
const indexBytes = align4(indexLength);
|
|
39919
|
+
const vertexBytes = count * 12;
|
|
39920
|
+
const bin = new ArrayBuffer(indexBytes + vertexBytes * 2);
|
|
39921
|
+
new IndexArray(bin, 0, mesh.indices.length).set(mesh.indices);
|
|
39922
|
+
new Float32Array(bin, indexBytes, count * 3).set(mesh.positions);
|
|
39923
|
+
new Float32Array(bin, indexBytes + vertexBytes, count * 3).set(mesh.normals);
|
|
39924
|
+
// The mesh node is last, each matrix node holding the next.
|
|
39925
|
+
const matrices = (_a = params.matrices) !== null && _a !== void 0 ? _a : [];
|
|
39926
|
+
const nodes = matrices.map((matrix, i) => ({ matrix, children: [i + 1] }));
|
|
39927
|
+
nodes.push({ mesh: 0 });
|
|
39928
|
+
const json = {
|
|
39929
|
+
asset: { version: "2.0", generator: "bruce-cesium GltfGenerator" },
|
|
39930
|
+
scene: 0,
|
|
39931
|
+
scenes: [{ nodes: [0] }],
|
|
39932
|
+
nodes,
|
|
39933
|
+
meshes: [{ primitives: [{ attributes: { POSITION: 1, NORMAL: 2 }, indices: 0, material: 0, mode: GL_TRIANGLES }] }],
|
|
39934
|
+
materials: [{
|
|
39935
|
+
pbrMetallicRoughness: {
|
|
39936
|
+
baseColorFactor: material.color,
|
|
39937
|
+
metallicFactor: (_b = material.metallic) !== null && _b !== void 0 ? _b : 0,
|
|
39938
|
+
roughnessFactor: (_c = material.roughness) !== null && _c !== void 0 ? _c : 1
|
|
39939
|
+
},
|
|
39940
|
+
doubleSided: Boolean(material.doubleSided)
|
|
39941
|
+
}],
|
|
39942
|
+
buffers: [{ byteLength: bin.byteLength }],
|
|
39943
|
+
bufferViews: [
|
|
39944
|
+
{ buffer: 0, byteOffset: 0, byteLength: indexLength, target: GL_ELEMENT_ARRAY_BUFFER },
|
|
39945
|
+
{ buffer: 0, byteOffset: indexBytes, byteLength: vertexBytes, target: GL_ARRAY_BUFFER },
|
|
39946
|
+
{ buffer: 0, byteOffset: indexBytes + vertexBytes, byteLength: vertexBytes, target: GL_ARRAY_BUFFER }
|
|
39947
|
+
],
|
|
39948
|
+
accessors: [
|
|
39949
|
+
{ bufferView: 0, componentType: wide ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, count: mesh.indices.length, type: "SCALAR" },
|
|
39950
|
+
{ bufferView: 1, componentType: GL_FLOAT, count, type: "VEC3", ...bounds(mesh.positions) },
|
|
39951
|
+
{ bufferView: 2, componentType: GL_FLOAT, count, type: "VEC3" }
|
|
39952
|
+
]
|
|
39953
|
+
};
|
|
39954
|
+
let text = JSON.stringify(json);
|
|
39955
|
+
text += " ".repeat(align4(text.length) - text.length);
|
|
39956
|
+
const jsonBytes = new TextEncoder().encode(text);
|
|
39957
|
+
const glb = new Uint8Array(20 + jsonBytes.length + 8 + bin.byteLength);
|
|
39958
|
+
const view = new DataView(glb.buffer);
|
|
39959
|
+
view.setUint32(0, GLB_MAGIC, true);
|
|
39960
|
+
view.setUint32(4, 2, true);
|
|
39961
|
+
view.setUint32(8, glb.length, true);
|
|
39962
|
+
view.setUint32(12, jsonBytes.length, true);
|
|
39963
|
+
view.setUint32(16, GLB_CHUNK_JSON, true);
|
|
39964
|
+
glb.set(jsonBytes, 20);
|
|
39965
|
+
const binStart = 20 + jsonBytes.length;
|
|
39966
|
+
view.setUint32(binStart, bin.byteLength, true);
|
|
39967
|
+
view.setUint32(binStart + 4, GLB_CHUNK_BIN, true);
|
|
39968
|
+
glb.set(new Uint8Array(bin), binStart + 8);
|
|
39969
|
+
return glb;
|
|
39970
|
+
}
|
|
39971
|
+
GltfGenerator.ToGlb = ToGlb;
|
|
39972
|
+
/**
|
|
39973
|
+
* Packs a mesh into a GLB data URI, which loads as a model url with nothing to fetch.
|
|
39974
|
+
*/
|
|
39975
|
+
function ToDataUri(params) {
|
|
39976
|
+
const glb = ToGlb(params);
|
|
39977
|
+
let binary = "";
|
|
39978
|
+
for (let i = 0; i < glb.length; i += 0x8000) {
|
|
39979
|
+
binary += String.fromCharCode.apply(null, glb.subarray(i, i + 0x8000));
|
|
39980
|
+
}
|
|
39981
|
+
return "data:model/gltf-binary;base64," + btoa(binary);
|
|
39982
|
+
}
|
|
39983
|
+
GltfGenerator.ToDataUri = ToDataUri;
|
|
39984
|
+
})(exports.GltfGenerator || (exports.GltfGenerator = {}));
|
|
39985
|
+
|
|
39986
|
+
// Sides on an arrow's shaft and cone.
|
|
39987
|
+
const ARROW_SIDES = 16;
|
|
39988
|
+
const SHAFT_RADIUS = 2.4;
|
|
39989
|
+
const SHAFT_LENGTH = 265;
|
|
39990
|
+
const CONE_RADIUS = 10;
|
|
39991
|
+
const CONE_BASE = 264.6;
|
|
39992
|
+
const CONE_TIP = 306.3;
|
|
39993
|
+
const RING_RADIUS = 210;
|
|
39994
|
+
const RING_TUBE = 1;
|
|
39995
|
+
const RING_SEGMENTS = 60;
|
|
39996
|
+
const RING_SIDES = 10;
|
|
39997
|
+
// The ring's two arrowheads sit either side of its 45 degree mark, each pointing back toward it.
|
|
39998
|
+
const RING_MARK_ANGLES = [137.64, -47.64];
|
|
39999
|
+
const RING_MARK_LENGTH = 5.82;
|
|
40000
|
+
const RING_MARK_CENTRE = 209.8;
|
|
40001
|
+
const RING_MARK_RADIUS = 8.5;
|
|
40002
|
+
const RING_MARK_SIDES = 8;
|
|
40003
|
+
// Turns the original models' Y up node into the Z up frame the pieces were drawn in.
|
|
40004
|
+
const ROOT_MATRIX = [1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1];
|
|
40005
|
+
// Points a piece drawn along +Z down another axis, as the original models' mesh nodes did.
|
|
40006
|
+
const TO_X_MATRIX = [0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1];
|
|
40007
|
+
const TO_Y_ARROW_MATRIX = [0, 0, -1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1];
|
|
40008
|
+
const TO_Y_RING_MATRIX = [1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1];
|
|
40009
|
+
const RED = { color: [1, 0, 0, 1] };
|
|
40010
|
+
const GREEN = { color: [0.2196, 0.5216, 0.1647, 1] };
|
|
40011
|
+
const BLUE = { color: [0.1608, 0.3843, 0.698, 1] };
|
|
40012
|
+
const LETTER_X = {
|
|
40013
|
+
across: [0, 1, 0],
|
|
40014
|
+
facing: [-1, 0, 0],
|
|
40015
|
+
strokes: [
|
|
40016
|
+
[[16.4, 318.6], [16.4, 324.2], [-19.4, 349.8], [-19.4, 343.9]],
|
|
40017
|
+
[[-19.4, 316.9], [-19.4, 322.6], [16.4, 348.8], [16.4, 343.6]]
|
|
40018
|
+
]
|
|
40019
|
+
};
|
|
40020
|
+
const LETTER_Y = {
|
|
40021
|
+
across: [0, 1, 0],
|
|
40022
|
+
facing: [-1, 0, 0],
|
|
40023
|
+
strokes: [
|
|
40024
|
+
[[-1.9, 320.1], [2.8, 320.1], [2.8, 337.7], [-1.9, 337.7]],
|
|
40025
|
+
[[-1.9, 335.3], [2.8, 335.3], [-10.7, 355.9], [-16.2, 355.9]],
|
|
40026
|
+
[[-1.9, 335.3], [2.8, 335.3], [16.6, 355.9], [10.9, 355.9]]
|
|
40027
|
+
]
|
|
40028
|
+
};
|
|
40029
|
+
const LETTER_Z = {
|
|
40030
|
+
across: [1, 0, 0],
|
|
40031
|
+
facing: [0, -1, 0],
|
|
40032
|
+
strokes: [
|
|
40033
|
+
[[-14.6, 319], [13.7, 319], [13.7, 323.3], [-14.6, 323.3]],
|
|
40034
|
+
[[-14.6, 323.3], [-9.1, 323.3], [13.1, 350.6], [7.5, 350.6]],
|
|
40035
|
+
[[-12.5, 350.6], [13.1, 350.6], [13.1, 354.8], [-12.5, 354.8]]
|
|
40036
|
+
]
|
|
40037
|
+
};
|
|
40038
|
+
/*
|
|
40039
|
+
* An axis arrow along +Z: shaft, cone and the axis letter past the tip.
|
|
40040
|
+
*/
|
|
40041
|
+
function arrowMesh(letter) {
|
|
40042
|
+
const mesh = new exports.GltfGenerator.Mesh();
|
|
40043
|
+
exports.GltfGenerator.AddCylinder(mesh, { start: [0, 0, 0], end: [0, 0, SHAFT_LENGTH], radius: SHAFT_RADIUS, sides: ARROW_SIDES });
|
|
40044
|
+
exports.GltfGenerator.AddCone(mesh, { base: [0, 0, CONE_BASE], tip: [0, 0, CONE_TIP], radius: CONE_RADIUS, sides: ARROW_SIDES });
|
|
40045
|
+
for (const stroke of letter.strokes) {
|
|
40046
|
+
exports.GltfGenerator.AddConvexOutline(mesh, { points: stroke, origin: [0, 0, 0], across: letter.across, up: [0, 0, 1], normal: letter.facing });
|
|
40047
|
+
}
|
|
40048
|
+
return mesh;
|
|
40049
|
+
}
|
|
40050
|
+
function onRing(degrees, radius) {
|
|
40051
|
+
const angle = degrees * Math.PI / 180;
|
|
40052
|
+
return [Math.cos(angle) * radius, Math.sin(angle) * radius, 0];
|
|
40053
|
+
}
|
|
40054
|
+
/*
|
|
40055
|
+
* A rotation ring about +Z with its two arrowheads.
|
|
40056
|
+
*/
|
|
40057
|
+
function ringMesh() {
|
|
40058
|
+
const mesh = new exports.GltfGenerator.Mesh();
|
|
40059
|
+
exports.GltfGenerator.AddTorus(mesh, {
|
|
40060
|
+
centre: [0, 0, 0],
|
|
40061
|
+
axis: [0, 0, 1],
|
|
40062
|
+
radius: RING_RADIUS,
|
|
40063
|
+
tube: RING_TUBE,
|
|
40064
|
+
segments: RING_SEGMENTS,
|
|
40065
|
+
sides: RING_SIDES
|
|
40066
|
+
});
|
|
40067
|
+
for (const degrees of RING_MARK_ANGLES) {
|
|
40068
|
+
const toward = degrees > 45 ? -RING_MARK_LENGTH : RING_MARK_LENGTH;
|
|
40069
|
+
exports.GltfGenerator.AddCone(mesh, {
|
|
40070
|
+
base: onRing(degrees, RING_MARK_CENTRE),
|
|
40071
|
+
tip: onRing(degrees + toward, RING_MARK_CENTRE),
|
|
40072
|
+
radius: RING_MARK_RADIUS,
|
|
40073
|
+
sides: RING_MARK_SIDES,
|
|
40074
|
+
faceted: true,
|
|
40075
|
+
// A rim corner points at the ring's centre, as the original arrowheads' did.
|
|
40076
|
+
start: onRing(degrees, 1)
|
|
40077
|
+
});
|
|
40078
|
+
}
|
|
40079
|
+
return mesh;
|
|
40080
|
+
}
|
|
40081
|
+
(function (GltfGizmo) {
|
|
40082
|
+
let EPiece;
|
|
40083
|
+
(function (EPiece) {
|
|
40084
|
+
EPiece["X"] = "x";
|
|
40085
|
+
EPiece["Y"] = "y";
|
|
40086
|
+
EPiece["Z"] = "z";
|
|
40087
|
+
EPiece["XRotation"] = "x-rot";
|
|
40088
|
+
EPiece["YRotation"] = "y-rot";
|
|
40089
|
+
EPiece["ZRotation"] = "z-rot";
|
|
40090
|
+
})(EPiece = GltfGizmo.EPiece || (GltfGizmo.EPiece = {}));
|
|
40091
|
+
const BUILDERS = {
|
|
40092
|
+
[EPiece.X]: () => exports.GltfGenerator.ToDataUri({ mesh: arrowMesh(LETTER_X), material: RED, matrices: [ROOT_MATRIX, TO_X_MATRIX] }),
|
|
40093
|
+
[EPiece.Y]: () => exports.GltfGenerator.ToDataUri({ mesh: arrowMesh(LETTER_Y), material: GREEN, matrices: [ROOT_MATRIX, TO_Y_ARROW_MATRIX] }),
|
|
40094
|
+
[EPiece.Z]: () => exports.GltfGenerator.ToDataUri({ mesh: arrowMesh(LETTER_Z), material: BLUE, matrices: [ROOT_MATRIX] }),
|
|
40095
|
+
[EPiece.XRotation]: () => exports.GltfGenerator.ToDataUri({ mesh: ringMesh(), material: RED, matrices: [ROOT_MATRIX, TO_X_MATRIX] }),
|
|
40096
|
+
[EPiece.YRotation]: () => exports.GltfGenerator.ToDataUri({ mesh: ringMesh(), material: GREEN, matrices: [ROOT_MATRIX, TO_Y_RING_MATRIX] }),
|
|
40097
|
+
[EPiece.ZRotation]: () => exports.GltfGenerator.ToDataUri({ mesh: ringMesh(), material: BLUE, matrices: [ROOT_MATRIX] })
|
|
40098
|
+
};
|
|
40099
|
+
// Built pieces, so every gizmo shares one string per piece and CesiumJS's model cache sees the same url.
|
|
40100
|
+
const built = {};
|
|
40101
|
+
/**
|
|
40102
|
+
* Returns a piece's model as a data URI, to pass wherever a model url is taken.
|
|
40103
|
+
*/
|
|
40104
|
+
function GetUri(piece) {
|
|
40105
|
+
if (!built[piece]) {
|
|
40106
|
+
built[piece] = BUILDERS[piece]();
|
|
40107
|
+
}
|
|
40108
|
+
return built[piece];
|
|
40109
|
+
}
|
|
40110
|
+
GltfGizmo.GetUri = GetUri;
|
|
40111
|
+
})(exports.GltfGizmo || (exports.GltfGizmo = {}));
|
|
40112
|
+
|
|
40113
|
+
// viewer[CLASS_INSTANCE_KEY] will point at an instance of MapGridHandler.
|
|
40114
|
+
const CLASS_INSTANCE_KEY = "nextspace-navigator-grid-handler";
|
|
40115
|
+
// viewer[UPDATE_MARKER_KEY] will point at a number that is incremented every time the grid is updated.
|
|
40116
|
+
// This is used to determine if the grid is still valid or if it needs to be redrawn.
|
|
40117
|
+
const UPDATE_MARKER_KEY = "nextspace-marker-grid";
|
|
40118
|
+
// viewer[LAST_SETTINGS_KEY] will point at the last settings used for the grid.
|
|
40119
|
+
const LAST_SETTINGS_KEY = "nextspace-navigator-last-settings-grid";
|
|
40120
|
+
/*
|
|
40121
|
+
* The point a distance in metres along a heading in degrees from another, over a spherical earth.
|
|
40122
|
+
*/
|
|
40123
|
+
function offsetPoint$1(point, distance, heading) {
|
|
40124
|
+
// Radius of earth.
|
|
40125
|
+
const radius = 6371e3;
|
|
40126
|
+
const δ = distance / radius;
|
|
40127
|
+
const θ = Cesium.Math.toRadians(heading);
|
|
40128
|
+
const φ1 = Cesium.Math.toRadians(point.latitude);
|
|
40129
|
+
const λ1 = Cesium.Math.toRadians(point.longitude);
|
|
40130
|
+
const sinφ2 = Math.sin(φ1) * Math.cos(δ) + Math.cos(φ1) * Math.sin(δ) * Math.cos(θ);
|
|
40131
|
+
const φ2 = Math.asin(sinφ2);
|
|
40132
|
+
const y = Math.sin(θ) * Math.sin(δ) * Math.cos(φ1);
|
|
40133
|
+
const x = Math.cos(δ) - Math.sin(φ1) * sinφ2;
|
|
40134
|
+
const λ2 = λ1 + Math.atan2(y, x);
|
|
40135
|
+
return {
|
|
40136
|
+
altitude: point.altitude,
|
|
40137
|
+
latitude: Cesium.Math.toDegrees(φ2),
|
|
40138
|
+
longitude: Cesium.Math.toDegrees(λ2)
|
|
40139
|
+
};
|
|
40140
|
+
}
|
|
40141
|
+
/**
|
|
40142
|
+
* Handler for drawing a grid of lines on the map with a specified spacing.
|
|
40143
|
+
* Initially this grid is for model-space viewing which means we start at 0,0 lat/lon and have it expand from there.
|
|
40144
|
+
* However, it should be a general-use tool for drawing a grid anywhere.
|
|
40145
|
+
*
|
|
40146
|
+
* It reacts to camera changes and draws the grid within view with padding.
|
|
40147
|
+
* This helps avoid performance issues around drawing too many lines.
|
|
40148
|
+
*/
|
|
40149
|
+
class MapGridHandler {
|
|
40150
|
+
constructor(viewer, spacingInMeters = 100, modelSpace = false) {
|
|
40151
|
+
this.gridCenterGizmoPieces = [];
|
|
40152
|
+
this.maxCacheSize = 1000;
|
|
40153
|
+
// === Thresholds for grid stability.
|
|
40154
|
+
// As a fraction of current grid extent.
|
|
40155
|
+
this.CAMERA_MOVE_THRESHOLD = 0.5;
|
|
40156
|
+
// 40% change in camera height triggers redraw.
|
|
40157
|
+
this.HEIGHT_CHANGE_THRESHOLD = 0.4;
|
|
40158
|
+
// 40% change in grid extent triggers redraw.
|
|
40159
|
+
this.GRID_EXTENT_CHANGE_THRESHOLD = 0.4;
|
|
40160
|
+
// 5km - distance that triggers origin reset.
|
|
40161
|
+
this.EXTREME_DISTANCE_THRESHOLD = 5000;
|
|
40162
|
+
// Grid limits to prevent drawing too many lines.
|
|
40163
|
+
this.MAX_LINES = 500;
|
|
40164
|
+
// 50% padding around visible area.
|
|
40165
|
+
this.VIEW_PADDING_FACTOR = 0.5;
|
|
40166
|
+
// Minimum pixels between grid lines to show them.
|
|
40167
|
+
this.MIN_GRID_PIXEL_SIZE = 20;
|
|
40168
|
+
// Base ratio - will be adjusted dynamically based on spacing size and camera height.
|
|
40169
|
+
this.BASE_MAX_VISIBILITY_RATIO = 30;
|
|
40170
|
+
// Height to spacing ratio.
|
|
40171
|
+
this.HEIGHT_TO_SPACING_RATIO = 100;
|
|
40172
|
+
// === Label settings
|
|
40173
|
+
// Label every nth intersection.
|
|
40174
|
+
this.LABEL_INTERVAL = 2;
|
|
40175
|
+
// Maximum number of labels to show.
|
|
40176
|
+
this.MAX_LABELS = 100;
|
|
40177
|
+
// Spacing levels to use for adaptive spacing.
|
|
40178
|
+
this.SPACING_LEVELS = [
|
|
40179
|
+
1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 300, 400, 500, 1000, 2000, 2500, 5000, 10000, 20000, 25000, 50000, 100000
|
|
40180
|
+
];
|
|
40181
|
+
// Minimum zoom distance to prevent precision issues.
|
|
40182
|
+
this.MINIMUM_ZOOM_DISTANCE = 40;
|
|
40183
|
+
// Base spacing to use for adaptive spacing.
|
|
40184
|
+
this.BASE_SPACING = 1;
|
|
40185
|
+
// Maximum number of attempts to find a suitable spacing.
|
|
40186
|
+
this.MAX_ADAPTIVE_ATTEMPTS = 5;
|
|
40187
|
+
this.viewer = viewer;
|
|
40188
|
+
this.spacingInMeters = spacingInMeters;
|
|
40189
|
+
this.isUpdating = false;
|
|
40190
|
+
this.modelSpace = modelSpace;
|
|
40191
|
+
this.lastCameraPosition = null;
|
|
40192
|
+
this.fixedGridOrigin = null;
|
|
40193
|
+
this.lastGridExtent = 0;
|
|
40194
|
+
this.offsetCache = new Map();
|
|
40195
|
+
this.gridPrimitives = new Cesium.PrimitiveCollection();
|
|
40196
|
+
this.viewer.scene.primitives.add(this.gridPrimitives);
|
|
40197
|
+
this.currentMarker = viewer[UPDATE_MARKER_KEY] || 1;
|
|
40198
|
+
const isMac = typeof navigator !== "undefined" && (navigator.platform.toUpperCase().indexOf("MAC") !== -1 ||
|
|
40199
|
+
navigator.userAgent.toUpperCase().indexOf("MAC") !== -1);
|
|
40200
|
+
if (modelSpace && isMac) {
|
|
40201
|
+
console.log("[MAC-FIX] Applying camera distance limits for Mac");
|
|
40202
|
+
viewer.scene.screenSpaceCameraController.minimumZoomDistance = this.MINIMUM_ZOOM_DISTANCE;
|
|
40203
|
+
}
|
|
40204
|
+
const manager = exports.ViewerUtils.GetManager({
|
|
40205
|
+
viewer: this.viewer,
|
|
40206
|
+
createIfMissing: false
|
|
40207
|
+
});
|
|
40208
|
+
if (manager) {
|
|
40209
|
+
this.cameraChangeRemoval = manager.Monitor.Updated().Subscribe(() => {
|
|
40210
|
+
this.onCameraChanged();
|
|
40211
|
+
});
|
|
40212
|
+
}
|
|
40213
|
+
// Initial grid draw.
|
|
40214
|
+
this.onCameraChanged();
|
|
40215
|
+
}
|
|
40216
|
+
Update(spacingInMeters, modelSpace) {
|
|
40217
|
+
this.currentMarker = this.viewer[UPDATE_MARKER_KEY] || 1;
|
|
40218
|
+
let needsUpdate = false;
|
|
40219
|
+
if (this.spacingInMeters !== spacingInMeters) {
|
|
40220
|
+
this.spacingInMeters = spacingInMeters;
|
|
40221
|
+
needsUpdate = true;
|
|
40222
|
+
}
|
|
40223
|
+
if (modelSpace !== undefined && this.modelSpace !== modelSpace) {
|
|
40224
|
+
this.modelSpace = modelSpace;
|
|
40225
|
+
needsUpdate = true;
|
|
40226
|
+
}
|
|
40227
|
+
if (needsUpdate) {
|
|
40228
|
+
this.clearGrid();
|
|
40229
|
+
// When spacing or model space changes, clear the cached origin to force recalculation
|
|
40230
|
+
// This ensures grid alignment at the new spacing.
|
|
40231
|
+
this.fixedGridOrigin = null;
|
|
40232
|
+
this.offsetCache.clear();
|
|
40233
|
+
this.onCameraChanged();
|
|
40234
|
+
}
|
|
40235
|
+
}
|
|
40236
|
+
Dispose() {
|
|
40237
|
+
this.clearGrid();
|
|
40238
|
+
this.clearGizmoPieces();
|
|
40239
|
+
if (this.cameraChangeRemoval) {
|
|
40240
|
+
this.cameraChangeRemoval();
|
|
40241
|
+
this.cameraChangeRemoval = null;
|
|
40242
|
+
}
|
|
40243
|
+
if (this.gridPrimitives) {
|
|
40244
|
+
this.viewer.scene.primitives.remove(this.gridPrimitives);
|
|
40245
|
+
this.gridPrimitives = null;
|
|
40246
|
+
}
|
|
40247
|
+
if (this.viewer) {
|
|
40248
|
+
this.viewer = null;
|
|
40249
|
+
}
|
|
40250
|
+
this.offsetCache.clear();
|
|
40251
|
+
this.fixedGridOrigin = null;
|
|
40252
|
+
}
|
|
40253
|
+
clearGrid() {
|
|
40254
|
+
if (this.gridPrimitives) {
|
|
40255
|
+
this.gridPrimitives.removeAll();
|
|
40256
|
+
this.gridLines = null;
|
|
40257
|
+
this.gridLabels = null;
|
|
40258
|
+
}
|
|
40259
|
+
}
|
|
40260
|
+
calculateAdaptiveSpacing(cameraHeight, baseSpacing) {
|
|
40261
|
+
// Define spacing levels that look good (powers of 2, 5, and 10)
|
|
40262
|
+
const spacingLevels = this.SPACING_LEVELS;
|
|
40263
|
+
// Ensure base spacing is at least 1m
|
|
40264
|
+
const adjustedBaseSpacing = Math.max(this.BASE_SPACING, baseSpacing);
|
|
40265
|
+
// For very close cameras, use much smaller spacing regardless of base
|
|
40266
|
+
if (cameraHeight < 10) {
|
|
40267
|
+
// Find the smallest reasonable spacing for this height
|
|
40268
|
+
// Rule: spacing should be roughly 1/20th to 1/50th of camera height, but at least 1m
|
|
40269
|
+
const targetSpacing = Math.max(this.BASE_SPACING, cameraHeight / 30);
|
|
40270
|
+
// Find the closest spacing level to our target
|
|
40271
|
+
let bestSpacing = this.BASE_SPACING; // Start with minimum
|
|
40272
|
+
let bestDifference = Math.abs(1 - targetSpacing);
|
|
40273
|
+
for (const spacing of spacingLevels) {
|
|
40274
|
+
if (spacing >= this.BASE_SPACING) { // Only consider valid spacings
|
|
40275
|
+
const difference = Math.abs(spacing - targetSpacing);
|
|
40276
|
+
if (difference < bestDifference) {
|
|
40277
|
+
bestSpacing = spacing;
|
|
40278
|
+
bestDifference = difference;
|
|
40279
|
+
}
|
|
40280
|
+
}
|
|
40281
|
+
}
|
|
40282
|
+
return bestSpacing;
|
|
40283
|
+
}
|
|
40284
|
+
// For medium distances (10m - 200m), scale more gradually
|
|
40285
|
+
if (cameraHeight < 200) {
|
|
40286
|
+
const targetSpacing = cameraHeight / 10; // More conservative scaling// Find closest spacing level, but prefer going up from base spacing
|
|
40287
|
+
let bestSpacing = adjustedBaseSpacing;
|
|
40288
|
+
let bestDifference = Math.abs(adjustedBaseSpacing - targetSpacing);
|
|
40289
|
+
for (const spacing of spacingLevels) {
|
|
40290
|
+
if (spacing >= Math.min(adjustedBaseSpacing, targetSpacing)) {
|
|
40291
|
+
const difference = Math.abs(spacing - targetSpacing);
|
|
40292
|
+
if (difference < bestDifference) {
|
|
40293
|
+
bestSpacing = spacing;
|
|
40294
|
+
bestDifference = difference;
|
|
40295
|
+
}
|
|
40296
|
+
}
|
|
40297
|
+
}
|
|
40298
|
+
return bestSpacing;
|
|
40299
|
+
}
|
|
40300
|
+
// For far distances (100m+), scale up aggressively
|
|
40301
|
+
const targetSpacing = cameraHeight / 20;
|
|
40302
|
+
// Find the spacing level that's closest to target but not smaller than base
|
|
40303
|
+
let bestSpacing = adjustedBaseSpacing;
|
|
40304
|
+
let bestDifference = Math.abs(adjustedBaseSpacing - targetSpacing);
|
|
40305
|
+
for (const spacing of spacingLevels) {
|
|
40306
|
+
if (spacing >= adjustedBaseSpacing) {
|
|
40307
|
+
const difference = Math.abs(spacing - targetSpacing);
|
|
40308
|
+
if (difference < bestDifference) {
|
|
40309
|
+
bestSpacing = spacing;
|
|
40310
|
+
bestDifference = difference;
|
|
40311
|
+
}
|
|
40312
|
+
}
|
|
40313
|
+
}
|
|
40314
|
+
return bestSpacing;
|
|
40315
|
+
}
|
|
40316
|
+
/*
|
|
40317
|
+
* The point model-space treats as 0,0,0: the UCS in UCS placement, otherwise 0,0 lat/lon at sea level.
|
|
40318
|
+
*/
|
|
40319
|
+
modelSpaceOrigin() {
|
|
40320
|
+
const origin = exports.ViewUtils.IsModelSpaceUcs(this.viewer) ? exports.ViewUtils.GetModelSpaceOrigin(this.viewer) : null;
|
|
40321
|
+
return origin !== null && origin !== void 0 ? origin : { latitude: 0, longitude: 0, altitude: 0 };
|
|
40322
|
+
}
|
|
40323
|
+
/*
|
|
40324
|
+
* The origin as a scene position, where the axis gizmo sits.
|
|
40325
|
+
*/
|
|
40326
|
+
modelSpaceOriginPos3d() {
|
|
40327
|
+
const origin = this.modelSpaceOrigin();
|
|
40328
|
+
return Cesium.Cartesian3.fromDegrees(origin.longitude, origin.latitude, origin.altitude);
|
|
40329
|
+
}
|
|
40330
|
+
drawGizmoPieces() {
|
|
40331
|
+
if (this.gridCenterGizmoPieces.length) {
|
|
40332
|
+
return;
|
|
40333
|
+
}
|
|
40334
|
+
// X.
|
|
40335
|
+
{
|
|
40336
|
+
const pos3d = this.modelSpaceOriginPos3d();
|
|
40337
|
+
const scale = new Cesium.CallbackProperty(() => Cesium.Cartesian3.distance(this.viewer.camera.position, pos3d) * 0.0002, false);
|
|
40338
|
+
const orientation = Cesium.Transforms.headingPitchRollQuaternion(pos3d, new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(90), 0, 0));
|
|
40339
|
+
const entity = this.viewer.entities.add(new Cesium.Entity({
|
|
40340
|
+
model: {
|
|
40341
|
+
uri: exports.GltfGizmo.GetUri(exports.GltfGizmo.EPiece.X),
|
|
40342
|
+
heightReference: Cesium.HeightReference.NONE,
|
|
40343
|
+
shadows: Cesium.ShadowMode.DISABLED,
|
|
40344
|
+
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, Number.MAX_VALUE),
|
|
40345
|
+
scale
|
|
40346
|
+
},
|
|
40347
|
+
position: pos3d,
|
|
40348
|
+
orientation: new Cesium.ConstantProperty(orientation)
|
|
40349
|
+
}));
|
|
40350
|
+
this.gridCenterGizmoPieces.push(entity);
|
|
40351
|
+
}
|
|
40352
|
+
// Y.
|
|
40353
|
+
{
|
|
40354
|
+
const pos3d = this.modelSpaceOriginPos3d();
|
|
40355
|
+
const scale = new Cesium.CallbackProperty(() => Cesium.Cartesian3.distance(this.viewer.camera.position, pos3d) * 0.0002, false);
|
|
40356
|
+
const orientation = Cesium.Transforms.headingPitchRollQuaternion(pos3d, new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(90), 0, 0));
|
|
40357
|
+
const entity = this.viewer.entities.add(new Cesium.Entity({
|
|
40358
|
+
model: {
|
|
40359
|
+
uri: exports.GltfGizmo.GetUri(exports.GltfGizmo.EPiece.Y),
|
|
40360
|
+
heightReference: Cesium.HeightReference.NONE,
|
|
40361
|
+
shadows: Cesium.ShadowMode.DISABLED,
|
|
40362
|
+
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, Number.MAX_VALUE),
|
|
40363
|
+
scale
|
|
40364
|
+
},
|
|
40365
|
+
position: pos3d,
|
|
40366
|
+
orientation: new Cesium.ConstantProperty(orientation)
|
|
40367
|
+
}));
|
|
40368
|
+
this.gridCenterGizmoPieces.push(entity);
|
|
40369
|
+
}
|
|
40370
|
+
// Z.
|
|
40371
|
+
{
|
|
40372
|
+
const pos3d = this.modelSpaceOriginPos3d();
|
|
40373
|
+
const scale = new Cesium.CallbackProperty(() => Cesium.Cartesian3.distance(this.viewer.camera.position, pos3d) * 0.0002, false);
|
|
40374
|
+
const orientation = Cesium.Transforms.headingPitchRollQuaternion(pos3d, new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(90), 0, 0));
|
|
40375
|
+
const entity = this.viewer.entities.add(new Cesium.Entity({
|
|
40376
|
+
model: {
|
|
40377
|
+
uri: exports.GltfGizmo.GetUri(exports.GltfGizmo.EPiece.Z),
|
|
40378
|
+
heightReference: Cesium.HeightReference.NONE,
|
|
40379
|
+
shadows: Cesium.ShadowMode.DISABLED,
|
|
40380
|
+
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, Number.MAX_VALUE),
|
|
40381
|
+
scale
|
|
40382
|
+
},
|
|
40383
|
+
position: pos3d,
|
|
40384
|
+
orientation: new Cesium.ConstantProperty(orientation)
|
|
40385
|
+
}));
|
|
40386
|
+
this.gridCenterGizmoPieces.push(entity);
|
|
40387
|
+
}
|
|
40388
|
+
}
|
|
40389
|
+
clearGizmoPieces() {
|
|
40390
|
+
for (const piece of this.gridCenterGizmoPieces) {
|
|
40391
|
+
if (piece && this.viewer.entities.contains(piece)) {
|
|
40392
|
+
this.viewer.entities.remove(piece);
|
|
40393
|
+
}
|
|
40394
|
+
}
|
|
40395
|
+
this.gridCenterGizmoPieces = [];
|
|
40396
|
+
}
|
|
40397
|
+
onCameraChanged() {
|
|
40398
|
+
if (this.isUpdating) {
|
|
40399
|
+
return;
|
|
40400
|
+
}
|
|
40401
|
+
else if (this.currentMarker !== this.viewer[UPDATE_MARKER_KEY]) {
|
|
40402
|
+
return;
|
|
40403
|
+
}
|
|
40404
|
+
this.isUpdating = true;
|
|
40405
|
+
// Small delay to avoid excessive updates during camera movement.
|
|
40406
|
+
setTimeout(() => {
|
|
40407
|
+
if (this.currentMarker !== this.viewer[UPDATE_MARKER_KEY]) {
|
|
40408
|
+
this.isUpdating = false;
|
|
40409
|
+
return;
|
|
40410
|
+
}
|
|
40411
|
+
try {
|
|
40412
|
+
// The grid goes first, since it notices when the origin has moved and the gizmo has to follow.
|
|
40413
|
+
this.updateGrid();
|
|
40414
|
+
if (this.modelSpace) {
|
|
40415
|
+
this.drawGizmoPieces();
|
|
40416
|
+
}
|
|
40417
|
+
else {
|
|
40418
|
+
this.clearGizmoPieces();
|
|
40419
|
+
}
|
|
40420
|
+
}
|
|
40421
|
+
catch (e) {
|
|
40422
|
+
console.error(e);
|
|
40423
|
+
}
|
|
40424
|
+
finally {
|
|
40425
|
+
this.isUpdating = false;
|
|
40426
|
+
}
|
|
40427
|
+
}, 100);
|
|
40428
|
+
}
|
|
40429
|
+
cachedOffsetPoint(point, distance, heading) {
|
|
40430
|
+
const key = `${point.latitude.toFixed(8)},${point.longitude.toFixed(8)},${distance.toFixed(1)},${heading}`;
|
|
40431
|
+
if (this.offsetCache.has(key)) {
|
|
40432
|
+
return this.offsetCache.get(key);
|
|
40433
|
+
}
|
|
40434
|
+
const result = offsetPoint$1(point, distance, heading);
|
|
40435
|
+
this.offsetCache.set(key, result);
|
|
40436
|
+
if (this.offsetCache.size > this.maxCacheSize) {
|
|
40437
|
+
const keys = Array.from(this.offsetCache.keys());
|
|
40438
|
+
for (let i = 0; i < 100; i++) {
|
|
40439
|
+
this.offsetCache.delete(keys[i]);
|
|
40440
|
+
}
|
|
40441
|
+
}
|
|
40442
|
+
return result;
|
|
40443
|
+
}
|
|
40444
|
+
/**
|
|
40445
|
+
* Calculates a dynamic visibility ratio based on spacing size.
|
|
40446
|
+
* For small grid spacings, we can show the grid from farther away.
|
|
40447
|
+
* For large grid spacings, we need to hide it sooner to prevent performance issues.
|
|
40448
|
+
*/
|
|
40449
|
+
getMaxVisibilityRatio() {
|
|
40450
|
+
if (this.spacingInMeters <= 10) {
|
|
40451
|
+
return this.BASE_MAX_VISIBILITY_RATIO;
|
|
40452
|
+
}
|
|
40453
|
+
if (this.spacingInMeters <= 100) {
|
|
40454
|
+
const factor = 1 - ((this.spacingInMeters - 10) / 90);
|
|
40455
|
+
return this.BASE_MAX_VISIBILITY_RATIO * (0.5 + (factor * 0.5));
|
|
40456
|
+
}
|
|
40457
|
+
const largeGridFactor = Math.min(1, 100 / this.spacingInMeters);
|
|
40458
|
+
return this.BASE_MAX_VISIBILITY_RATIO * 0.5 * largeGridFactor;
|
|
40459
|
+
}
|
|
40460
|
+
updateGrid() {
|
|
40461
|
+
const cameraPosition = this.viewer.camera.position;
|
|
40462
|
+
const cameraCartographic = Cesium.Cartographic.fromCartesian(cameraPosition);
|
|
40463
|
+
const cameraHeight = cameraCartographic.height;
|
|
40464
|
+
let adaptiveSpacing = this.calculateAdaptiveSpacing(cameraHeight, this.spacingInMeters);
|
|
40465
|
+
// Calculate visible area.
|
|
40466
|
+
const visibleArea = this.calculateVisibleArea();
|
|
40467
|
+
if (!visibleArea) {
|
|
40468
|
+
return;
|
|
40469
|
+
}
|
|
40470
|
+
let densityCheckPassed = false;
|
|
40471
|
+
let attempts = 0;
|
|
40472
|
+
while (!densityCheckPassed && attempts < this.MAX_ADAPTIVE_ATTEMPTS) {
|
|
40473
|
+
if (this.isGridDensityAcceptable(visibleArea, cameraHeight, adaptiveSpacing)) {
|
|
40474
|
+
densityCheckPassed = true;
|
|
40475
|
+
}
|
|
40476
|
+
else {
|
|
40477
|
+
adaptiveSpacing *= 2;
|
|
40478
|
+
attempts++;
|
|
40479
|
+
}
|
|
40480
|
+
}
|
|
40481
|
+
// The origin follows the UCS of what is shown, so the grid moves with it when that changes.
|
|
40482
|
+
if (this.modelSpace && this.fixedGridOrigin && !BModels.Carto.IsEqual(this.fixedGridOrigin, this.modelSpaceOrigin())) {
|
|
40483
|
+
this.fixedGridOrigin = null;
|
|
40484
|
+
this.lastCameraPosition = null;
|
|
40485
|
+
this.clearGizmoPieces();
|
|
40486
|
+
}
|
|
40487
|
+
// Calculate grid origin if we don't have one yet.
|
|
40488
|
+
if (!this.fixedGridOrigin) {
|
|
40489
|
+
if (this.modelSpace) {
|
|
40490
|
+
this.fixedGridOrigin = this.modelSpaceOrigin();
|
|
40491
|
+
}
|
|
40492
|
+
else {
|
|
40493
|
+
const centerCartographic = Cesium.Cartographic.fromCartesian(visibleArea.centerCartesian);
|
|
40494
|
+
const centerPoint = {
|
|
40495
|
+
latitude: Cesium.Math.toDegrees(centerCartographic.latitude),
|
|
40496
|
+
longitude: Cesium.Math.toDegrees(centerCartographic.longitude),
|
|
40497
|
+
altitude: 0
|
|
40498
|
+
};
|
|
40499
|
+
// Simple attempt at snapping to the grid, should do something smarter.
|
|
40500
|
+
this.fixedGridOrigin = {
|
|
40501
|
+
latitude: Math.round(centerPoint.latitude * 100000) / 100000,
|
|
40502
|
+
longitude: Math.round(centerPoint.longitude * 100000) / 100000,
|
|
40503
|
+
altitude: 0
|
|
40504
|
+
};
|
|
40505
|
+
}
|
|
40506
|
+
}
|
|
40507
|
+
// Calculate appropriate grid extent based on camera height.
|
|
40508
|
+
// For larger grid spacings, we use a tighter extent to prevent performance issues.
|
|
40509
|
+
const scaledExtentFactor = Math.max(0.5, Math.min(3, 3 - Math.log10(Math.max(1, adaptiveSpacing)) * 0.5));
|
|
40510
|
+
const visibilityRadius = Math.min(cameraHeight * scaledExtentFactor, Infinity);
|
|
40511
|
+
// Check if we should redraw the grid.
|
|
40512
|
+
const shouldRedraw = this.shouldRedrawGrid(cameraPosition, cameraHeight, visibilityRadius, adaptiveSpacing);
|
|
40513
|
+
if (!shouldRedraw) {
|
|
40514
|
+
return;
|
|
40515
|
+
}
|
|
40516
|
+
// Remember current settings for next comparison.
|
|
40517
|
+
this.lastCameraHeight = cameraHeight;
|
|
40518
|
+
this.lastCameraPosition = cameraPosition.clone();
|
|
40519
|
+
this.lastGridExtent = visibilityRadius;
|
|
40520
|
+
// Clear existing grid and create a new one.
|
|
40521
|
+
this.clearGrid();
|
|
40522
|
+
this.gridLines = new Cesium.PolylineCollection();
|
|
40523
|
+
this.gridLabels = new Cesium.LabelCollection({
|
|
40524
|
+
scene: this.viewer.scene
|
|
40525
|
+
});
|
|
40526
|
+
this.gridPrimitives.add(this.gridLines);
|
|
40527
|
+
this.gridPrimitives.add(this.gridLabels);
|
|
40528
|
+
// Generate grid lines and labels using our fixed origin.
|
|
40529
|
+
this.generateGridLines(this.fixedGridOrigin, visibilityRadius, adaptiveSpacing);
|
|
40530
|
+
}
|
|
40531
|
+
shouldRedrawGrid(cameraPosition, cameraHeight, gridExtent, currentSpacing) {
|
|
40532
|
+
// check if the spacing has changed
|
|
40533
|
+
if (this.lastUsedSpacing && this.lastUsedSpacing !== currentSpacing) {
|
|
40534
|
+
return true;
|
|
40535
|
+
}
|
|
40536
|
+
// Always draw if we don't have a grid yet.
|
|
40537
|
+
if (!this.gridLines || !this.lastCameraPosition || !this.fixedGridOrigin) {
|
|
40538
|
+
return true;
|
|
40539
|
+
}
|
|
40540
|
+
// Check for significant height change.
|
|
40541
|
+
const heightRatio = cameraHeight / this.lastCameraHeight;
|
|
40542
|
+
if (heightRatio > (1 + this.HEIGHT_CHANGE_THRESHOLD) ||
|
|
40543
|
+
heightRatio < (1 - this.HEIGHT_CHANGE_THRESHOLD)) {
|
|
40544
|
+
return true;
|
|
40545
|
+
}
|
|
40546
|
+
// Check for significant position change relative to current grid extent.
|
|
40547
|
+
const distance = Cesium.Cartesian3.distance(cameraPosition, this.lastCameraPosition);
|
|
40548
|
+
const moveThreshold = gridExtent * this.CAMERA_MOVE_THRESHOLD;
|
|
40549
|
+
if (distance > moveThreshold) {
|
|
40550
|
+
return true;
|
|
40551
|
+
}
|
|
40552
|
+
// Check for significant grid extent change.
|
|
40553
|
+
const extentRatio = gridExtent / this.lastGridExtent;
|
|
40554
|
+
if (extentRatio > (1 + this.GRID_EXTENT_CHANGE_THRESHOLD) ||
|
|
40555
|
+
extentRatio < (1 - this.GRID_EXTENT_CHANGE_THRESHOLD)) {
|
|
40556
|
+
return true;
|
|
40557
|
+
}
|
|
40558
|
+
// No significant changes, keep current grid.
|
|
40559
|
+
return false;
|
|
40560
|
+
}
|
|
40561
|
+
isGridDensityAcceptable(visibleArea, cameraHeight, spacingToCheck = this.spacingInMeters) {
|
|
40562
|
+
const lonRange = visibleArea.eastLon - visibleArea.westLon;
|
|
40563
|
+
const latRange = visibleArea.northLat - visibleArea.southLat;
|
|
40564
|
+
// Convert to approximate meters at center
|
|
40565
|
+
const centerCartographic = Cesium.Cartographic.fromCartesian(visibleArea.centerCartesian);
|
|
40566
|
+
const metersPerDegreeLon = 111319.9 * Math.cos(centerCartographic.latitude);
|
|
40567
|
+
const canvas = this.viewer.scene.canvas;
|
|
40568
|
+
const spacingInDegrees = spacingToCheck / metersPerDegreeLon;
|
|
40569
|
+
const canvasWidth = canvas.clientWidth;
|
|
40570
|
+
const pixelsPerDegree = canvasWidth / lonRange;
|
|
40571
|
+
const pixelsBetweenGridLines = pixelsPerDegree * spacingInDegrees;
|
|
40572
|
+
const minPixelSize = 1;
|
|
40573
|
+
return pixelsBetweenGridLines >= minPixelSize;
|
|
40574
|
+
}
|
|
40575
|
+
calculateVisibleArea() {
|
|
40576
|
+
const scene = this.viewer.scene;
|
|
40577
|
+
const camera = scene.camera;
|
|
40578
|
+
const globe = scene.globe;
|
|
40579
|
+
// Get the corners of the view frustum
|
|
40580
|
+
const canvas = scene.canvas;
|
|
40581
|
+
const corners = [
|
|
40582
|
+
new Cesium.Cartesian2(0, 0),
|
|
40583
|
+
new Cesium.Cartesian2(canvas.clientWidth, 0),
|
|
40584
|
+
new Cesium.Cartesian2(0, canvas.clientHeight),
|
|
40585
|
+
new Cesium.Cartesian2(canvas.clientWidth, canvas.clientHeight),
|
|
40586
|
+
new Cesium.Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2) // Center
|
|
40587
|
+
];
|
|
40588
|
+
// Project corners to the globe
|
|
40589
|
+
const cartographicCorners = [];
|
|
40590
|
+
for (const corner of corners) {
|
|
40591
|
+
const ray = camera.getPickRay(corner);
|
|
40592
|
+
if (!ray) {
|
|
40593
|
+
continue;
|
|
40594
|
+
}
|
|
40595
|
+
const position = globe.pick(ray, scene);
|
|
40596
|
+
if (!position) {
|
|
40597
|
+
continue;
|
|
40598
|
+
}
|
|
40599
|
+
cartographicCorners.push(Cesium.Cartographic.fromCartesian(position));
|
|
40600
|
+
}
|
|
40601
|
+
if (cartographicCorners.length === 0) {
|
|
40602
|
+
return null;
|
|
40603
|
+
}
|
|
40604
|
+
// Find the bounding rectangle
|
|
40605
|
+
let westLon = Number.POSITIVE_INFINITY;
|
|
40606
|
+
let eastLon = Number.NEGATIVE_INFINITY;
|
|
40607
|
+
let southLat = Number.POSITIVE_INFINITY;
|
|
40608
|
+
let northLat = Number.NEGATIVE_INFINITY;
|
|
40609
|
+
for (const corner of cartographicCorners) {
|
|
40610
|
+
westLon = Math.min(westLon, corner.longitude);
|
|
40611
|
+
eastLon = Math.max(eastLon, corner.longitude);
|
|
40612
|
+
southLat = Math.min(southLat, corner.latitude);
|
|
40613
|
+
northLat = Math.max(northLat, corner.latitude);
|
|
40614
|
+
}
|
|
40615
|
+
// Add some padding around the visible area
|
|
40616
|
+
const lonPadding = (eastLon - westLon) * this.VIEW_PADDING_FACTOR;
|
|
40617
|
+
const latPadding = (northLat - southLat) * this.VIEW_PADDING_FACTOR;
|
|
40618
|
+
westLon -= lonPadding;
|
|
40619
|
+
eastLon += lonPadding;
|
|
40620
|
+
southLat -= latPadding;
|
|
40621
|
+
northLat += latPadding;
|
|
40622
|
+
// Get center position for reference
|
|
40623
|
+
const centerCartographic = new Cesium.Cartographic((westLon + eastLon) / 2, (southLat + northLat) / 2);
|
|
40624
|
+
const centerCartesian = Cesium.Cartesian3.fromRadians(centerCartographic.longitude, centerCartographic.latitude, 0);
|
|
40625
|
+
return {
|
|
40626
|
+
westLon: Cesium.Math.toDegrees(westLon),
|
|
40627
|
+
eastLon: Cesium.Math.toDegrees(eastLon),
|
|
40628
|
+
southLat: Cesium.Math.toDegrees(southLat),
|
|
40629
|
+
northLat: Cesium.Math.toDegrees(northLat),
|
|
40630
|
+
centerCartesian: centerCartesian
|
|
40631
|
+
};
|
|
40632
|
+
}
|
|
40633
|
+
generateGridLines(originPoint, visibilityRadius, actualSpacing = this.spacingInMeters) {
|
|
40634
|
+
// Number of grid cells in each direction (add 1 for edge lines).
|
|
40635
|
+
const gridCellCount = Math.ceil(visibilityRadius / actualSpacing) + 1;
|
|
40636
|
+
const maxCells = Math.min(gridCellCount, Math.floor(this.MAX_LINES / 2));
|
|
40637
|
+
// Calculate interval to skip lines for density control.
|
|
40638
|
+
const spacingFactor = actualSpacing / 100;
|
|
40639
|
+
const heightFactor = this.lastCameraHeight / 500;
|
|
40640
|
+
let lineSkipInterval = Math.floor(spacingFactor * heightFactor);
|
|
40641
|
+
lineSkipInterval = Math.min(Math.max(lineSkipInterval, 1), 10); // Clamp between 1 and 10
|
|
40642
|
+
const intersections = [];
|
|
40643
|
+
// Generate north-south grid lines (longitude lines)
|
|
40644
|
+
for (let i = -maxCells; i <= maxCells; i++) {
|
|
40645
|
+
if (i % lineSkipInterval !== 0) {
|
|
40646
|
+
continue;
|
|
40647
|
+
}
|
|
40648
|
+
const positions = [];
|
|
40649
|
+
// Create a vertical line at this longitude offset.
|
|
40650
|
+
// East heading for longitude.
|
|
40651
|
+
const startPoint = this.cachedOffsetPoint(originPoint, i * actualSpacing, 90);
|
|
40652
|
+
// Draw the line from south to north
|
|
40653
|
+
for (let j = -maxCells; j <= maxCells; j++) {
|
|
40654
|
+
if (j % lineSkipInterval !== 0) {
|
|
40655
|
+
continue;
|
|
40656
|
+
}
|
|
40657
|
+
// North heading for latitude.
|
|
40658
|
+
const linePoint = this.cachedOffsetPoint(startPoint, j * actualSpacing, 0);
|
|
40659
|
+
const position = Cesium.Cartesian3.fromDegrees(linePoint.longitude, linePoint.latitude, linePoint.altitude);
|
|
40660
|
+
positions.push(position);
|
|
40661
|
+
intersections.push({
|
|
40662
|
+
position: position,
|
|
40663
|
+
i: i,
|
|
40664
|
+
j: j
|
|
40665
|
+
});
|
|
40666
|
+
}
|
|
40667
|
+
if (positions.length >= 2) {
|
|
40668
|
+
// Check if this is the primary Y axis (i=0) in model space
|
|
40669
|
+
const isPrimaryYAxis = this.modelSpace && i === 0;
|
|
40670
|
+
this.addGridLine(positions, isPrimaryYAxis ? "y" : null);
|
|
40671
|
+
}
|
|
40672
|
+
}
|
|
40673
|
+
// Generate east-west grid lines (latitude lines).
|
|
40674
|
+
for (let j = -maxCells; j <= maxCells; j++) {
|
|
40675
|
+
if (j % lineSkipInterval !== 0) {
|
|
40676
|
+
continue;
|
|
40677
|
+
}
|
|
40678
|
+
const positions = [];
|
|
40679
|
+
// Create a horizontal line at this latitude offset
|
|
40680
|
+
// North heading for latitude.
|
|
40681
|
+
const startPoint = this.cachedOffsetPoint(originPoint, j * actualSpacing, 0);
|
|
40682
|
+
// Draw the line from west to east.
|
|
40683
|
+
for (let i = -maxCells; i <= maxCells; i++) {
|
|
40684
|
+
if (i % lineSkipInterval !== 0) {
|
|
40685
|
+
continue;
|
|
40686
|
+
}
|
|
40687
|
+
// East heading for longitude.
|
|
40688
|
+
const linePoint = this.cachedOffsetPoint(startPoint, i * actualSpacing, 90);
|
|
40689
|
+
const position = Cesium.Cartesian3.fromDegrees(linePoint.longitude, linePoint.latitude, linePoint.altitude);
|
|
40690
|
+
positions.push(position);
|
|
40691
|
+
}
|
|
40692
|
+
if (positions.length >= 2) {
|
|
40693
|
+
// Check if this is the primary X axis (j=0) in model space
|
|
40694
|
+
const isPrimaryXAxis = this.modelSpace && j === 0;
|
|
40695
|
+
this.addGridLine(positions, isPrimaryXAxis ? "x" : null);
|
|
40696
|
+
}
|
|
40697
|
+
}
|
|
40698
|
+
this.addGridLabels(intersections, actualSpacing);
|
|
40699
|
+
}
|
|
40700
|
+
addGridLabels(intersections, actualSpacing) {
|
|
40701
|
+
if (!this.gridLabels) {
|
|
40702
|
+
return;
|
|
40703
|
+
}
|
|
40704
|
+
// Determine label interval based on number of intersections.
|
|
40705
|
+
// Use higher interval for more intersections to prevent crowding.
|
|
40706
|
+
const totalIntersections = intersections.length;
|
|
40707
|
+
let labelInterval = this.LABEL_INTERVAL;
|
|
40708
|
+
if (totalIntersections > 1000) {
|
|
40709
|
+
labelInterval = 5;
|
|
40710
|
+
}
|
|
40711
|
+
else if (totalIntersections > 500) {
|
|
40712
|
+
labelInterval = 3;
|
|
40713
|
+
}
|
|
40714
|
+
let labelCount = 0;
|
|
40715
|
+
// Filter intersections to only those at the labelInterval.
|
|
40716
|
+
const labelPoints = intersections.filter(point => {
|
|
40717
|
+
// Always label the origin (0,0).
|
|
40718
|
+
if (point.i === 0 && point.j === 0) {
|
|
40719
|
+
return true;
|
|
40720
|
+
}
|
|
40721
|
+
return (point.i % labelInterval === 0 && point.j % labelInterval === 0);
|
|
40722
|
+
});
|
|
40723
|
+
// Sort points by distance from origin.
|
|
40724
|
+
labelPoints.sort((a, b) => {
|
|
40725
|
+
const distA = Math.abs(a.i) + Math.abs(a.j);
|
|
40726
|
+
const distB = Math.abs(b.i) + Math.abs(b.j);
|
|
40727
|
+
return distA - distB;
|
|
40728
|
+
});
|
|
40729
|
+
// Add labels, prioritizing those closest to the origin.
|
|
40730
|
+
for (const point of labelPoints) {
|
|
40731
|
+
if (labelCount >= this.MAX_LABELS) {
|
|
40732
|
+
// Limit reached.
|
|
40733
|
+
break;
|
|
40734
|
+
}
|
|
40735
|
+
let labelText;
|
|
40736
|
+
// Model space coordinates (meters from origin).
|
|
40737
|
+
if (this.modelSpace) {
|
|
40738
|
+
const xMeters = point.i * actualSpacing;
|
|
40739
|
+
const yMeters = point.j * actualSpacing;
|
|
40740
|
+
if (point.i === 0 && point.j === 0) {
|
|
40741
|
+
labelText = "0,0,0";
|
|
40742
|
+
}
|
|
40743
|
+
else {
|
|
40744
|
+
labelText = `${xMeters},${yMeters}`;
|
|
40745
|
+
}
|
|
40746
|
+
}
|
|
40747
|
+
// Geographic coordinates (degrees).
|
|
40748
|
+
else {
|
|
40749
|
+
const cartographic = Cesium.Cartographic.fromCartesian(point.position);
|
|
40750
|
+
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
|
|
40751
|
+
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
|
|
40752
|
+
const latFormatted = latitude.toFixed(5);
|
|
40753
|
+
const lonFormatted = longitude.toFixed(5);
|
|
40754
|
+
labelText = `${latFormatted}, ${lonFormatted}`;
|
|
40755
|
+
}
|
|
40756
|
+
this.gridLabels.add({
|
|
40757
|
+
position: point.position,
|
|
40758
|
+
text: labelText,
|
|
40759
|
+
font: this.modelSpace && point.i === 0 && point.j === 0 ? "20px sans-serif" : "14px sans-serif",
|
|
40760
|
+
fillColor: this.modelSpace && point.i === 0 && point.j === 0 ? Cesium.Color.WHITE : Cesium.Color.WHITE.withAlpha(0.5),
|
|
40761
|
+
style: Cesium.LabelStyle.FILL,
|
|
40762
|
+
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
|
40763
|
+
verticalOrigin: Cesium.VerticalOrigin.TOP,
|
|
40764
|
+
pixelOffset: new Cesium.Cartesian2(0, 10),
|
|
40765
|
+
// Fade out with distance.
|
|
40766
|
+
translucencyByDistance: new Cesium.NearFarScalar(1000, 1.0, 15000, 0.4)
|
|
40767
|
+
});
|
|
40768
|
+
labelCount++;
|
|
40769
|
+
}
|
|
40770
|
+
}
|
|
40771
|
+
addGridLine(positions, primaryAxis = null) {
|
|
40772
|
+
if (!this.gridLines || positions.length < 2) {
|
|
40773
|
+
return;
|
|
40774
|
+
}
|
|
40775
|
+
let lineColor;
|
|
40776
|
+
let lineWidth = 1.0;
|
|
40777
|
+
let lineAlpha = 0.2;
|
|
40778
|
+
// Red for X axis.
|
|
40779
|
+
if (primaryAxis === "x") {
|
|
40780
|
+
lineColor = Cesium.Color.RED;
|
|
40781
|
+
lineAlpha = 0.5;
|
|
40782
|
+
lineWidth = 1.5;
|
|
40783
|
+
}
|
|
40784
|
+
// Green for Y axis.
|
|
40785
|
+
else if (primaryAxis === "y") {
|
|
40786
|
+
lineColor = Cesium.Color.GREEN;
|
|
40787
|
+
lineAlpha = 0.5;
|
|
40788
|
+
lineWidth = 1.5;
|
|
40789
|
+
}
|
|
40790
|
+
// Default gray for regular grid lines.
|
|
40791
|
+
else {
|
|
40792
|
+
lineColor = Cesium.Color.LIGHTGRAY;
|
|
40793
|
+
}
|
|
40794
|
+
this.gridLines.add({
|
|
40795
|
+
positions: positions,
|
|
40796
|
+
width: lineWidth,
|
|
40797
|
+
material: Cesium.Material.fromType("Color", {
|
|
40798
|
+
color: lineColor.withAlpha(lineAlpha)
|
|
40799
|
+
})
|
|
40800
|
+
});
|
|
40801
|
+
}
|
|
40802
|
+
}
|
|
40803
|
+
// Grid square size used when settings name none, in metres.
|
|
40804
|
+
const DEFAULT_SPACING = 50;
|
|
40805
|
+
(function (CesiumMapGrid) {
|
|
40806
|
+
function GetCurrentSpacing(viewer) {
|
|
40807
|
+
if (!viewer || viewer.isDestroyed()) {
|
|
40808
|
+
return 0;
|
|
40809
|
+
}
|
|
40810
|
+
const handler = viewer[CLASS_INSTANCE_KEY];
|
|
40811
|
+
return handler ? handler.getCurrentSpacing() : 0;
|
|
40812
|
+
}
|
|
40813
|
+
CesiumMapGrid.GetCurrentSpacing = GetCurrentSpacing;
|
|
40814
|
+
function Remove(viewer) {
|
|
40815
|
+
if (!viewer || viewer.isDestroyed()) {
|
|
40816
|
+
return;
|
|
40817
|
+
}
|
|
40818
|
+
else if (!viewer[LAST_SETTINGS_KEY]) {
|
|
40819
|
+
return;
|
|
40820
|
+
}
|
|
40821
|
+
if (!viewer[UPDATE_MARKER_KEY]) {
|
|
40822
|
+
viewer[UPDATE_MARKER_KEY] = 1;
|
|
40823
|
+
}
|
|
40824
|
+
else {
|
|
40825
|
+
viewer[UPDATE_MARKER_KEY]++;
|
|
40826
|
+
}
|
|
40827
|
+
// Dispose of the handler.
|
|
40828
|
+
if (viewer[CLASS_INSTANCE_KEY]) {
|
|
40829
|
+
viewer[CLASS_INSTANCE_KEY].Dispose();
|
|
40830
|
+
viewer[CLASS_INSTANCE_KEY] = null;
|
|
40831
|
+
}
|
|
40832
|
+
viewer[LAST_SETTINGS_KEY] = {
|
|
40833
|
+
...viewer[LAST_SETTINGS_KEY],
|
|
40834
|
+
enabled: false
|
|
40835
|
+
};
|
|
40836
|
+
}
|
|
40837
|
+
CesiumMapGrid.Remove = Remove;
|
|
40838
|
+
function Draw(viewer, spacingInMeters, modelSpace = true) {
|
|
40839
|
+
if (!viewer || viewer.isDestroyed()) {
|
|
40840
|
+
return;
|
|
40841
|
+
}
|
|
40842
|
+
let settings = viewer[LAST_SETTINGS_KEY];
|
|
40843
|
+
if (settings) {
|
|
40844
|
+
// See if the settings are the same as the last time.
|
|
40845
|
+
if (settings.spacing === spacingInMeters &&
|
|
40846
|
+
settings.modelSpace === modelSpace &&
|
|
40847
|
+
settings.enabled) {
|
|
40848
|
+
return;
|
|
40849
|
+
}
|
|
40850
|
+
}
|
|
40851
|
+
if (!viewer[UPDATE_MARKER_KEY]) {
|
|
40852
|
+
viewer[UPDATE_MARKER_KEY] = 1;
|
|
40853
|
+
}
|
|
40854
|
+
else {
|
|
40855
|
+
viewer[UPDATE_MARKER_KEY]++;
|
|
40856
|
+
}
|
|
40857
|
+
settings = {
|
|
40858
|
+
...settings,
|
|
40859
|
+
enabled: true,
|
|
40860
|
+
spacing: spacingInMeters,
|
|
40861
|
+
modelSpace: modelSpace
|
|
40862
|
+
};
|
|
40863
|
+
viewer[LAST_SETTINGS_KEY] = settings;
|
|
40864
|
+
// Create or update the handler.
|
|
40865
|
+
if (viewer[CLASS_INSTANCE_KEY]) {
|
|
40866
|
+
viewer[CLASS_INSTANCE_KEY].Update(spacingInMeters, modelSpace);
|
|
40867
|
+
}
|
|
40868
|
+
else {
|
|
40869
|
+
viewer[CLASS_INSTANCE_KEY] = new MapGridHandler(viewer, spacingInMeters, modelSpace);
|
|
40870
|
+
}
|
|
40871
|
+
}
|
|
40872
|
+
CesiumMapGrid.Draw = Draw;
|
|
40873
|
+
/**
|
|
40874
|
+
* Draws a bookmark's grid, or removes the grid when it has none; only model-space draws one.
|
|
40875
|
+
*/
|
|
40876
|
+
function Apply(viewer, grid, modelSpace) {
|
|
40877
|
+
if ((grid === null || grid === void 0 ? void 0 : grid.enabled) && modelSpace) {
|
|
40878
|
+
Draw(viewer, Number(grid.spacing) || DEFAULT_SPACING, modelSpace);
|
|
40879
|
+
}
|
|
40880
|
+
else {
|
|
40881
|
+
Remove(viewer);
|
|
40882
|
+
}
|
|
40883
|
+
}
|
|
40884
|
+
CesiumMapGrid.Apply = Apply;
|
|
40885
|
+
function GetIsEnabled(viewer) {
|
|
40886
|
+
if (!viewer || viewer.isDestroyed()) {
|
|
40887
|
+
return false;
|
|
40888
|
+
}
|
|
40889
|
+
const settings = viewer[LAST_SETTINGS_KEY];
|
|
40890
|
+
if (settings) {
|
|
40891
|
+
return Boolean(settings.enabled);
|
|
40892
|
+
}
|
|
40893
|
+
return false;
|
|
40894
|
+
}
|
|
40895
|
+
CesiumMapGrid.GetIsEnabled = GetIsEnabled;
|
|
40896
|
+
/**
|
|
40897
|
+
* Returns the last applied grid settings.
|
|
40898
|
+
*/
|
|
40899
|
+
function GetSettings(viewer) {
|
|
40900
|
+
if (!viewer || viewer.isDestroyed()) {
|
|
40901
|
+
return null;
|
|
40902
|
+
}
|
|
40903
|
+
return viewer[LAST_SETTINGS_KEY];
|
|
40904
|
+
}
|
|
40905
|
+
CesiumMapGrid.GetSettings = GetSettings;
|
|
40906
|
+
})(exports.CesiumMapGrid || (exports.CesiumMapGrid = {}));
|
|
40907
|
+
|
|
38715
40908
|
// TODO: Move these to the data model.
|
|
38716
40909
|
const MIN_SHADOW_SIZE = 2048;
|
|
38717
40910
|
const MAX_SHADOW_SIZE = 32768;
|
|
@@ -38903,7 +41096,7 @@
|
|
|
38903
41096
|
* @param view
|
|
38904
41097
|
*/
|
|
38905
41098
|
async function renderNavigator(iteration, params, bookmark, view, getters) {
|
|
38906
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8;
|
|
41099
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9;
|
|
38907
41100
|
const viewer = params.manager.Viewer;
|
|
38908
41101
|
const scene = viewer.scene;
|
|
38909
41102
|
// Ion requests go through the server-side proxy (set up during viewer init or here as fallback).
|
|
@@ -39128,15 +41321,7 @@
|
|
|
39128
41321
|
scene.globe.translucency.frontFaceAlphaByDistance.farValue = 1;
|
|
39129
41322
|
}
|
|
39130
41323
|
scene.globe.translucency.frontFaceAlphaByDistance.nearValue = EnsureNumber(globeAlpha, 1);
|
|
39131
|
-
|
|
39132
|
-
if (shadows == null) {
|
|
39133
|
-
shadows = (_u = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _u === void 0 ? void 0 : _u.shadows;
|
|
39134
|
-
}
|
|
39135
|
-
if (shadows == null) {
|
|
39136
|
-
shadows = {
|
|
39137
|
-
enabled: false
|
|
39138
|
-
};
|
|
39139
|
-
}
|
|
41324
|
+
const shadows = exports.ViewRenderEngine.ResolveShadows(bSettings, defaults === null || defaults === void 0 ? void 0 : defaults.settings, contentType);
|
|
39140
41325
|
viewer.shadows = Boolean(shadows.enabled);
|
|
39141
41326
|
viewer.shadowMap.enabled = Boolean(shadows.enabled);
|
|
39142
41327
|
let size = EnsureNumber(shadows.pixelSize);
|
|
@@ -39149,9 +41334,11 @@
|
|
|
39149
41334
|
viewer.shadowMap.size = size;
|
|
39150
41335
|
viewer.shadowMap.softShadows = Boolean(shadows.soften);
|
|
39151
41336
|
viewer.shadowMap.darkness = EnsureNumber(shadows.darkness, 0.3);
|
|
41337
|
+
// Drawn after the model-space origin is resolved above, since the grid starts from it.
|
|
41338
|
+
exports.CesiumMapGrid.Apply(viewer, (_u = bSettings.grid) !== null && _u !== void 0 ? _u : (_v = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _v === void 0 ? void 0 : _v.grid, contentType == BModels.ProjectViewBookmark.EContentType.WEB_3D_MODEL_SPACE);
|
|
39152
41339
|
let ambientOcclusion = bSettings === null || bSettings === void 0 ? void 0 : bSettings.ambientOcclusion;
|
|
39153
41340
|
if (ambientOcclusion == null) {
|
|
39154
|
-
ambientOcclusion = (
|
|
41341
|
+
ambientOcclusion = (_w = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _w === void 0 ? void 0 : _w.ambientOcclusion;
|
|
39155
41342
|
}
|
|
39156
41343
|
if (ambientOcclusion == null) {
|
|
39157
41344
|
ambientOcclusion = {
|
|
@@ -39174,7 +41361,7 @@
|
|
|
39174
41361
|
}
|
|
39175
41362
|
let lighting = bSettings === null || bSettings === void 0 ? void 0 : bSettings.lighting;
|
|
39176
41363
|
if (lighting == null) {
|
|
39177
|
-
lighting = (
|
|
41364
|
+
lighting = (_x = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _x === void 0 ? void 0 : _x.lighting;
|
|
39178
41365
|
}
|
|
39179
41366
|
if (lighting == null) {
|
|
39180
41367
|
lighting = {
|
|
@@ -39192,7 +41379,7 @@
|
|
|
39192
41379
|
exports.ViewUtils.ApplyDefaultBrightness(viewer, EnsureNumber(lighting.brightnessShift, DEFAULT_BRIGHTNESS_SHIFT));
|
|
39193
41380
|
let quality = bSettings === null || bSettings === void 0 ? void 0 : bSettings.quality;
|
|
39194
41381
|
if (quality == null) {
|
|
39195
|
-
quality = (
|
|
41382
|
+
quality = (_y = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _y === void 0 ? void 0 : _y.quality;
|
|
39196
41383
|
}
|
|
39197
41384
|
if (quality == null) {
|
|
39198
41385
|
quality = {
|
|
@@ -39203,11 +41390,11 @@
|
|
|
39203
41390
|
fxaa.enabled = Boolean(quality.fxaa);
|
|
39204
41391
|
let dateTime = bSettings === null || bSettings === void 0 ? void 0 : bSettings.dateTime;
|
|
39205
41392
|
if (dateTime == null) {
|
|
39206
|
-
dateTime = (
|
|
41393
|
+
dateTime = (_z = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _z === void 0 ? void 0 : _z.dateTime;
|
|
39207
41394
|
}
|
|
39208
41395
|
let timeline = bSettings === null || bSettings === void 0 ? void 0 : bSettings.timeline;
|
|
39209
41396
|
if (timeline == null) {
|
|
39210
|
-
timeline = (
|
|
41397
|
+
timeline = (_0 = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _0 === void 0 ? void 0 : _0.timeline;
|
|
39211
41398
|
}
|
|
39212
41399
|
if (timeline == null) {
|
|
39213
41400
|
timeline = {
|
|
@@ -39231,7 +41418,7 @@
|
|
|
39231
41418
|
// Newer version that has states per Entity ID + Menu Item ID.
|
|
39232
41419
|
let states = bSettings === null || bSettings === void 0 ? void 0 : bSettings.states;
|
|
39233
41420
|
if (states == null) {
|
|
39234
|
-
states = (
|
|
41421
|
+
states = (_1 = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _1 === void 0 ? void 0 : _1.states;
|
|
39235
41422
|
}
|
|
39236
41423
|
if (states != null) {
|
|
39237
41424
|
params.manager.VisualsRegister.OverrideStates(states);
|
|
@@ -39240,7 +41427,7 @@
|
|
|
39240
41427
|
else {
|
|
39241
41428
|
let selectedIds = bSettings === null || bSettings === void 0 ? void 0 : bSettings.selectedEntityIds;
|
|
39242
41429
|
if (selectedIds == null) {
|
|
39243
|
-
selectedIds = (
|
|
41430
|
+
selectedIds = (_2 = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _2 === void 0 ? void 0 : _2.selectedEntityIds;
|
|
39244
41431
|
}
|
|
39245
41432
|
if (selectedIds != null) {
|
|
39246
41433
|
params.manager.VisualsRegister.ClearSelected();
|
|
@@ -39254,7 +41441,7 @@
|
|
|
39254
41441
|
params.manager.VisualsRegister.ClearHidden();
|
|
39255
41442
|
let hiddenIds = bSettings === null || bSettings === void 0 ? void 0 : bSettings.hiddenEntityIds;
|
|
39256
41443
|
if (hiddenIds == null) {
|
|
39257
|
-
hiddenIds = (
|
|
41444
|
+
hiddenIds = (_3 = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _3 === void 0 ? void 0 : _3.hiddenEntityIds;
|
|
39258
41445
|
}
|
|
39259
41446
|
if (hiddenIds != null) {
|
|
39260
41447
|
params.manager.VisualsRegister.SetHidden({
|
|
@@ -39266,7 +41453,7 @@
|
|
|
39266
41453
|
params.manager.VisualsRegister.ClearIsolated();
|
|
39267
41454
|
let isolatedIds = bSettings === null || bSettings === void 0 ? void 0 : bSettings.isolatedEntityIds;
|
|
39268
41455
|
if (isolatedIds == null) {
|
|
39269
|
-
isolatedIds = (
|
|
41456
|
+
isolatedIds = (_4 = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _4 === void 0 ? void 0 : _4.isolatedEntityIds;
|
|
39270
41457
|
}
|
|
39271
41458
|
if (isolatedIds != null) {
|
|
39272
41459
|
params.manager.VisualsRegister.SetIsolated({
|
|
@@ -39277,7 +41464,7 @@
|
|
|
39277
41464
|
}
|
|
39278
41465
|
let labelledIds = bSettings === null || bSettings === void 0 ? void 0 : bSettings.labelledEntityIds;
|
|
39279
41466
|
if (labelledIds == null) {
|
|
39280
|
-
labelledIds = (
|
|
41467
|
+
labelledIds = (_5 = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _5 === void 0 ? void 0 : _5.labelledEntityIds;
|
|
39281
41468
|
}
|
|
39282
41469
|
if (!(labelledIds === null || labelledIds === void 0 ? void 0 : labelledIds.length)) {
|
|
39283
41470
|
params.manager.VisualsRegister.ClearLabelled({
|
|
@@ -39301,7 +41488,7 @@
|
|
|
39301
41488
|
params.manager.VisualsRegister.ClearOpacity();
|
|
39302
41489
|
let entityOpacityMap = bSettings === null || bSettings === void 0 ? void 0 : bSettings.entityOpacityMap;
|
|
39303
41490
|
if (entityOpacityMap == null) {
|
|
39304
|
-
entityOpacityMap = (
|
|
41491
|
+
entityOpacityMap = (_6 = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _6 === void 0 ? void 0 : _6.entityOpacityMap;
|
|
39305
41492
|
}
|
|
39306
41493
|
if (entityOpacityMap != null) {
|
|
39307
41494
|
for (const entityId in entityOpacityMap) {
|
|
@@ -39316,7 +41503,7 @@
|
|
|
39316
41503
|
}
|
|
39317
41504
|
}
|
|
39318
41505
|
}
|
|
39319
|
-
let imagery = (bSettings === null || bSettings === void 0 ? void 0 : bSettings.imagery) != null ? bSettings.imagery : (
|
|
41506
|
+
let imagery = (bSettings === null || bSettings === void 0 ? void 0 : bSettings.imagery) != null ? bSettings.imagery : (_7 = defaults.settings) === null || _7 === void 0 ? void 0 : _7.imagery;
|
|
39320
41507
|
if (imagery == null) {
|
|
39321
41508
|
// TODO: Need global default.
|
|
39322
41509
|
imagery = [
|
|
@@ -39386,7 +41573,7 @@
|
|
|
39386
41573
|
// Otherwise we cause a flicker of rendered graphics.
|
|
39387
41574
|
// Maybe TODO to such a function for menu item manager.
|
|
39388
41575
|
const curEnabled = params.manager.GetEnabledItemIds();
|
|
39389
|
-
const newItemIds = (
|
|
41576
|
+
const newItemIds = (_8 = bSettings === null || bSettings === void 0 ? void 0 : bSettings.menuItemIds) !== null && _8 !== void 0 ? _8 : [];
|
|
39390
41577
|
for (const id of curEnabled) {
|
|
39391
41578
|
let shouldRemove;
|
|
39392
41579
|
if (id == RELATION_MENU_ITEM_ID) {
|
|
@@ -39460,7 +41647,7 @@
|
|
|
39460
41647
|
}
|
|
39461
41648
|
let gOcclusion = bSettings === null || bSettings === void 0 ? void 0 : bSettings.groundOcclusion;
|
|
39462
41649
|
if (gOcclusion == null) {
|
|
39463
|
-
gOcclusion = (
|
|
41650
|
+
gOcclusion = (_9 = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _9 === void 0 ? void 0 : _9.groundOcclusion;
|
|
39464
41651
|
}
|
|
39465
41652
|
if (gOcclusion == null) {
|
|
39466
41653
|
// TODO: Need global default.
|
|
@@ -39477,6 +41664,17 @@
|
|
|
39477
41664
|
BModels.ProjectViewBookmark.EContentType.WEB_3D_MODEL_SPACE,
|
|
39478
41665
|
BModels.ProjectViewBookmark.EContentType.WEB_3D_SPLIT_PLUGIN
|
|
39479
41666
|
];
|
|
41667
|
+
/**
|
|
41668
|
+
* A bookmark's own shadows, else the view's defaults unless it is model-space, which reads brighter without.
|
|
41669
|
+
*/
|
|
41670
|
+
function ResolveShadows(bSettings, defaults, contentType) {
|
|
41671
|
+
let shadows = bSettings === null || bSettings === void 0 ? void 0 : bSettings.shadows;
|
|
41672
|
+
if (shadows == null && contentType != BModels.ProjectViewBookmark.EContentType.WEB_3D_MODEL_SPACE) {
|
|
41673
|
+
shadows = defaults === null || defaults === void 0 ? void 0 : defaults.shadows;
|
|
41674
|
+
}
|
|
41675
|
+
return shadows !== null && shadows !== void 0 ? shadows : { enabled: false };
|
|
41676
|
+
}
|
|
41677
|
+
ViewRenderEngine.ResolveShadows = ResolveShadows;
|
|
39480
41678
|
/**
|
|
39481
41679
|
* Returns where a model-space bookmark's camera goes, measured from the origin in effect.
|
|
39482
41680
|
* @param viewer
|
|
@@ -44201,7 +46399,7 @@
|
|
|
44201
46399
|
const HOLD_LOADER_DELAY_MS = 150;
|
|
44202
46400
|
const CONTINUOUS_ANIMATION_SPEED = 0.02 / 3;
|
|
44203
46401
|
const ZOOM_DISTANCE = 0.85;
|
|
44204
|
-
const MIN_ZOOM_DISTANCE = 0.3;
|
|
46402
|
+
const MIN_ZOOM_DISTANCE$1 = 0.3;
|
|
44205
46403
|
const NEAR_NADIR_PITCH = -Math.PI / 2 + 0.05;
|
|
44206
46404
|
const MIN_ORBIT_OFFSET_METERS = 5;
|
|
44207
46405
|
// Caps how far a pivot may be from the camera relative to the camera's own altitude.
|
|
@@ -44407,7 +46605,7 @@
|
|
|
44407
46605
|
const mag = (ZOOM_DISTANCE * 0.5) / Math.max(1, 4 - Math.log10(height));
|
|
44408
46606
|
const zoom = zoomIn ? 1 - mag : 1 + mag;
|
|
44409
46607
|
const offset = Cesium.Cartesian3.multiplyByScalar(Cesium.Cartesian3.subtract(p, ground, new Cesium.Cartesian3()), zoom, new Cesium.Cartesian3());
|
|
44410
|
-
if (zoomIn && Cesium.Cartesian3.magnitude(offset) <= MIN_ZOOM_DISTANCE) {
|
|
46608
|
+
if (zoomIn && Cesium.Cartesian3.magnitude(offset) <= MIN_ZOOM_DISTANCE$1) {
|
|
44411
46609
|
return Promise.resolve(null);
|
|
44412
46610
|
}
|
|
44413
46611
|
const to = Cesium.Cartesian3.add(ground, offset, new Cesium.Cartesian3());
|
|
@@ -44631,7 +46829,7 @@
|
|
|
44631
46829
|
const mag = (ZOOM_DISTANCE * 0.5) / Math.max(1, 4 - Math.log10(height));
|
|
44632
46830
|
const zoom = zoomIn ? 1 - mag * 0.03 : 1 + mag * 0.03;
|
|
44633
46831
|
const offset = Cesium.Cartesian3.multiplyByScalar(Cesium.Cartesian3.subtract(p, ground, new Cesium.Cartesian3()), zoom, new Cesium.Cartesian3());
|
|
44634
|
-
if (zoomIn && Cesium.Cartesian3.magnitude(offset) <= MIN_ZOOM_DISTANCE) {
|
|
46832
|
+
if (zoomIn && Cesium.Cartesian3.magnitude(offset) <= MIN_ZOOM_DISTANCE$1) {
|
|
44635
46833
|
active = false;
|
|
44636
46834
|
return;
|
|
44637
46835
|
}
|
|
@@ -45440,7 +47638,7 @@
|
|
|
45440
47638
|
* @param viewer
|
|
45441
47639
|
*/
|
|
45442
47640
|
function InitViewer(params) {
|
|
45443
|
-
const { viewer, destroyWidgets, styleContainer, nextspaceWidgets, widgetParams } = params;
|
|
47641
|
+
const { viewer, destroyWidgets, styleContainer, nextspaceWidgets, widgetParams, cameraControls } = params;
|
|
45444
47642
|
const data = { widgets: {} };
|
|
45445
47643
|
if (destroyWidgets != false) {
|
|
45446
47644
|
try {
|
|
@@ -45467,6 +47665,12 @@
|
|
|
45467
47665
|
// Newer cesium is much dimmer by default so we shift brightness a bit.
|
|
45468
47666
|
exports.ViewUtils.ApplyDefaultBrightness(viewer);
|
|
45469
47667
|
backgroundRendering(viewer);
|
|
47668
|
+
if (cameraControls != false) {
|
|
47669
|
+
exports.CameraControls.Attach(viewer);
|
|
47670
|
+
if (exports.ViewUtils.GetModelSpace(viewer)) {
|
|
47671
|
+
exports.CameraControls.SetModelSpace(viewer, () => exports.ViewUtils.GetModelSpaceFrame(viewer));
|
|
47672
|
+
}
|
|
47673
|
+
}
|
|
45470
47674
|
// Capturing the destroy call to destroy all widgets.
|
|
45471
47675
|
const olDestroy = viewer.destroy;
|
|
45472
47676
|
viewer.destroy = () => {
|
|
@@ -45475,6 +47679,7 @@
|
|
|
45475
47679
|
}
|
|
45476
47680
|
try {
|
|
45477
47681
|
ViewerUtils.DisposeNextspaceWidgets(viewer);
|
|
47682
|
+
exports.CameraControls.Detach(viewer);
|
|
45478
47683
|
}
|
|
45479
47684
|
// We don't want the actual destroy to fail.
|
|
45480
47685
|
catch (e) {
|
|
@@ -49609,7 +51814,7 @@
|
|
|
49609
51814
|
...exports.ViewUtils.DefaultLightingProps(),
|
|
49610
51815
|
heightReference: heightRef,
|
|
49611
51816
|
scale: new Cesium.CallbackProperty(() => scale * styleScale, true),
|
|
49612
|
-
shadows:
|
|
51817
|
+
shadows: exports.ViewUtils.GetEntityShadowMode(params.viewer),
|
|
49613
51818
|
colorBlendAmount: blendAmount,
|
|
49614
51819
|
colorBlendMode: blendMode,
|
|
49615
51820
|
color: new Cesium.CallbackProperty(() => {
|
|
@@ -49644,7 +51849,7 @@
|
|
|
49644
51849
|
cEntity.model.uri = new Cesium.ConstantProperty(params.lodUrl);
|
|
49645
51850
|
}
|
|
49646
51851
|
cEntity.model.heightReference = new Cesium.ConstantProperty(heightRef);
|
|
49647
|
-
cEntity.model.shadows = new Cesium.ConstantProperty(
|
|
51852
|
+
cEntity.model.shadows = new Cesium.ConstantProperty(exports.ViewUtils.GetEntityShadowMode(params.viewer));
|
|
49648
51853
|
cEntity.model.colorBlendAmount = new Cesium.ConstantProperty(blendAmount);
|
|
49649
51854
|
cEntity.model.colorBlendMode = new Cesium.ConstantProperty(blendMode);
|
|
49650
51855
|
cEntity.model.distanceDisplayCondition = new Cesium.ConstantProperty(exports.EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance));
|
|
@@ -50482,7 +52687,7 @@
|
|
|
50482
52687
|
zIndex: getZIndex(style, entity, params.tags),
|
|
50483
52688
|
distanceDisplayCondition: exports.EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance, width),
|
|
50484
52689
|
cornerType: Cesium.CornerType.MITERED,
|
|
50485
|
-
shadows:
|
|
52690
|
+
shadows: exports.ViewUtils.GetEntityShadowMode(params.viewer),
|
|
50486
52691
|
fill: true
|
|
50487
52692
|
} : null,
|
|
50488
52693
|
position: exports.EntityUtils.GetPos({
|
|
@@ -54870,6 +57075,12 @@ void main() {
|
|
|
54870
57075
|
? BModels.Geometry.ParsePoints(ring.LinearRing).map((point) => point.altitude)
|
|
54871
57076
|
: [])
|
|
54872
57077
|
});
|
|
57078
|
+
const drapesOnGround = (heightRef === Cesium.HeightReference.CLAMP_TO_GROUND &&
|
|
57079
|
+
extrusion.value == undefined &&
|
|
57080
|
+
!drawsDisplacedSurface);
|
|
57081
|
+
if (drapesOnGround) {
|
|
57082
|
+
posses = groundPosses(posses);
|
|
57083
|
+
}
|
|
54873
57084
|
posses = CullDuplicateCPosses(posses);
|
|
54874
57085
|
if (posses.length < 4) {
|
|
54875
57086
|
return null;
|
|
@@ -54878,6 +57089,9 @@ void main() {
|
|
|
54878
57089
|
const holePosses = holeRings.map((x) => {
|
|
54879
57090
|
const points = BModels.Geometry.ParsePoints(x.LinearRing);
|
|
54880
57091
|
let holePosses = points.map(x => Cesium.Cartesian3.fromDegrees(EnsureNumber(x.longitude), EnsureNumber(x.latitude), EnsureNumber(x.altitude)));
|
|
57092
|
+
if (drapesOnGround) {
|
|
57093
|
+
holePosses = groundPosses(holePosses);
|
|
57094
|
+
}
|
|
54881
57095
|
holePosses = CullDuplicateCPosses(holePosses);
|
|
54882
57096
|
BModels.Cartes.CloseRing3(holePosses);
|
|
54883
57097
|
return holePosses;
|
|
@@ -54928,7 +57142,7 @@ void main() {
|
|
|
54928
57142
|
fill: !drawsDisplacedSurface,
|
|
54929
57143
|
extrudedHeight: extrusion.value,
|
|
54930
57144
|
extrudedHeightReference: extrusion.exHeightRef,
|
|
54931
|
-
shadows:
|
|
57145
|
+
shadows: exports.ViewUtils.GetEntityShadowMode(params.viewer),
|
|
54932
57146
|
heightReference: heightRef,
|
|
54933
57147
|
classificationType: classification,
|
|
54934
57148
|
perPositionHeight: heightRef == Cesium.HeightReference.CLAMP_TO_GROUND ? false : true,
|
|
@@ -55008,7 +57222,7 @@ void main() {
|
|
|
55008
57222
|
viewer: params.viewer,
|
|
55009
57223
|
graphic: cEntity.polygon,
|
|
55010
57224
|
property: "shadows",
|
|
55011
|
-
value:
|
|
57225
|
+
value: exports.ViewUtils.GetEntityShadowMode(params.viewer)
|
|
55012
57226
|
});
|
|
55013
57227
|
SetCPropIfChanged({
|
|
55014
57228
|
viewer: params.viewer,
|
|
@@ -55143,7 +57357,7 @@ void main() {
|
|
|
55143
57357
|
cornerType: Cesium.CornerType.MITERED,
|
|
55144
57358
|
classificationType: classification,
|
|
55145
57359
|
distanceDisplayCondition: exports.EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance, width, false, size),
|
|
55146
|
-
shadows:
|
|
57360
|
+
shadows: exports.ViewUtils.GetEntityShadowMode(params.viewer)
|
|
55147
57361
|
} : null,
|
|
55148
57362
|
show: true
|
|
55149
57363
|
});
|
|
@@ -55266,7 +57480,7 @@ void main() {
|
|
|
55266
57480
|
cornerType: Cesium.CornerType.MITERED,
|
|
55267
57481
|
classificationType: classification,
|
|
55268
57482
|
distanceDisplayCondition: exports.EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance, width, false, size),
|
|
55269
|
-
shadows:
|
|
57483
|
+
shadows: exports.ViewUtils.GetEntityShadowMode(params.viewer),
|
|
55270
57484
|
} : null,
|
|
55271
57485
|
show: true
|
|
55272
57486
|
});
|
|
@@ -55664,6 +57878,18 @@ void main() {
|
|
|
55664
57878
|
/*
|
|
55665
57879
|
* The lon/lat bounding rectangle of a ring, which is the frame Cesium drapes an image material in.
|
|
55666
57880
|
*/
|
|
57881
|
+
/*
|
|
57882
|
+
* Moves a ring onto the ellipsoid, as Cesium fills only a strip of a ground polygon with high vertices.
|
|
57883
|
+
*/
|
|
57884
|
+
function groundPosses(posses) {
|
|
57885
|
+
return posses.map((pos) => {
|
|
57886
|
+
const carto = Cesium.Cartographic.fromCartesian(pos);
|
|
57887
|
+
if (!carto) {
|
|
57888
|
+
return pos;
|
|
57889
|
+
}
|
|
57890
|
+
return Cesium.Cartesian3.fromRadians(carto.longitude, carto.latitude, 0);
|
|
57891
|
+
});
|
|
57892
|
+
}
|
|
55667
57893
|
function polygonBbox(posses) {
|
|
55668
57894
|
if (!posses || posses.length === 0) {
|
|
55669
57895
|
return undefined;
|
|
@@ -57575,7 +59801,7 @@ void main() {
|
|
|
57575
59801
|
StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
|
|
57576
59802
|
})(exports.StyleUtils || (exports.StyleUtils = {}));
|
|
57577
59803
|
|
|
57578
|
-
const VERSION = "7.7.
|
|
59804
|
+
const VERSION = "7.7.8";
|
|
57579
59805
|
/**
|
|
57580
59806
|
* Updates the environment instance used by bruce-cesium to one specified.
|
|
57581
59807
|
* This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
|