bruce-cesium 7.7.4 → 7.7.6

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.
@@ -1,6 +1,6 @@
1
1
  import * as Cesium from 'cesium';
2
- import { Cartographic, Entity, ConstantPositionProperty, HeightReference, Color, DistanceDisplayCondition, ColorMaterialProperty, Math as Math$1, Cartesian3, JulianDate, Quaternion, Transforms, HeadingPitchRoll, Matrix4, ColorBlendMode, CallbackProperty, ShadowMode, ConstantProperty, ClassificationType, Model, Primitive, Cesium3DTileFeature, ShaderSource, Ellipsoid, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, Cartesian2, Cartesian4, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, PixelDatatype, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, HorizontalOrigin, VerticalOrigin, ImageMaterialProperty, PolygonHierarchy, PolylineGraphics, ArcType, CornerType, Rectangle, SceneMode, IntersectionTests, Ray, SceneTransforms, NearFarScalar, Matrix3, KmlDataSource, GeoJsonDataSource, FeatureDetection, HeadingPitchRange, Cesium3DTileStyle, Cesium3DTileColorBlendMode, Ion, IonResource, OrthographicFrustum, EasingFunction, Intersect, Cesium3DTileset, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, WebGLConstants, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, PolygonPipeline, GeometryInstance, CustomDataSource, CesiumInspector, ClockRange, DynamicEnvironmentMapManager, ScreenSpaceEventHandler, ScreenSpaceEventType, Fullscreen, CzmlDataSource } from 'cesium';
3
- import { Cartes, LRUCache, Carto, ProjectViewTile, EntityHistoricData, BruceEvent, DelayQueue, Entity as Entity$1, EntityTag, ClientFile, ENVIRONMENT, EntityAttachmentType, EntityRelationType, EntityType, Tileset, Calculator, DataLab, Style, ObjectUtils, MenuItem, AccountConcept, RecordChangeFeed, EntityLod, UrlUtils, Api, Bounds, ClientFileValueMap, Color as Color$1, Geometry as Geometry$1, ProjectView, ProjectViewBookmark, ZoomControl, Assembly, EntityCoords, BruceApi, EntityRelation, EntitySource, Camera, ProgramKey, ProjectViewLegacyTile, Account, AccountLimits, Session, EntityAttachment, EntityAttribute, MathUtils, ProjectViewBookmarkGroup } from 'bruce-models';
2
+ import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, HeightReference, ColorBlendMode, HeadingPitchRoll, Math as Math$1, Transforms, ShadowMode, Cartesian3, Model, ClassificationType, Matrix4, JulianDate, Quaternion, ConstantPositionProperty, DistanceDisplayCondition, ArcType, CornerType, ShaderSource, Ellipsoid, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, Cartesian2, Cartesian4, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, PixelDatatype, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, ImageMaterialProperty, SceneMode, IntersectionTests, Ray, SceneTransforms, HorizontalOrigin, VerticalOrigin, PolygonHierarchy, PolylineGraphics, Rectangle, NearFarScalar, Matrix3, KmlDataSource, FeatureDetection, GeoJsonDataSource, Cesium3DTileStyle, HeadingPitchRange, Ion, IonResource, Cesium3DTileColorBlendMode, Cesium3DTileset, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, Intersect, WebGLConstants, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, OrthographicFrustum, EasingFunction, CesiumInspector, ClockRange, DynamicEnvironmentMapManager, ScreenSpaceEventHandler, ScreenSpaceEventType, CustomDataSource, PolygonPipeline, GeometryInstance, Fullscreen, CzmlDataSource } from 'cesium';
3
+ import { Cartes, LRUCache, Carto, ProjectViewTile, EntityHistoricData, BruceEvent, DelayQueue, Entity as Entity$1, EntityTag, ClientFile, ENVIRONMENT, EntityAttachmentType, EntityRelationType, EntityType, Tileset, Calculator, DataLab, Style, ObjectUtils, MenuItem, AccountConcept, RecordChangeFeed, EntityLod, UrlUtils, Geometry as Geometry$1, ClientFileValueMap, Color as Color$1, ProjectView, ProjectViewBookmark, ZoomControl, Api, Bounds, Assembly, EntityCoords, BruceApi, EntityRelation, EntitySource, ProgramKey, ProjectViewLegacyTile, Camera, Account, AccountLimits, Session, ProjectViewBookmarkGroup, EntityAttachment, EntityAttribute, MathUtils } from 'bruce-models';
4
4
 
5
5
  /**
6
6
  * Ensures a number is returned from a given value.
@@ -28178,10 +28178,13 @@ var DataSourceStaticKmlManager;
28178
28178
  */
28179
28179
  var XGridsRenderEngine;
28180
28180
  (function (XGridsRenderEngine) {
28181
+ /*
28182
+ * Loads a scan with optional SDK tuning and per-viewer budget control.
28183
+ */
28181
28184
  function Render(params) {
28182
- var _a, _b, _c;
28185
+ var _a, _b, _c, _d;
28183
28186
  const { api, viewer, tileset } = params;
28184
- if (!LCCRender) {
28187
+ if (typeof LCCRender === "undefined" || typeof (LCCRender === null || LCCRender === void 0 ? void 0 : LCCRender.load) !== "function") {
28185
28188
  console.warn("[XGridsRenderEngine] Render() LCCRender library not available.");
28186
28189
  return null;
28187
28190
  }
@@ -28224,6 +28227,46 @@ var XGridsRenderEngine;
28224
28227
  if (loadUrl.indexOf("?") !== -1) {
28225
28228
  loadUrl = loadUrl.split("?")[0];
28226
28229
  }
28230
+ let maxSplats = 6000000;
28231
+ /*
28232
+ * Configures optional LOD capabilities once metadata or initial geometry is ready.
28233
+ */
28234
+ const configure = (object, ready) => {
28235
+ var _a, _b, _c;
28236
+ if (!object || object["_nextspaceLodRemoved"] || object["_nextspaceLodConfigured"] || ((_a = viewer.isDestroyed) === null || _a === void 0 ? void 0 : _a.call(viewer))) {
28237
+ return;
28238
+ }
28239
+ try {
28240
+ if (typeof object.setMaxSplats !== "function") {
28241
+ return;
28242
+ }
28243
+ const lods = typeof object.getLodInfos === "function" ? object.getLodInfos() : undefined;
28244
+ const adaptive = (LCCRender.nextspaceAdaptiveLod === 1 &&
28245
+ typeof object.setNextspaceMetadataCallback === "function" &&
28246
+ Array.isArray(lods) &&
28247
+ typeof object.setVisible === "function" &&
28248
+ typeof object.setLodAutoLevelUp === "function");
28249
+ // Unpatched SDKs still need the legacy clamp to avoid their fixed LOD shortcut.
28250
+ const budget = !adaptive && (lods === null || lods === void 0 ? void 0 : lods[0]) > maxSplats && (lods === null || lods === void 0 ? void 0 : lods[1]) > 0
28251
+ ? Math.min(maxSplats, lods[1]) : maxSplats;
28252
+ object.setMaxSplats(budget);
28253
+ // A requested fly-to needs its initial data before off-screen culling can take over.
28254
+ if (adaptive &&
28255
+ (!params.flyTo || ready) &&
28256
+ ((_b = viewer.scene.preUpdate) === null || _b === void 0 ? void 0 : _b.addEventListener) &&
28257
+ ((_c = viewer.scene.postRender) === null || _c === void 0 ? void 0 : _c.addEventListener)) {
28258
+ const controller = viewer["_nextspaceXGridsLod"] ||
28259
+ (viewer["_nextspaceXGridsLod"] = new AdaptiveLod(viewer));
28260
+ controller.Add(object, maxSplats);
28261
+ controller.Update();
28262
+ }
28263
+ object["_nextspaceLodConfigured"] = !params.flyTo || ready;
28264
+ }
28265
+ catch (error) {
28266
+ // Optional SDK tuning must not abort its metadata parsing or the Cesium render loop.
28267
+ console.warn("[XGridsRenderEngine] Optional LOD setup unavailable.", error);
28268
+ }
28269
+ };
28227
28270
  const obj = LCCRender.load({
28228
28271
  camera: viewer.camera,
28229
28272
  scene: viewer.scene,
@@ -28237,22 +28280,19 @@ var XGridsRenderEngine;
28237
28280
  // When true it makes the new pieces fade-in.
28238
28281
  // The look is similar to sectioning in cesium. Might be a nice setting.
28239
28282
  useLoadingEffect: false
28240
- }, async () => {
28241
- var _a, _b;
28242
- // Array index is the LOD level, ie: index 0 = LOD 0.
28243
- const lods = obj.getLodInfos();
28244
- const maxSplats = 6000000;
28245
- // LCC 0.5.4 fixes the LOD when the budget exceeds LOD 1, leaving distant splats sparse.
28246
- const lodBudget = (lods === null || lods === void 0 ? void 0 : lods[0]) > maxSplats && (lods === null || lods === void 0 ? void 0 : lods[1]) > 0
28247
- ? Math.min(maxSplats, lods[1])
28248
- : maxSplats;
28249
- obj.setMaxSplats(lodBudget);
28283
+ }, async (loaded) => {
28284
+ var _a, _b, _c;
28285
+ const object = loaded || obj;
28286
+ if (!object || object["_nextspaceLodRemoved"] || ((_a = viewer.isDestroyed) === null || _a === void 0 ? void 0 : _a.call(viewer))) {
28287
+ return;
28288
+ }
28289
+ configure(object, true);
28250
28290
  // Auto fly to.
28251
28291
  if (params.flyTo) {
28252
- const pos3d = (_b = (_a = obj === null || obj === void 0 ? void 0 : obj.getBounds) === null || _a === void 0 ? void 0 : _a.call(obj)) === null || _b === void 0 ? void 0 : _b.origin;
28292
+ const pos3d = typeof object.getBounds === "function" ? (_b = object.getBounds()) === null || _b === void 0 ? void 0 : _b.origin : undefined;
28253
28293
  if (pos3d && Cartes.ValidateCartes3(pos3d)) {
28254
28294
  const altered = await DrawingUtils.RaisePos3d(viewer, pos3d, 300);
28255
- if (altered && Cartes.ValidateCartes3(altered)) {
28295
+ if (!object["_nextspaceLodRemoved"] && !((_c = viewer.isDestroyed) === null || _c === void 0 ? void 0 : _c.call(viewer)) && altered && Cartes.ValidateCartes3(altered)) {
28256
28296
  viewer.scene.camera.flyTo({
28257
28297
  destination: altered,
28258
28298
  orientation: {
@@ -28267,13 +28307,32 @@ var XGridsRenderEngine;
28267
28307
  }
28268
28308
  }
28269
28309
  });
28310
+ if (!obj) {
28311
+ return null;
28312
+ }
28313
+ try {
28314
+ const sdkBudget = typeof obj.getCurrentConfig === "function" ? (_d = obj.getCurrentConfig()) === null || _d === void 0 ? void 0 : _d.maxLoadSplatCount : undefined;
28315
+ // Preserve the SDK's mobile and weak-GPU limits before applying any temporary startup cap.
28316
+ maxSplats = Number.isFinite(sdkBudget) && sdkBudget > 0 && sdkBudget <= 2000000 ? sdkBudget : 6000000;
28317
+ if (LCCRender.nextspaceMetadataReady === 1 &&
28318
+ typeof obj.setNextspaceMetadataCallback === "function" &&
28319
+ typeof obj.setMaxSplats === "function") {
28320
+ obj.setNextspaceMetadataCallback((object) => configure(object, false));
28321
+ obj.setMaxSplats(Math.min(maxSplats, 1000000));
28322
+ }
28323
+ }
28324
+ catch (error) {
28325
+ console.warn("[XGridsRenderEngine] Optional startup LOD setup unavailable.", error);
28326
+ }
28270
28327
  if (typeof (obj === null || obj === void 0 ? void 0 : obj.setRenderState) === "function") {
28271
28328
  // Splats must respect nearer meshes already drawn in the same Cesium pass.
28272
28329
  obj.setRenderState({
28273
28330
  depthTest: {
28274
28331
  enabled: true,
28275
28332
  func: WebGLConstants.LEQUAL
28276
- }
28333
+ },
28334
+ // Transparent Gaussian edges must not occlude the splats blended behind them.
28335
+ depthMask: false
28277
28336
  });
28278
28337
  }
28279
28338
  if (typeof (obj === null || obj === void 0 ? void 0 : obj.setVisible) === "function") {
@@ -28281,9 +28340,13 @@ var XGridsRenderEngine;
28281
28340
  }
28282
28341
  TilesetRenderEngine.WatchDateRange(viewer, obj, tileset);
28283
28342
  // Highest LOD.
28284
- obj.setStartLod(0);
28343
+ if (typeof obj.setStartLod === "function") {
28344
+ obj.setStartLod(0);
28345
+ }
28285
28346
  // Max distance for rendering.
28286
- obj.setMaxDistance(800);
28347
+ if (typeof obj.setMaxDistance === "function") {
28348
+ obj.setMaxDistance(800);
28349
+ }
28287
28350
  // Adjust LODs based on camera distance.
28288
28351
  if (typeof obj.setLodAutoLevelUp === "function") {
28289
28352
  obj.setLodAutoLevelUp(true);
@@ -28291,43 +28354,336 @@ var XGridsRenderEngine;
28291
28354
  return obj;
28292
28355
  }
28293
28356
  XGridsRenderEngine.Render = Render;
28357
+ /*
28358
+ * Detaches optional tuning before unloading the scan.
28359
+ */
28294
28360
  function Remove(viewer, renderObject) {
28361
+ var _a;
28295
28362
  if (!renderObject) {
28296
28363
  return;
28297
28364
  }
28298
- if (!LCCRender) {
28365
+ renderObject["_nextspaceLodRemoved"] = true;
28366
+ delete renderObject.nextspaceOnMetadata;
28367
+ (_a = viewer === null || viewer === void 0 ? void 0 : viewer["_nextspaceXGridsLod"]) === null || _a === void 0 ? void 0 : _a.Remove(renderObject);
28368
+ TilesetRenderEngine.UnwatchDateRange(viewer, renderObject);
28369
+ if (typeof LCCRender === "undefined" || typeof (LCCRender === null || LCCRender === void 0 ? void 0 : LCCRender.unload) !== "function") {
28299
28370
  console.warn("[XGridsRenderEngine] Remove() LCCRender library not available.");
28300
28371
  return;
28301
28372
  }
28302
- TilesetRenderEngine.UnwatchDateRange(viewer, renderObject);
28303
28373
  LCCRender.unload(renderObject);
28304
28374
  }
28305
28375
  XGridsRenderEngine.Remove = Remove;
28376
+ /*
28377
+ * Shares a bounded splat budget across visible scans without overriding layer or timeline visibility.
28378
+ */
28379
+ class AdaptiveLod {
28380
+ /*
28381
+ * Observes scene updates and rendered frames for this viewer only.
28382
+ */
28383
+ constructor(viewer) {
28384
+ this.viewer = viewer;
28385
+ this.entries = [];
28386
+ this.removals = [];
28387
+ this.lastUpdate = -Infinity;
28388
+ this.lastAllocation = -Infinity;
28389
+ this.lastFrame = 0;
28390
+ this.lastMove = 0;
28391
+ this.slowSince = 0;
28392
+ this.fastSince = 0;
28393
+ this.lastChange = -Infinity;
28394
+ this.scale = 1;
28395
+ this.position = new Cartesian3();
28396
+ this.direction = new Cartesian3();
28397
+ this.havePose = false;
28398
+ this.removals.push(viewer.scene.preUpdate.addEventListener(() => {
28399
+ try {
28400
+ this.Update();
28401
+ }
28402
+ catch (error) {
28403
+ console.warn("[XGridsRenderEngine] Optional LOD controller disabled.", error);
28404
+ this.Dispose();
28405
+ }
28406
+ }));
28407
+ this.removals.push(viewer.scene.postRender.addEventListener(() => {
28408
+ const camera = viewer.scene.camera;
28409
+ const moving = this.havePose && (Cartesian3.distanceSquared(camera.positionWC, this.position) > 0.000001 ||
28410
+ Cartesian3.distanceSquared(camera.directionWC, this.direction) > 0.00000001);
28411
+ Cartesian3.clone(camera.positionWC, this.position);
28412
+ Cartesian3.clone(camera.directionWC, this.direction);
28413
+ this.havePose = true;
28414
+ this.ObserveFrame(performance.now(), moving);
28415
+ }));
28416
+ }
28417
+ /*
28418
+ * Registers a scan while preserving the caller's visibility intent.
28419
+ */
28420
+ Add(object, maxBudget) {
28421
+ var _a, _b, _c, _d;
28422
+ if (this.entries.some(entry => entry.object === object)) {
28423
+ return;
28424
+ }
28425
+ if (typeof object.setMaxSplats !== "function") {
28426
+ return;
28427
+ }
28428
+ const lods = typeof object.getLodInfos === "function" ? object.getLodInfos() : undefined;
28429
+ const finest = lods === null || lods === void 0 ? void 0 : lods[0];
28430
+ const coarsest = (lods === null || lods === void 0 ? void 0 : lods.length) > 1 ? lods[lods.length - 1] : 0;
28431
+ const entry = {
28432
+ object, maxBudget,
28433
+ capacity: Number.isFinite(finest) && finest > 0 ? Math.min(maxBudget, finest) : maxBudget,
28434
+ minimum: Number.isFinite(coarsest) && coarsest > 0 ? Math.min(maxBudget, coarsest) : 0,
28435
+ wantedVisible: ((_b = (_a = object.getCurrentConfig) === null || _a === void 0 ? void 0 : _a.call(object)) === null || _b === void 0 ? void 0 : _b.visible) !== false,
28436
+ visible: ((_d = (_c = object.getCurrentConfig) === null || _c === void 0 ? void 0 : _c.call(object)) === null || _d === void 0 ? void 0 : _d.visible) !== false,
28437
+ culled: false, setVisible: object.setVisible, budget: -1, nodeBudget: -1,
28438
+ radiusPixels: Infinity, surfaceDistance: Infinity, weight: 1, near: false
28439
+ };
28440
+ if (typeof entry.setVisible === "function") {
28441
+ object.setVisible = (visible) => {
28442
+ entry.wantedVisible = !!visible;
28443
+ this.applyVisibility(entry);
28444
+ this.lastUpdate = -Infinity;
28445
+ this.lastAllocation = -Infinity;
28446
+ };
28447
+ }
28448
+ this.entries.push(entry);
28449
+ this.lastMove = performance.now();
28450
+ this.lastUpdate = -Infinity;
28451
+ this.lastAllocation = -Infinity;
28452
+ }
28453
+ /*
28454
+ * Combines explicit visibility with temporary camera culling.
28455
+ */
28456
+ applyVisibility(entry) {
28457
+ const visible = entry.wantedVisible && !entry.culled;
28458
+ if (entry.visible !== visible && typeof entry.setVisible === "function") {
28459
+ entry.visible = visible;
28460
+ entry.setVisible.call(entry.object, visible);
28461
+ this.viewer.scene.requestRender();
28462
+ }
28463
+ }
28464
+ /*
28465
+ * Render pressure uses elapsed wall time because a paused timeline must still adapt.
28466
+ */
28467
+ ObserveFrame(now, moving) {
28468
+ const gap = now - this.lastFrame;
28469
+ this.lastFrame = now;
28470
+ if (moving) {
28471
+ this.lastMove = now;
28472
+ }
28473
+ if (!moving || gap <= 0 || gap > 1000) {
28474
+ this.slowSince = this.fastSince = 0;
28475
+ return;
28476
+ }
28477
+ this.slowSince = gap > 40 ? this.slowSince || now : 0;
28478
+ this.fastSince = gap < 22 ? this.fastSince || now : 0;
28479
+ if (now - this.lastChange < 4000) {
28480
+ return;
28481
+ }
28482
+ if (this.slowSince && now - this.slowSince >= 1500 && this.scale > 0.25) {
28483
+ this.scale = Math.max(0.25, this.scale * 0.75);
28484
+ this.lastChange = now;
28485
+ this.slowSince = this.fastSince = 0;
28486
+ this.lastUpdate = -Infinity;
28487
+ }
28488
+ else if (this.fastSince && now - this.fastSince >= 6000 && this.scale < 1) {
28489
+ this.scale = Math.min(1, this.scale + 0.125);
28490
+ this.lastChange = now;
28491
+ this.slowSince = this.fastSince = 0;
28492
+ this.lastUpdate = -Infinity;
28493
+ }
28494
+ }
28495
+ /*
28496
+ * Estimates visible screen coverage and distance, failing open for unknown bounds.
28497
+ */
28498
+ measure(entry) {
28499
+ var _a;
28500
+ entry.weight = 1;
28501
+ entry.radiusPixels = entry.surfaceDistance = Infinity;
28502
+ try {
28503
+ const object = entry.object;
28504
+ const bounds = (_a = object.getBounds) === null || _a === void 0 ? void 0 : _a.call(object);
28505
+ const matrix = object.modelMatrix;
28506
+ const camera = this.viewer.scene.camera;
28507
+ const width = this.viewer.scene.drawingBufferWidth;
28508
+ const height = this.viewer.scene.drawingBufferHeight;
28509
+ if (!(bounds === null || bounds === void 0 ? void 0 : bounds.min) || !(bounds === null || bounds === void 0 ? void 0 : bounds.max) || !matrix || !(width > 0 && height > 0)) {
28510
+ entry.culled = false;
28511
+ return;
28512
+ }
28513
+ const center = Cartesian3.midpoint(bounds.min, bounds.max, new Cartesian3());
28514
+ Matrix4.multiplyByPoint(matrix, center, center);
28515
+ const scale = Matrix4.getMaximumScale(matrix);
28516
+ const radius = Cartesian3.distance(bounds.min, bounds.max) * scale * 0.55 + 1;
28517
+ const distance = Cartesian3.distance(camera.positionWC, center);
28518
+ const pixel = camera.frustum.getPixelDimensions(width, height, Math.max(1, distance), 1, new Cartesian2());
28519
+ const pixels = radius / Math.max(pixel.x, pixel.y);
28520
+ if (!Number.isFinite(pixels) || !(radius > 0)) {
28521
+ entry.culled = false;
28522
+ return;
28523
+ }
28524
+ const sphere = new BoundingSphere(center, radius);
28525
+ const frustum = camera.frustum.computeCullingVolume(camera.positionWC, camera.directionWC, camera.upWC);
28526
+ const outside = frustum.computeVisibility(sphere) === Intersect.OUTSIDE;
28527
+ entry.culled = outside || pixels < (entry.culled ? 1.5 : 0.75);
28528
+ entry.radiusPixels = pixels;
28529
+ entry.weight = Math.pow(Math.min(1, Math.max(0.05, pixels / height)), 2);
28530
+ const local = Matrix4.multiplyByPoint(Matrix4.inverse(matrix, new Matrix4()), camera.positionWC, new Cartesian3());
28531
+ const nearest = new Cartesian3(Math$1.clamp(local.x, bounds.min.x, bounds.max.x), Math$1.clamp(local.y, bounds.min.y, bounds.max.y), Math$1.clamp(local.z, bounds.min.z, bounds.max.z));
28532
+ entry.surfaceDistance = Cartesian3.distance(local, nearest) * scale;
28533
+ }
28534
+ catch {
28535
+ // Unknown bounds or a changed optional SDK must fail open instead of hiding the scan.
28536
+ entry.culled = false;
28537
+ }
28538
+ }
28539
+ /*
28540
+ * Allocates the shared hardware budget across visible scans with hysteresis.
28541
+ */
28542
+ Update(now = performance.now()) {
28543
+ var _a, _b;
28544
+ if ((_b = (_a = this.viewer).isDestroyed) === null || _b === void 0 ? void 0 : _b.call(_a)) {
28545
+ this.Dispose();
28546
+ return;
28547
+ }
28548
+ if (now - this.lastUpdate < 250) {
28549
+ return;
28550
+ }
28551
+ this.lastUpdate = now;
28552
+ // A still view can recover detail gradually without treating missing frames as a slow GPU.
28553
+ if (now - this.lastMove > 3000 && now - this.lastChange > 6000 && this.scale < 1) {
28554
+ this.scale = Math.min(1, this.scale + 0.125);
28555
+ this.lastChange = now;
28556
+ }
28557
+ for (const entry of this.entries) {
28558
+ this.measure(entry);
28559
+ this.applyVisibility(entry);
28560
+ }
28561
+ const visible = this.entries.filter(entry => entry.wantedVisible && !entry.culled);
28562
+ if (!visible.length || now - this.lastAllocation < 2000) {
28563
+ return;
28564
+ }
28565
+ this.lastAllocation = now;
28566
+ const maximum = Math.max(...visible.map(entry => entry.maxBudget));
28567
+ const minimum = visible.reduce((sum, entry) => sum + Math.min(entry.capacity, entry.minimum), 0);
28568
+ // Preserve authored coarse coverage when it fits; dropping below a whole tile can erase a scan.
28569
+ const reserve = minimum <= maximum;
28570
+ let remaining = Math.max(maximum * this.scale, reserve ? minimum : 0);
28571
+ const allocations = new Map();
28572
+ visible.forEach(entry => {
28573
+ const floor = reserve ? Math.min(entry.capacity, entry.minimum) : 0;
28574
+ allocations.set(entry, floor);
28575
+ remaining -= floor;
28576
+ });
28577
+ let pending = visible.filter(entry => allocations.get(entry) < entry.capacity);
28578
+ while (pending.length) {
28579
+ const totalWeight = pending.reduce((sum, entry) => sum + entry.weight, 0);
28580
+ const capped = pending.filter(entry => remaining * entry.weight / totalWeight >= entry.capacity - allocations.get(entry));
28581
+ if (!capped.length) {
28582
+ pending.forEach(entry => allocations.set(entry, allocations.get(entry) + remaining * entry.weight / totalWeight));
28583
+ break;
28584
+ }
28585
+ capped.forEach(entry => {
28586
+ remaining -= entry.capacity - allocations.get(entry);
28587
+ allocations.set(entry, entry.capacity);
28588
+ });
28589
+ pending = pending.filter(entry => allocations.get(entry) < entry.capacity);
28590
+ }
28591
+ for (const entry of visible) {
28592
+ const allocation = allocations.get(entry);
28593
+ const step = allocation < 250000 ? 10000 : 250000;
28594
+ const budget = allocation >= entry.capacity ? entry.capacity : Math.max(1, reserve ? entry.minimum : 0, Math.floor(allocation / step) * step);
28595
+ const near = entry.surfaceDistance < (entry.near ? 20 : 12) && entry.radiusPixels > (entry.near ? 80 : 120);
28596
+ entry.near = near;
28597
+ const ceiling = entry.maxBudget <= 2000000 ? entry.maxBudget / 2 : near ? 4000000 : 2000000;
28598
+ const nodeBudget = Math.max(1, Math.min(ceiling, entry.capacity <= ceiling ? budget : budget * (near ? 2 / 3 : 1 / 3)));
28599
+ if (entry.budget !== budget) {
28600
+ entry.object.setMaxSplats(budget);
28601
+ entry.budget = budget;
28602
+ this.viewer.scene.requestRender();
28603
+ }
28604
+ if (entry.nodeBudget !== nodeBudget && typeof entry.object.setMaxNodeSplats === "function") {
28605
+ entry.object.setMaxNodeSplats(nodeBudget);
28606
+ entry.nodeBudget = nodeBudget;
28607
+ this.viewer.scene.requestRender();
28608
+ }
28609
+ }
28610
+ }
28611
+ /*
28612
+ * Restores the scan's visibility method and releases unused listeners.
28613
+ */
28614
+ Remove(object) {
28615
+ const entry = this.entries.find(entry => entry.object === object);
28616
+ if (entry) {
28617
+ object.setVisible = entry.setVisible;
28618
+ this.entries = this.entries.filter(other => other !== entry);
28619
+ this.lastUpdate = -Infinity;
28620
+ this.lastAllocation = -Infinity;
28621
+ }
28622
+ if (!this.entries.length) {
28623
+ this.Dispose();
28624
+ }
28625
+ }
28626
+ /*
28627
+ * Restores visibility intent and releases all controller listeners.
28628
+ */
28629
+ Dispose() {
28630
+ this.removals.forEach(remove => remove());
28631
+ this.removals = [];
28632
+ this.entries.forEach(entry => {
28633
+ entry.object.setVisible = entry.setVisible;
28634
+ try {
28635
+ if (entry.visible !== entry.wantedVisible && typeof entry.setVisible === "function") {
28636
+ entry.setVisible.call(entry.object, entry.wantedVisible);
28637
+ }
28638
+ }
28639
+ catch {
28640
+ // A disposed or replaced optional SDK must not break viewer cleanup.
28641
+ }
28642
+ });
28643
+ this.entries = [];
28644
+ if (this.viewer["_nextspaceXGridsLod"] === this) {
28645
+ delete this.viewer["_nextspaceXGridsLod"];
28646
+ }
28647
+ }
28648
+ }
28649
+ XGridsRenderEngine.AdaptiveLod = AdaptiveLod;
28306
28650
  /**
28307
28651
  * Applies position, rotation, and scale to a live XGrids render object.
28308
28652
  */
28309
28653
  function ApplyPosition(params) {
28310
28654
  const { renderObject, origin, rotation, dontRotate, dontTransform } = params;
28311
- if (!renderObject) {
28655
+ if (!renderObject || dontTransform) {
28312
28656
  return;
28313
28657
  }
28314
28658
  const lat = EnsureNumber(origin === null || origin === void 0 ? void 0 : origin.latitude, 0);
28315
28659
  const lon = EnsureNumber(origin === null || origin === void 0 ? void 0 : origin.longitude, 0);
28316
28660
  const alt = EnsureNumber(origin === null || origin === void 0 ? void 0 : origin.altitude, 0);
28317
28661
  const scale = EnsureNumber((origin === null || origin === void 0 ? void 0 : origin.scale) || 1, 1);
28662
+ // A complete matrix avoids SDK setters whose translation, rotation and scale contracts differ.
28663
+ if (typeof renderObject.setModelMatrix === "function") {
28664
+ const matrix = Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(lon, lat, alt));
28665
+ if (!dontRotate) {
28666
+ const hpr = HeadingPitchRoll.fromDegrees(EnsureNumber(rotation === null || rotation === void 0 ? void 0 : rotation.z), EnsureNumber(rotation === null || rotation === void 0 ? void 0 : rotation.x), EnsureNumber(rotation === null || rotation === void 0 ? void 0 : rotation.y));
28667
+ const local = Matrix4.fromRotation(Matrix3.fromHeadingPitchRoll(hpr));
28668
+ Matrix4.multiply(matrix, local, matrix);
28669
+ Matrix4.multiplyByUniformScale(matrix, scale, matrix);
28670
+ }
28671
+ renderObject.setModelMatrix(matrix);
28672
+ return;
28673
+ }
28318
28674
  if (!dontTransform) {
28319
28675
  const pos = Cartesian3.fromDegrees(lon, lat, alt);
28320
- if (typeof renderObject.setTranslation === 'function') {
28676
+ if (typeof renderObject.setTranslation === "function") {
28321
28677
  renderObject.setTranslation(pos);
28322
28678
  }
28323
28679
  }
28324
28680
  if (!dontRotate && !dontTransform) {
28325
28681
  const hpr = HeadingPitchRoll.fromDegrees(EnsureNumber(rotation === null || rotation === void 0 ? void 0 : rotation.z), EnsureNumber(rotation === null || rotation === void 0 ? void 0 : rotation.x), EnsureNumber(rotation === null || rotation === void 0 ? void 0 : rotation.y), new HeadingPitchRoll());
28326
28682
  const rotMatrix = Matrix3.fromHeadingPitchRoll(hpr);
28327
- if (typeof renderObject.setRotation === 'function') {
28683
+ if (typeof renderObject.setRotation === "function") {
28328
28684
  renderObject.setRotation(rotMatrix);
28329
28685
  }
28330
- if (typeof renderObject.setScale === 'function') {
28686
+ if (typeof renderObject.setScale === "function") {
28331
28687
  renderObject.setScale(new Cartesian3(scale, scale, scale));
28332
28688
  }
28333
28689
  }
@@ -49603,88 +49959,103 @@ var EntityRenderEngineModel3d;
49603
49959
  });
49604
49960
  cEntity.show = true;
49605
49961
  }
49606
- const fileRadiusKey = `model3d_${UrlUtils.RemoveSignature(params.lodUrl)}_${scale * styleScale}_radius`;
49607
- let heightProm = _fileRadiusCache.Get(fileRadiusKey);
49608
- if (!heightProm) {
49609
- heightProm = new Promise(async (res) => {
49610
- try {
49611
- // We'll have an async secondary model created for calculating boundaries.
49612
- // Once calculated we remove it.
49613
- const modelPrim = await getModelEntity(params.lodUrl, scale * styleScale);
49614
- const onReady = () => {
49615
- readyEventRemoval === null || readyEventRemoval === void 0 ? void 0 : readyEventRemoval();
49616
- readyEventRemoval = null;
49617
- const kill = () => {
49618
- if (params.viewer && !params.viewer.isDestroyed() && params.viewer.scene.primitives.contains(modelPrim)) {
49619
- params.viewer.scene.primitives.remove(modelPrim);
49962
+ const model = cEntity.model;
49963
+ model._radiusLoaded = false;
49964
+ /*
49965
+ * Retains bounds measurement for hosts that cannot expose the displayed model.
49966
+ */
49967
+ const measureTemporaryModelBounds = () => {
49968
+ const fileRadiusKey = `model3d_${UrlUtils.RemoveSignature(params.lodUrl)}_${scale * styleScale}_radius`;
49969
+ let heightProm = _fileRadiusCache.Get(fileRadiusKey);
49970
+ if (!heightProm) {
49971
+ heightProm = new Promise(async (res) => {
49972
+ try {
49973
+ // The fallback needs a temporary primitive until its bounds become available.
49974
+ const modelPrim = await getModelEntity(params.lodUrl, scale * styleScale);
49975
+ /*
49976
+ * Copies bounds before releasing the temporary primitive.
49977
+ */
49978
+ const onReady = () => {
49979
+ readyEventRemoval === null || readyEventRemoval === void 0 ? void 0 : readyEventRemoval();
49980
+ readyEventRemoval = null;
49981
+ /*
49982
+ * Releases the temporary primitive even if the viewer was destroyed.
49983
+ */
49984
+ const kill = () => {
49985
+ if (params.viewer && !params.viewer.isDestroyed() && params.viewer.scene.primitives.contains(modelPrim)) {
49986
+ params.viewer.scene.primitives.remove(modelPrim);
49987
+ }
49988
+ if (!modelPrim.isDestroyed()) {
49989
+ modelPrim.destroy();
49990
+ }
49991
+ };
49992
+ if (!cEntity || !(modelPrim === null || modelPrim === void 0 ? void 0 : modelPrim.boundingSphere)) {
49993
+ kill();
49994
+ res(null);
49995
+ return;
49620
49996
  }
49621
- if (!modelPrim.isDestroyed()) {
49622
- modelPrim.destroy();
49997
+ let radius = modelPrim.boundingSphere.radius;
49998
+ let centerOffset = modelPrim.boundingSphere.center;
49999
+ const bounds = {
50000
+ center: centerOffset ? Cartesian3.clone(centerOffset) : Cartesian3.ZERO.clone(),
50001
+ radius: modelPrim.boundingSphere.radius
50002
+ };
50003
+ if (centerOffset) {
50004
+ // Include the offset so origin-based culling contains the full model.
50005
+ const magnitude = Cartesian3.magnitude(centerOffset);
50006
+ radius += magnitude;
50007
+ // Preserve extra culling padding for models with large origin offsets.
50008
+ if (magnitude > 100) {
50009
+ radius += 100;
50010
+ }
49623
50011
  }
49624
- };
49625
- if (!cEntity || !(modelPrim === null || modelPrim === void 0 ? void 0 : modelPrim.boundingSphere)) {
49626
50012
  kill();
49627
- res(null);
49628
- return;
49629
- }
49630
- let radius = modelPrim.boundingSphere.radius;
49631
- let centerOffset = modelPrim.boundingSphere.center;
49632
- const bounds = {
49633
- center: centerOffset ? Cartesian3.clone(centerOffset) : Cartesian3.ZERO.clone(),
49634
- radius: modelPrim.boundingSphere.radius
50013
+ res({ radius, bounds });
49635
50014
  };
49636
- if (centerOffset) {
49637
- // Add the center offset to the radius because I am not good at math.
49638
- const magnitude = Cartesian3.magnitude(centerOffset);
49639
- radius += magnitude;
49640
- // Extra padding if the model is super not centered.
49641
- // I saw some strange boundaries for some bad models.
49642
- if (magnitude > 100) {
49643
- radius += 100;
49644
- }
50015
+ let readyEventRemoval = null;
50016
+ // Prefer the ready event when the host exposes it.
50017
+ if (modelPrim["readyEvent"]) {
50018
+ modelPrim["readyEvent"].addEventListener(onReady);
50019
+ }
50020
+ // Retain compatibility with promise-based hosts.
50021
+ else if (modelPrim["readyPromise"]) {
50022
+ modelPrim["readyPromise"].then(() => {
50023
+ onReady();
50024
+ }).catch((e) => {
50025
+ console.error(e);
50026
+ res(null);
50027
+ });
49645
50028
  }
49646
- kill();
49647
- res({ radius, bounds });
49648
- };
49649
- let readyEventRemoval = null;
49650
- // Newer Cesium versions have readyEvent. They don't use readyPromise.
49651
- if (modelPrim["readyEvent"]) {
49652
- modelPrim["readyEvent"].addEventListener(onReady);
49653
- }
49654
- // Older Cesium versions don't have readyEvent, they use readyPromise.
49655
- else if (modelPrim["readyPromise"]) {
49656
- modelPrim["readyPromise"].then(() => {
49657
- onReady();
49658
- }).catch((e) => {
49659
- console.error(e);
49660
- res(null);
49661
- });
50029
+ params.viewer.scene.primitives.add(modelPrim);
50030
+ }
50031
+ catch (e) {
50032
+ console.error(e);
50033
+ res(null);
50034
+ }
50035
+ });
50036
+ _fileRadiusCache.Set(fileRadiusKey, heightProm);
50037
+ }
50038
+ heightProm.then((measured) => {
50039
+ if (model) {
50040
+ model._radius = measured ? measured.radius : null;
50041
+ model._bounds = measured ? measured.bounds : null;
50042
+ model._radiusLoaded = true;
50043
+ if (params.viewer && params.viewer.scene && !params.viewer.isDestroyed()) {
50044
+ VisualRegisterCuller.MarkShouldRecheck(params.viewer);
49662
50045
  }
49663
- params.viewer.scene.primitives.add(modelPrim);
49664
50046
  }
49665
- catch (e) {
49666
- console.error(e);
49667
- res(null);
50047
+ // Rough estimate on when the model is ready in the scene.
50048
+ if (animateScale) {
50049
+ animateScale.Play();
49668
50050
  }
49669
50051
  });
49670
- _fileRadiusCache.Set(fileRadiusKey, heightProm);
50052
+ };
50053
+ if (Model.fromGltfAsync) {
50054
+ trackRenderedModelBounds(params.viewer, cEntity, model, scale * styleScale, measureTemporaryModelBounds, animateScale);
50055
+ }
50056
+ else {
50057
+ measureTemporaryModelBounds();
49671
50058
  }
49672
- heightProm.then((measured) => {
49673
- if (model) {
49674
- model._radius = measured ? measured.radius : null;
49675
- model._bounds = measured ? measured.bounds : null;
49676
- model._radiusLoaded = true;
49677
- if (params.viewer && params.viewer.scene && !params.viewer.isDestroyed()) {
49678
- VisualRegisterCuller.MarkShouldRecheck(params.viewer);
49679
- }
49680
- }
49681
- // Rough estimate on when the model is ready in the scene.
49682
- if (animateScale) {
49683
- animateScale.Play();
49684
- }
49685
- });
49686
- const model = cEntity.model;
49687
- model._radiusLoaded = false;
49688
50059
  model._clientFileId = params.lodClientFileId;
49689
50060
  model._entityScale = {
49690
50061
  record: scale,
@@ -49892,6 +50263,88 @@ var EntityRenderEngineModel3d;
49892
50263
  }
49893
50264
  EntityRenderEngineModel3d.RenderGroup = RenderGroup;
49894
50265
  })(EntityRenderEngineModel3d || (EntityRenderEngineModel3d = {}));
50266
+ /*
50267
+ * Measures bounds from Cesium's displayed model so loading a visual does not also load a temporary model.
50268
+ */
50269
+ function trackRenderedModelBounds(viewer, entity, model, targetScale, fallback, animateScale) {
50270
+ const key = "_nextspaceRenderedModelBoundsMonitor";
50271
+ let monitor = viewer[key];
50272
+ if (!monitor) {
50273
+ monitor = { pending: new Map() };
50274
+ viewer[key] = monitor;
50275
+ }
50276
+ monitor.pending.set(entity.id, { entity, model, targetScale, fallback, animateScale });
50277
+ if (monitor.removeListener) {
50278
+ return;
50279
+ }
50280
+ monitor.removeListener = viewer.scene.postRender.addEventListener(() => {
50281
+ var _a, _b, _c, _d, _e, _f;
50282
+ if (viewer.isDestroyed()) {
50283
+ monitor.pending.clear();
50284
+ }
50285
+ else {
50286
+ const primitives = viewer.scene.primitives;
50287
+ for (let i = 0; i < primitives.length && monitor.pending.size; i++) {
50288
+ const primitive = primitives.get(i);
50289
+ if (!(primitive instanceof Model) || !primitive.ready) {
50290
+ continue;
50291
+ }
50292
+ const task = monitor.pending.get((_a = primitive.id) === null || _a === void 0 ? void 0 : _a.id);
50293
+ if (!task || primitive.id !== task.entity || task.entity.model !== task.model) {
50294
+ continue;
50295
+ }
50296
+ try {
50297
+ const sphere = primitive.boundingSphere;
50298
+ const matrix = primitive.heightReference == null || primitive.heightReference === HeightReference.NONE
50299
+ ? primitive.modelMatrix
50300
+ : primitive._clampedModelMatrix;
50301
+ if (!sphere || !matrix ||
50302
+ !Number.isFinite(sphere.radius) || sphere.radius < 0 ||
50303
+ !Number.isFinite((_b = sphere.center) === null || _b === void 0 ? void 0 : _b.x) ||
50304
+ !Number.isFinite((_c = sphere.center) === null || _c === void 0 ? void 0 : _c.y) ||
50305
+ !Number.isFinite((_d = sphere.center) === null || _d === void 0 ? void 0 : _d.z)) {
50306
+ throw new Error("Displayed model bounds unavailable");
50307
+ }
50308
+ const inverse = Matrix4.inverseTransformation(matrix, new Matrix4());
50309
+ const center = Matrix4.multiplyByPoint(inverse, sphere.center, new Cartesian3());
50310
+ const scaleRatio = task.targetScale / primitive.scale;
50311
+ Cartesian3.multiplyByScalar(center, scaleRatio, center);
50312
+ const radius = sphere.radius * scaleRatio;
50313
+ const magnitude = Cartesian3.magnitude(center);
50314
+ if (!Number.isFinite(scaleRatio) || scaleRatio <= 0 || !Number.isFinite(magnitude) || !Number.isFinite(radius)) {
50315
+ throw new Error("Displayed model bounds transform invalid");
50316
+ }
50317
+ task.model._bounds = { center, radius };
50318
+ task.model._radius = radius + magnitude + (magnitude > 100 ? 100 : 0);
50319
+ task.model._radiusLoaded = true;
50320
+ (_e = task.animateScale) === null || _e === void 0 ? void 0 : _e.Play();
50321
+ VisualRegisterCuller.MarkShouldRecheck(viewer);
50322
+ }
50323
+ catch (e) {
50324
+ task.fallback();
50325
+ }
50326
+ monitor.pending.delete(task.entity.id);
50327
+ }
50328
+ for (const [id, task] of monitor.pending) {
50329
+ const modelShown = GetCValue(viewer, task.model.show) !== false;
50330
+ if (!task.entity.isShowing || !modelShown) {
50331
+ task.visibleAt = null;
50332
+ }
50333
+ else if (task.visibleAt == null) {
50334
+ task.visibleAt = performance.now();
50335
+ }
50336
+ else if (performance.now() - task.visibleAt > 60000) {
50337
+ task.fallback();
50338
+ monitor.pending.delete(id);
50339
+ }
50340
+ }
50341
+ }
50342
+ if (!monitor.pending.size) {
50343
+ (_f = monitor.removeListener) === null || _f === void 0 ? void 0 : _f.call(monitor);
50344
+ monitor.removeListener = null;
50345
+ }
50346
+ });
50347
+ }
49895
50348
  async function getName$1(api, entity, offline) {
49896
50349
  var _a;
49897
50350
  const ownName = GetUsableEntityName(entity);
@@ -57274,7 +57727,7 @@ var StyleUtils;
57274
57727
  StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
57275
57728
  })(StyleUtils || (StyleUtils = {}));
57276
57729
 
57277
- const VERSION = "7.7.4";
57730
+ const VERSION = "7.7.6";
57278
57731
  /**
57279
57732
  * Updates the environment instance used by bruce-cesium to one specified.
57280
57733
  * This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.