bruce-cesium 7.5.2 → 7.5.4

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, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, ConstantPositionProperty, HeightReference, DistanceDisplayCondition, HorizontalOrigin, VerticalOrigin, Cartesian3, ClassificationType, Math as Math$1, JulianDate, Quaternion, Transforms, HeadingPitchRoll, Matrix4, ArcType, CornerType, ShadowMode, ColorBlendMode, Model, ImageMaterialProperty, Ellipsoid, Cartesian2, SceneTransforms, ShaderSource, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, Cartesian4, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, PolygonHierarchy, PolylineGraphics, Rectangle, FeatureDetection, SceneMode, NearFarScalar, KmlDataSource, Matrix3, GeoJsonDataSource, Cesium3DTileStyle, HeadingPitchRange, Ion, IonResource, Cesium3DTileColorBlendMode, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, Cesium3DTileset, Intersect, OrthographicFrustum, EasingFunction, PolygonPipeline, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, CesiumInspector, defined, ClockRange, DynamicEnvironmentMapManager, GeometryInstance, EllipsoidGeodesic, sampleTerrainMostDetailed, CustomDataSource, ScreenSpaceEventHandler, ScreenSpaceEventType, Fullscreen, CzmlDataSource } from 'cesium';
3
- import { Cartes, LRUCache, ProjectViewTile, ENVIRONMENT, Api, Calculator, ClientFile, Entity as Entity$1, EntityTag, EntityType, ObjectUtils, Style, Carto, Geometry as Geometry$1, EntityLod, ClientFileValueMap, Color as Color$1, MenuItem, ProjectView, ProjectViewBookmark, Tileset, ZoomControl, Bounds, BruceEvent, EntityCoords, DataLab, DelayQueue, BruceApi, EntityRelation, AccountConcept, RecordChangeFeed, EntitySource, ProgramKey, ProjectViewLegacyTile, Camera, Account, AccountLimits, Session, UrlUtils, ProjectViewBookmarkGroup, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, EntityRelationType, EntityHistoricData } from 'bruce-models';
2
+ import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, HeightReference, ColorBlendMode, HeadingPitchRoll, Math as Math$1, Transforms, ShadowMode, Cartesian3, ClassificationType, Model, ShaderSource, Ellipsoid, Matrix4, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, Cartesian2, Cartesian4, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, JulianDate, Quaternion, ArcType, CornerType, ConstantPositionProperty, DistanceDisplayCondition, HorizontalOrigin, VerticalOrigin, ImageMaterialProperty, PolygonHierarchy, PolylineGraphics, Rectangle, SceneTransforms, NearFarScalar, Matrix3, KmlDataSource, FeatureDetection, SceneMode, GeoJsonDataSource, Cesium3DTileStyle, HeadingPitchRange, Cesium3DTileColorBlendMode, Ion, IonResource, Cesium3DTileset, OrthographicFrustum, EasingFunction, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, Intersect, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, PolygonPipeline, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, CustomDataSource, CesiumInspector, ClockRange, DynamicEnvironmentMapManager, ScreenSpaceEventHandler, ScreenSpaceEventType, GeometryInstance, Fullscreen, CzmlDataSource } from 'cesium';
3
+ import { Cartes, LRUCache, ProjectViewTile, ENVIRONMENT, Calculator, ClientFile, Entity as Entity$1, EntityLod, EntityTag, EntityType, ObjectUtils, Style, Carto, Geometry as Geometry$1, Api, ClientFileValueMap, Color as Color$1, BruceEvent, EntityCoords, MenuItem, Tileset, ProjectView, ProjectViewBookmark, ZoomControl, Bounds, DataLab, DelayQueue, AccountConcept, BruceApi, EntityRelation, RecordChangeFeed, EntitySource, Camera, ProgramKey, ProjectViewLegacyTile, Account, AccountLimits, Session, UrlUtils, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, ProjectViewBookmarkGroup, EntityRelationType, EntityHistoricData } from 'bruce-models';
4
4
 
5
5
  /**
6
6
  * Ensures a number is returned from a given value.
@@ -3028,6 +3028,7 @@ function isAlive(viewer, visual) {
3028
3028
  }
3029
3029
  let _selectColor = Color.fromAlpha(Color.YELLOW, 0.5);
3030
3030
  let _highlightColor = Color.fromCssColorString("#33b1ff").withAlpha(0.5);
3031
+ const DIMMING_REGISTERS_KEY = "_nextspaceDimmingRegisters";
3031
3032
  const STORE_COLOR_PREFIX = "_storeColor_";
3032
3033
  function getStoreKey(key) {
3033
3034
  return STORE_COLOR_PREFIX + key;
@@ -3184,6 +3185,10 @@ function refreshColor(viewer, graphic, opacity) {
3184
3185
  getColor$1(viewer, "default", graphic);
3185
3186
  }
3186
3187
  let color = (_a = getColor$1(viewer, key, graphic)) !== null && _a !== void 0 ? _a : Color.WHITE;
3188
+ // A model that is itself dimmed stays see-through. Only the ones standing out from dimmed surroundings go opaque.
3189
+ if (key != "default" && (opacity == null || opacity >= 1) && CesiumEntityStyler.IsSelectionOpaque(viewer, graphic)) {
3190
+ color = color.withAlpha(1);
3191
+ }
3187
3192
  // If we're selecting and it's highlighted, don't change the color.
3188
3193
  if (key != "select" || getKeyState(graphic, "highlight") == false) {
3189
3194
  let animateMs = key == "highlight" ? ANIMATE_COLOR_HIGHLIGHT_MS : ANIMATE_COLOR_MS;
@@ -3359,6 +3364,50 @@ function getKeyState(graphic, key) {
3359
3364
  */
3360
3365
  var CesiumEntityStyler;
3361
3366
  (function (CesiumEntityStyler) {
3367
+ /**
3368
+ * Whether a graphic or visual is a 3D model, ie: a tileset feature from a CAD or assembly import, or a model Entity.
3369
+ * @param graphic
3370
+ */
3371
+ function IsModel(graphic) {
3372
+ if (graphic instanceof Entity) {
3373
+ return Boolean(graphic.model);
3374
+ }
3375
+ return graphic instanceof Cesium3DTileFeature || graphic instanceof ModelGraphics;
3376
+ }
3377
+ CesiumEntityStyler.IsModel = IsModel;
3378
+ /**
3379
+ * Whether a graphic is drawn with an opaque selection and highlight because something on its viewer is dimmed.
3380
+ * Only 3D models are. GIS graphics keep the colour's own alpha.
3381
+ * @param viewer
3382
+ * @param graphic
3383
+ */
3384
+ function IsSelectionOpaque(viewer, graphic) {
3385
+ var _a;
3386
+ return Boolean((_a = viewer === null || viewer === void 0 ? void 0 : viewer[DIMMING_REGISTERS_KEY]) === null || _a === void 0 ? void 0 : _a.size) && IsModel(graphic);
3387
+ }
3388
+ CesiumEntityStyler.IsSelectionOpaque = IsSelectionOpaque;
3389
+ /**
3390
+ * Records whether a register dims anything on a viewer, by isolation or by opacity.
3391
+ * @param viewer
3392
+ * @param registerId
3393
+ * @param dimming
3394
+ * @returns Whether the viewer went into or out of dimming, so selected models can be recoloured.
3395
+ */
3396
+ function SetDimming(viewer, registerId, dimming) {
3397
+ let ids = viewer[DIMMING_REGISTERS_KEY];
3398
+ const before = Boolean(ids === null || ids === void 0 ? void 0 : ids.size);
3399
+ if (dimming) {
3400
+ if (!ids) {
3401
+ ids = viewer[DIMMING_REGISTERS_KEY] = new Set();
3402
+ }
3403
+ ids.add(registerId);
3404
+ }
3405
+ else {
3406
+ ids === null || ids === void 0 ? void 0 : ids.delete(registerId);
3407
+ }
3408
+ return before !== Boolean(ids === null || ids === void 0 ? void 0 : ids.size);
3409
+ }
3410
+ CesiumEntityStyler.SetDimming = SetDimming;
3362
3411
  function UpdateColorSetting(key, color) {
3363
3412
  if (key == "highlight") {
3364
3413
  _highlightColor = color.clone();
@@ -9858,6 +9907,8 @@ var VisualsRegister;
9858
9907
  // Caches GetIsIsolatedAny(). `null` means "unknown, recompute".
9859
9908
  // Invalidated on any write that could affect isolation.
9860
9909
  this.isolatedAnyCache = null;
9910
+ // Set when isolation or opacity changes, so the next update checks whether anything is still dimmed.
9911
+ this.dimmingDirty = false;
9861
9912
  // Caches GetIsolationOpacity(). `undefined` means "unknown, recompute", `null` means
9862
9913
  // "isolation (if any) is the legacy boolean/full-hide kind, not a numeric dim".
9863
9914
  // Invalidated alongside isolatedAnyCache.
@@ -10126,6 +10177,7 @@ var VisualsRegister;
10126
10177
  this.updateQueueInterval = null;
10127
10178
  return;
10128
10179
  }
10180
+ this.syncDimming();
10129
10181
  const batch = this.updateQueue.splice(0, BATCH_SIZE);
10130
10182
  const cache = {};
10131
10183
  for (let i = 0; i < batch.length; i++) {
@@ -10360,6 +10412,9 @@ var VisualsRegister;
10360
10412
  this.isolatedAnyCache = null;
10361
10413
  this.isolationOpacityCache = undefined;
10362
10414
  }
10415
+ if (keys.includes("isolated") || keys.includes("opacity")) {
10416
+ this.dimmingDirty = true;
10417
+ }
10363
10418
  let changed = false;
10364
10419
  const eStates = this.states[entityId] || {};
10365
10420
  this.states[entityId] = eStates;
@@ -10791,8 +10846,36 @@ var VisualsRegister;
10791
10846
  const { rego } = params;
10792
10847
  markEntity(this, rego, rego.visual, false);
10793
10848
  }
10849
+ /**
10850
+ * Tells the styler whether this register dims anything, by isolation or opacity.
10851
+ */
10852
+ syncDimming() {
10853
+ var _a, _b;
10854
+ if (!this.dimmingDirty) {
10855
+ return;
10856
+ }
10857
+ this.dimmingDirty = false;
10858
+ const dimming = Object.values(this.states).some(states => Object.values(states).some(state => state.isolated || (state.opacity != null && state.opacity < 1)));
10859
+ if (!CesiumEntityStyler.SetDimming(this.viewer, this.id, dimming)) {
10860
+ return;
10861
+ }
10862
+ const entityIds = new Set([...this.GetSelected(), ...this.GetHighlighted()]);
10863
+ for (const entityId of entityIds) {
10864
+ for (const rego of (_a = this.rego[entityId]) !== null && _a !== void 0 ? _a : []) {
10865
+ if (CesiumEntityStyler.IsModel(rego.visual)) {
10866
+ CesiumEntityStyler.Refresh({
10867
+ viewer: this.viewer,
10868
+ entity: rego.visual,
10869
+ requestRender: false
10870
+ });
10871
+ }
10872
+ }
10873
+ }
10874
+ (_b = this.viewer.scene) === null || _b === void 0 ? void 0 : _b.requestRender();
10875
+ }
10794
10876
  Dispose() {
10795
10877
  var _a, _b, _c, _d, _e;
10878
+ CesiumEntityStyler.SetDimming(this.viewer, this.id, false);
10796
10879
  this.ClearLabelHover();
10797
10880
  clearTimeout(this.hoverRingReset);
10798
10881
  this.hoverRingState = {};
@@ -11419,6 +11502,7 @@ var VisualsRegister;
11419
11502
  // Mutated `isolated` directly rather than through setStateValues().
11420
11503
  this.isolatedAnyCache = false;
11421
11504
  this.isolationOpacityCache = null;
11505
+ this.dimmingDirty = true;
11422
11506
  }
11423
11507
  if (changed && (params === null || params === void 0 ? void 0 : params.doUpdate) !== false) {
11424
11508
  this.updateAllEntities({
@@ -12155,6 +12239,7 @@ var VisualsRegister;
12155
12239
  }
12156
12240
  }
12157
12241
  async updateAllEntities(params) {
12242
+ this.syncDimming();
12158
12243
  const ids = Object.keys(this.rego);
12159
12244
  const interval = setInterval(() => {
12160
12245
  const batch = ids.splice(0, 3000);
@@ -12249,6 +12334,7 @@ var VisualsRegister;
12249
12334
  const clearedIds = new Set();
12250
12335
  this.ForEachState((state) => {
12251
12336
  if (!clearedIds.has(state.entityId) && state.opacity != null) {
12337
+ this.dimmingDirty = true;
12252
12338
  this.markLazyExclusion(state.entityId);
12253
12339
  this.queueUpdate({
12254
12340
  entityId: state.entityId,
@@ -17211,6 +17297,9 @@ class EntityGatherer {
17211
17297
  }
17212
17298
  };
17213
17299
  }
17300
+ HasEntityDataOverride(eId) {
17301
+ return this.entityDataOverrides.has(eId);
17302
+ }
17214
17303
  SetEntityDataOverride(eId, entity) {
17215
17304
  if (this.disposed) {
17216
17305
  return;
@@ -17907,6 +17996,24 @@ function correctStyle(style) {
17907
17996
  }
17908
17997
  return style;
17909
17998
  }
17999
+ /**
18000
+ * Reads the baked attribute declaration from a loaded tileset, keyed by path.
18001
+ * @param cTileset
18002
+ */
18003
+ function getBakedAttributes(cTileset) {
18004
+ var _a, _b;
18005
+ const result = new Map();
18006
+ const declared = (_b = (_a = cTileset === null || cTileset === void 0 ? void 0 : cTileset.extras) === null || _a === void 0 ? void 0 : _a.bruce) === null || _b === void 0 ? void 0 : _b.bakedAttributes;
18007
+ if (!Array.isArray(declared)) {
18008
+ return result;
18009
+ }
18010
+ for (const baked of declared) {
18011
+ if (Array.isArray(baked === null || baked === void 0 ? void 0 : baked.path) && baked.path.length && typeof baked.property == "string") {
18012
+ result.set(JSON.stringify(baked.path), baked);
18013
+ }
18014
+ }
18015
+ return result;
18016
+ }
17910
18017
  function colorToCColor$3(color) {
17911
18018
  return new Color(color.red ? color.red / 255 : 0, color.green ? color.green / 255 : 0, color.blue ? color.blue / 255 : 0, color.alpha);
17912
18019
  }
@@ -17977,6 +18084,10 @@ class TilesetStyler {
17977
18084
  // When false the Entity data is still gathered and the regos still updated, but no colour is
17978
18085
  // applied. A historic item that did not ask for styling needs the first without the second.
17979
18086
  this.applyStyles = true;
18087
+ // Attribute values the tiles carry per feature, keyed by JSON path. Empty for a tileset without them.
18088
+ this.bakedAttributes = new Map();
18089
+ // Entities changed since the tileset was generated, so their baked values can no longer be trusted.
18090
+ this.bakedStaleIds = new Set();
17980
18091
  this.reRenderState = new EntityReRenderMaintainState();
17981
18092
  // More expensive process.
17982
18093
  // When an Entity is styled, the rego is reviewed and updated if needed.
@@ -18018,6 +18129,7 @@ class TilesetStyler {
18018
18129
  this.viewer = viewer;
18019
18130
  this.api = api;
18020
18131
  this.cTileset = cTileset;
18132
+ this.bakedAttributes = getBakedAttributes(cTileset);
18021
18133
  this.register = register;
18022
18134
  this.menuItemId = menuItemId;
18023
18135
  this.historic = Boolean(historic);
@@ -18096,6 +18208,7 @@ class TilesetStyler {
18096
18208
  for (const [internalId, entityId] of this.styledByInternalId) {
18097
18209
  if (RecordChangeFeed.isEntityInternalIdInRangeString(rangeStr, internalId)) {
18098
18210
  this.feedPendingRestyleIds.add(entityId);
18211
+ this.bakedStaleIds.add(entityId);
18099
18212
  }
18100
18213
  }
18101
18214
  if (this.feedPendingRestyleIds.size && !this.feedRestyleThrottleTimer) {
@@ -18335,8 +18448,6 @@ class TilesetStyler {
18335
18448
  console.error(e);
18336
18449
  }
18337
18450
  // Before we start the loop we'll do a single request for the needed Entity Types.
18338
- // This will be aimed at rows that don't specify a Style and if the default fallback-
18339
- //is set to 0 (using the default of the Entity Type).
18340
18451
  const typeMap = new Map();
18341
18452
  const typeIds = styleMapping.map(x => x.StyleID == -1 || Boolean(x.style) ? null : x.EntityTypeID).filter(x => !!x);
18342
18453
  if (typeIds.length) {
@@ -18402,23 +18513,7 @@ class TilesetStyler {
18402
18513
  }
18403
18514
  this.styleMappingsLoaded.set(styleMap.EntityTypeID, true);
18404
18515
  if (this.loaded) {
18405
- // If we have a bunch of Entities that were waiting for the style to load,
18406
- // then we can pass them to the gatherer now.
18407
- const relatedIds = this.stylePendingEntityIds.get(styleMap.EntityTypeID);
18408
- if (relatedIds) {
18409
- if (this.getTilesetFeatureNeedsFullData(styleMap.EntityTypeID)) {
18410
- this.entityGatherer.Queue(Array.from(relatedIds));
18411
- }
18412
- else {
18413
- relatedIds.forEach(id => {
18414
- const rego = this.getEntityRego(id);
18415
- if (rego) {
18416
- this.styleTilesetFeature(rego);
18417
- }
18418
- });
18419
- }
18420
- this.stylePendingEntityIds.delete(styleMap.EntityTypeID);
18421
- }
18516
+ this.flushPendingForType(styleMap.EntityTypeID);
18422
18517
  }
18423
18518
  }
18424
18519
  if (this.loadingCounter != counter) {
@@ -18434,22 +18529,63 @@ class TilesetStyler {
18434
18529
  for (const key of pendingKeys) {
18435
18530
  const relatedIds = this.stylePendingEntityIds.get(key);
18436
18531
  if (relatedIds) {
18437
- if (this.getTilesetFeatureNeedsFullData(key)) {
18438
- this.entityGatherer.Queue(Array.from(relatedIds));
18439
- }
18440
- else {
18441
- relatedIds.forEach(id => {
18442
- const rego = this.getEntityRego(id);
18443
- if (rego) {
18444
- this.styleTilesetFeature(rego);
18445
- }
18446
- });
18447
- }
18532
+ this.styleOrGatherIds(key, relatedIds);
18448
18533
  }
18449
18534
  }
18450
18535
  this.stylePendingEntityIds.clear();
18451
18536
  }
18452
18537
  }
18538
+ /**
18539
+ * Passes the Entities waiting on an Entity Type's style on to be styled, now that the style is known.
18540
+ * @param entityTypeId
18541
+ */
18542
+ flushPendingForType(entityTypeId) {
18543
+ const relatedIds = this.stylePendingEntityIds.get(entityTypeId);
18544
+ if (!relatedIds) {
18545
+ return;
18546
+ }
18547
+ this.styleOrGatherIds(entityTypeId, relatedIds);
18548
+ this.stylePendingEntityIds.delete(entityTypeId);
18549
+ }
18550
+ hasMappingRow(entityTypeId) {
18551
+ var _a;
18552
+ return Boolean((_a = this.styleMapping) === null || _a === void 0 ? void 0 : _a.some(x => x.EntityTypeID == entityTypeId));
18553
+ }
18554
+ /**
18555
+ * Loads the default style of an Entity Type that the style mapping does not list.
18556
+ * @param entityTypeId
18557
+ */
18558
+ async loadEntityTypeDefaultStyle(entityTypeId) {
18559
+ const counter = this.loadingCounter;
18560
+ const styleMap = { EntityTypeID: entityTypeId, StyleID: 0, style: null };
18561
+ this.styleMapping.push(styleMap);
18562
+ try {
18563
+ const { entityType } = await EntityType.Get({
18564
+ api: this.api,
18565
+ entityTypeId
18566
+ });
18567
+ const styleId = entityType === null || entityType === void 0 ? void 0 : entityType["DisplaySetting.ID"];
18568
+ if (styleId && styleId != -1) {
18569
+ const { style } = await Style.Get({
18570
+ api: this.api,
18571
+ styleId
18572
+ });
18573
+ if (style) {
18574
+ styleMap.style = style;
18575
+ styleMap.StyleID = styleId;
18576
+ }
18577
+ }
18578
+ }
18579
+ catch (e) {
18580
+ console.error(e);
18581
+ }
18582
+ // A reload in the meantime resolves the row itself, or replaces the mapping and re-queues everything.
18583
+ if (this.disposed || this.loadingCounter != counter) {
18584
+ return;
18585
+ }
18586
+ this.styleMappingsLoaded.set(entityTypeId, true);
18587
+ this.flushPendingForType(entityTypeId);
18588
+ }
18453
18589
  disposeGatherer() {
18454
18590
  if (this.feedRestyleThrottleTimer) {
18455
18591
  clearTimeout(this.feedRestyleThrottleTimer);
@@ -18488,21 +18624,15 @@ class TilesetStyler {
18488
18624
  this.overrideFeatureColor.set(rego.entityId, false);
18489
18625
  }
18490
18626
  const typeId = rego.entityTypeId || "";
18491
- if (this.styleMappingLoaded || this.styleMappingsLoaded.get(typeId)) {
18492
- if (this.getTilesetFeatureNeedsFullData(typeId)) {
18493
- needsDataIds.push(rego.entityId);
18494
- }
18495
- else {
18496
- this.styleTilesetFeature(rego);
18497
- }
18627
+ // With no fallback style to use instead, a type the mapping does not list falls back to its own default.
18628
+ if (this.styleMappingLoaded && typeId && !this.fallbackStyle && !this.hasMappingRow(typeId)) {
18629
+ this.loadEntityTypeDefaultStyle(typeId);
18630
+ }
18631
+ if (this.styleMappingsLoaded.get(typeId) || (this.styleMappingLoaded && !this.hasMappingRow(typeId))) {
18632
+ this.styleOrCollect(rego, typeId, needsDataIds);
18498
18633
  }
18499
18634
  else if (!this.styleMappingsLoaded.has(typeId) && this.styleFallbackLoaded) {
18500
- if (this.getTilesetFeatureNeedsFullData(typeId)) {
18501
- needsDataIds.push(rego.entityId);
18502
- }
18503
- else {
18504
- this.styleTilesetFeature(rego);
18505
- }
18635
+ this.styleOrCollect(rego, typeId, needsDataIds);
18506
18636
  }
18507
18637
  else {
18508
18638
  if (!this.stylePendingEntityIds.has(typeId)) {
@@ -18515,6 +18645,96 @@ class TilesetStyler {
18515
18645
  this.entityGatherer.Queue(needsDataIds, highPriority);
18516
18646
  }
18517
18647
  }
18648
+ /**
18649
+ * Styles each Entity waiting on a type's style, gathering the records of those that need one.
18650
+ * @param entityTypeId
18651
+ * @param entityIds
18652
+ */
18653
+ styleOrGatherIds(entityTypeId, entityIds) {
18654
+ const gatherIds = [];
18655
+ for (const id of entityIds) {
18656
+ const rego = this.getEntityRego(id);
18657
+ if (rego) {
18658
+ this.styleOrCollect(rego, entityTypeId, gatherIds);
18659
+ }
18660
+ }
18661
+ if (gatherIds.length) {
18662
+ this.entityGatherer.Queue(gatherIds);
18663
+ }
18664
+ }
18665
+ /**
18666
+ * Styles a feature now when its style reads no record, or reads only values its tile carries, and otherwise collects it for gathering.
18667
+ * @param rego
18668
+ * @param entityTypeId
18669
+ * @param gatherIds Receives the Entity ID when its record is needed.
18670
+ */
18671
+ styleOrCollect(rego, entityTypeId, gatherIds) {
18672
+ if (!this.getTilesetFeatureNeedsFullData(entityTypeId)) {
18673
+ this.styleTilesetFeature(rego);
18674
+ return;
18675
+ }
18676
+ const record = this.canStyleFromBaked(rego.entityId, entityTypeId) ? this.readBakedRecord(rego.visual) : null;
18677
+ if (!record) {
18678
+ gatherIds.push(rego.entityId);
18679
+ return;
18680
+ }
18681
+ this.styleTilesetFeatureFullData(rego, record, []);
18682
+ // A later edit has to reach the record path, and the change feed finds Entities by Internal ID.
18683
+ if (rego.internalId != null) {
18684
+ this.styledByInternalId.set(rego.internalId, rego.entityId);
18685
+ }
18686
+ }
18687
+ /**
18688
+ * Returns if the tile's baked values hold everything this Entity's style would read from its record.
18689
+ * @param entityId
18690
+ * @param entityTypeId
18691
+ */
18692
+ canStyleFromBaked(entityId, entityTypeId) {
18693
+ var _a, _b, _c;
18694
+ // Baked values are the base record at generation time, which a scenario, a point in time or merged sources all differ from.
18695
+ if (!this.bakedAttributes.size || this.historic || !!this.scenario || this.expandSources) {
18696
+ return false;
18697
+ }
18698
+ if (this.bakedStaleIds.has(entityId) || ((_a = this.entityGatherer) === null || _a === void 0 ? void 0 : _a.HasEntityDataOverride(entityId))) {
18699
+ return false;
18700
+ }
18701
+ const paths = Calculator.GetFieldPaths((_c = (_b = this.getTilesetFeatureStyle(entityTypeId)) === null || _b === void 0 ? void 0 : _b.modelStyle) === null || _c === void 0 ? void 0 : _c.fillColor);
18702
+ return paths != null && paths.every(path => this.bakedAttributes.has(JSON.stringify(path)));
18703
+ }
18704
+ /**
18705
+ * Builds a stand-in record from a feature's baked values, holding only the attributes that have one.
18706
+ * Returns null when the feature's tile lacks a declared column, so the record is gathered instead.
18707
+ * @param visual
18708
+ */
18709
+ readBakedRecord(visual) {
18710
+ if (!(visual instanceof Cesium3DTileFeature)) {
18711
+ return null;
18712
+ }
18713
+ const record = {};
18714
+ for (const baked of this.bakedAttributes.values()) {
18715
+ if (!visual.hasProperty(baked.property)) {
18716
+ return null;
18717
+ }
18718
+ let value = visual.getProperty(baked.property);
18719
+ if (value == null) {
18720
+ continue;
18721
+ }
18722
+ // Booleans travel as 0/1 because a boolean column has no way to say "no value".
18723
+ if (baked.type == "BOOLEAN") {
18724
+ value = value != 0;
18725
+ }
18726
+ let target = record;
18727
+ for (let i = 0; i < baked.path.length - 1; i++) {
18728
+ const segment = baked.path[i];
18729
+ if (target[segment] == null || typeof target[segment] != "object") {
18730
+ target[segment] = {};
18731
+ }
18732
+ target = target[segment];
18733
+ }
18734
+ target[baked.path[baked.path.length - 1]] = value;
18735
+ }
18736
+ return record;
18737
+ }
18518
18738
  styleTilesetFeature(rego) {
18519
18739
  this.styleTilesetFeatureFullData(rego, null, []);
18520
18740
  }
@@ -40591,6 +40811,47 @@ function calcEntityLocation(entity, modelSpace) {
40591
40811
  }
40592
40812
  return Matrix4.multiplyByPoint(enuFrame, new Cartesian3(tx, ty, tz), new Cartesian3());
40593
40813
  }
40814
+ // Records created within about this long ago are the ones the oversized GeometryRadius bug could have written.
40815
+ const GEOMETRY_RADIUS_SUSPECT_AGE_MS = 1000 * 60 * 60 * 24 * 183;
40816
+ // Above this a recent record's radius is treated as that bug rather than a real size.
40817
+ const GEOMETRY_RADIUS_SUSPECT_ABOVE = 500;
40818
+ /**
40819
+ * Returns an entity's Bruce.GeometryRadius, or null when there is none or it looks like the oversized-radius bug.
40820
+ * The size cap only applies to records created recently; older ones and ones with no known date are taken as they are.
40821
+ * @param entity
40822
+ */
40823
+ function getTrustedGeometryRadius(entity) {
40824
+ var _a;
40825
+ const radius = EnsureNumber(Entity$1.GetValue({
40826
+ entity: entity,
40827
+ path: ["Bruce", "GeometryRadius"]
40828
+ }));
40829
+ if (!(radius > 0)) {
40830
+ return null;
40831
+ }
40832
+ const created = Date.parse((_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a.Created);
40833
+ const recent = !isNaN(created) && Date.now() - created < GEOMETRY_RADIUS_SUSPECT_AGE_MS;
40834
+ if (recent && radius >= GEOMETRY_RADIUS_SUSPECT_ABOVE) {
40835
+ return null;
40836
+ }
40837
+ return radius;
40838
+ }
40839
+ /**
40840
+ * Returns the world centre of a model's own bounds, applying the entity's orientation to the offset.
40841
+ * The position is the one GetLocation already resolved, so height references are honoured.
40842
+ * @param viewer
40843
+ * @param part
40844
+ * @param pos3d
40845
+ * @param bounds
40846
+ */
40847
+ function getModelBoundsCenter(viewer, part, pos3d, bounds) {
40848
+ const orientation = GetValue$1(viewer, part.orientation);
40849
+ const rotation = orientation
40850
+ ? Matrix3.fromQuaternion(orientation, new Matrix3())
40851
+ : Matrix4.getMatrix3(Transforms.eastNorthUpToFixedFrame(pos3d), new Matrix3());
40852
+ const offset = Matrix3.multiplyByVector(rotation, bounds.center, new Cartesian3());
40853
+ return Cartesian3.add(pos3d, offset, new Cartesian3());
40854
+ }
40594
40855
  // +90° rotation around Z: aligns GLTF Y-up forward with Cesium ENU North.
40595
40856
  const _assemblyAxisShift = new Quaternion(0, 0, -Math.SQRT1_2, Math.SQRT1_2);
40596
40857
  var EntityUtils;
@@ -40642,6 +40903,9 @@ var EntityUtils;
40642
40903
  rectangle: null,
40643
40904
  sphere: null
40644
40905
  };
40906
+ const spheres = [];
40907
+ // The tile holding a rendered feature that says nothing of where it is, used only when nothing better turns up.
40908
+ const tileSpheres = [];
40645
40909
  const processPosHeight = async (pos3d, heightRef) => {
40646
40910
  // Raise to minimumAlt if needed.
40647
40911
  if ((!isNaN(minimumAlt) && minimumAlt != null)) {
@@ -40719,7 +40983,7 @@ var EntityUtils;
40719
40983
  * Returns an array of positions from the entity's currently rendered graphics.
40720
40984
  */
40721
40985
  const evaluateRendered = async () => {
40722
- var _a, _b;
40986
+ var _a, _b, _c, _d;
40723
40987
  const rego = visualRegister ? visualRegister.GetRego({
40724
40988
  entityId: sample.entityId,
40725
40989
  menuItemId: sample.menuItemId
@@ -40851,9 +41115,14 @@ var EntityUtils;
40851
41115
  posses.push(pos3d);
40852
41116
  if (part.model) {
40853
41117
  const modelExt = part.model;
40854
- if (modelExt._radius && modelExt._radius > -1) {
41118
+ const bounds = modelExt._bounds;
41119
+ if ((bounds === null || bounds === void 0 ? void 0 : bounds.radius) > 0) {
41120
+ spheres.push(new BoundingSphere(getModelBoundsCenter(viewer, part, pos3d, bounds), bounds.radius));
41121
+ }
41122
+ else if (modelExt._radius && modelExt._radius > -1) {
40855
41123
  const sphere = BoundingSphere.fromPoints([pos3d]);
40856
41124
  sphere.radius = modelExt._radius;
41125
+ spheres.push(sphere);
40857
41126
  }
40858
41127
  }
40859
41128
  else if (part.ellipse) {
@@ -40872,6 +41141,7 @@ var EntityUtils;
40872
41141
  const radius = Math.max(semiMajorAxis, semiMinorAxis, height);
40873
41142
  if (radius && radius > 0) {
40874
41143
  const sphere = new BoundingSphere(pos3d, radius);
41144
+ spheres.push(sphere);
40875
41145
  }
40876
41146
  }
40877
41147
  }
@@ -40890,6 +41160,14 @@ var EntityUtils;
40890
41160
  posses.push(pos3d);
40891
41161
  if (data.radius != null && !isNaN(data.radius)) {
40892
41162
  const sphere = new BoundingSphere(pos3d, data.radius);
41163
+ spheres.push(sphere);
41164
+ }
41165
+ }
41166
+ else {
41167
+ // Missing from the typings this library builds against, but there at runtime.
41168
+ const tileSphere = (_d = (_c = part.content) === null || _c === void 0 ? void 0 : _c.tile) === null || _d === void 0 ? void 0 : _d.boundingSphere;
41169
+ if ((tileSphere === null || tileSphere === void 0 ? void 0 : tileSphere.radius) > 0) {
41170
+ tileSpheres.push(BoundingSphere.clone(tileSphere));
40893
41171
  }
40894
41172
  }
40895
41173
  }
@@ -40969,7 +41247,7 @@ var EntityUtils;
40969
41247
  }
40970
41248
  const boundaries = Entity$1.GetValue({
40971
41249
  entity: entity,
40972
- path: ["Bruce, Boundaries"]
41250
+ path: ["Bruce", "Boundaries"]
40973
41251
  });
40974
41252
  if (boundaries && typeof boundaries == "object") {
40975
41253
  const point = {
@@ -41022,16 +41300,9 @@ var EntityUtils;
41022
41300
  const transformedOffset = Matrix3.multiplyByVector(transformHpr, offset, new Cartesian3());
41023
41301
  pos3d = Matrix4.multiplyByPoint(m1, transformedOffset, new Cartesian3());
41024
41302
  pos3d = await processPosHeight(pos3d, HeightReference.NONE);
41025
- const geometryRadius = Entity$1.GetValue({
41026
- entity: entity,
41027
- path: ["Bruce", "GeometryRadius"]
41028
- });
41303
+ const geometryRadius = getTrustedGeometryRadius(entity);
41029
41304
  if (geometryRadius && (pos3d === null || pos3d === void 0 ? void 0 : pos3d.x)) {
41030
- const sphere = BoundingSphere.fromPoints([pos3d]);
41031
- // For now making sure it's less than x amount because we had a bug which made it huge.
41032
- if (geometryRadius && geometryRadius < 500 && geometryRadius > 0.1) {
41033
- sphere.radius = geometryRadius;
41034
- }
41305
+ spheres.push(new BoundingSphere(pos3d, geometryRadius));
41035
41306
  }
41036
41307
  return [pos3d];
41037
41308
  }
@@ -41039,16 +41310,9 @@ var EntityUtils;
41039
41310
  if (location) {
41040
41311
  let pos3d = Cartesian3.fromDegrees(longitude, latitude, EnsureNumber(location.altitude));
41041
41312
  pos3d = await processPosHeight(pos3d, HeightReference.NONE);
41042
- const geometryRadius = Entity$1.GetValue({
41043
- entity: entity,
41044
- path: ["Bruce", "GeometryRadius"]
41045
- });
41313
+ const geometryRadius = getTrustedGeometryRadius(entity);
41046
41314
  if (geometryRadius && (pos3d === null || pos3d === void 0 ? void 0 : pos3d.x)) {
41047
- const sphere = BoundingSphere.fromPoints([pos3d]);
41048
- // For now making sure it's less than x amount because we had a bug which made it huge.
41049
- if (geometryRadius && geometryRadius < 500 && geometryRadius > 0.1) {
41050
- sphere.radius = geometryRadius;
41051
- }
41315
+ spheres.push(new BoundingSphere(pos3d, geometryRadius));
41052
41316
  }
41053
41317
  return [pos3d];
41054
41318
  }
@@ -41071,16 +41335,9 @@ var EntityUtils;
41071
41335
  }
41072
41336
  if (pos3d) {
41073
41337
  pos3d = await processPosHeight(pos3d, HeightReference.NONE);
41074
- const geometryRadius = Entity$1.GetValue({
41075
- entity: entity,
41076
- path: ["Bruce", "GeometryRadius"]
41077
- });
41338
+ const geometryRadius = getTrustedGeometryRadius(entity);
41078
41339
  if (geometryRadius && (pos3d === null || pos3d === void 0 ? void 0 : pos3d.x)) {
41079
- const sphere = BoundingSphere.fromPoints([pos3d]);
41080
- // For now making sure it's less than x amount because we had a bug which made it huge.
41081
- if (geometryRadius && geometryRadius < 500 && geometryRadius > 0.1) {
41082
- sphere.radius = geometryRadius;
41083
- }
41340
+ spheres.push(new BoundingSphere(pos3d, geometryRadius));
41084
41341
  }
41085
41342
  return [pos3d];
41086
41343
  }
@@ -41100,16 +41357,9 @@ var EntityUtils;
41100
41357
  if (pos3d === null || pos3d === void 0 ? void 0 : pos3d.x) {
41101
41358
  pos3d = await processPosHeight(pos3d, HeightReference.NONE);
41102
41359
  }
41103
- const geometryRadius = Entity$1.GetValue({
41104
- entity: entity,
41105
- path: ["Bruce", "GeometryRadius"]
41106
- });
41360
+ const geometryRadius = getTrustedGeometryRadius(entity);
41107
41361
  if (geometryRadius && (pos3d === null || pos3d === void 0 ? void 0 : pos3d.x)) {
41108
- const sphere = BoundingSphere.fromPoints([pos3d]);
41109
- // For now making sure it's less than x amount because we had a bug which made it huge.
41110
- if (geometryRadius && geometryRadius < 500 && geometryRadius > 0.1) {
41111
- sphere.radius = geometryRadius;
41112
- }
41362
+ spheres.push(new BoundingSphere(pos3d, geometryRadius));
41113
41363
  }
41114
41364
  }
41115
41365
  }
@@ -41344,6 +41594,7 @@ var EntityUtils;
41344
41594
  radius /= 2;
41345
41595
  }
41346
41596
  const sphere = new BoundingSphere(data.pos3d, radius);
41597
+ spheres.push(sphere);
41347
41598
  data.sphere = sphere;
41348
41599
  }
41349
41600
  else {
@@ -41353,23 +41604,24 @@ var EntityUtils;
41353
41604
  else if (allPosses.length == 1) {
41354
41605
  data.pos3d = allPosses[0];
41355
41606
  }
41356
- /*
41357
- Was used to clean up bad radius data but now seems to do more harm than good.
41358
- TODO: re-review edge cases.
41359
- if ((data.rectangle && spheres.length > 1) || (!data.rectangle && spheres.length > 0)) {
41360
- const allPointSphere = Cesium.BoundingSphere.fromPoints(allPosses);
41361
- spheres.push(allPointSphere);
41362
- if (data.rectangle) {
41363
- const rectSphere = Cesium.BoundingSphere.fromRectangle3D(data.rectangle);
41364
- spheres.push(rectSphere);
41365
- }
41366
- const combinedSphere = Cesium.BoundingSphere.fromBoundingSpheres(spheres);
41367
- data.sphere = combinedSphere;
41607
+ // The entities' own sizes, so a large piece is framed whole and a small one is not left far away.
41608
+ // The rectangle sphere above only spans the sampled points, which for one entity is its pivot alone.
41609
+ const sizeSpheres = spheres.filter(x => x !== data.sphere && x.radius > 0);
41610
+ if (sizeSpheres.length) {
41611
+ data.sphere = BoundingSphere.fromBoundingSpheres(data.sphere ? [data.sphere, ...sizeSpheres] : sizeSpheres);
41612
+ if (allPosses.length <= 1) {
41613
+ data.pos3d = data.sphere.center;
41614
+ }
41368
41615
  }
41369
- */
41370
41616
  if (data.sphere && data.sphere.radius <= 0) {
41371
41617
  data.sphere = null;
41372
41618
  }
41619
+ // An Assembly part's record can come back without a location while the API has yet to resolve its model's
41620
+ // bounds, and its feature carries no position of its own. The tile it is drawn in still says roughly where.
41621
+ if (!data.pos3d && tileSpheres.length) {
41622
+ data.sphere = BoundingSphere.fromBoundingSpheres(tileSpheres);
41623
+ data.pos3d = data.sphere.center;
41624
+ }
41373
41625
  return data;
41374
41626
  }
41375
41627
  EntityUtils.GetLocation = GetLocation;
@@ -43937,6 +44189,10 @@ var EntityRenderEngineModel3d;
43937
44189
  }
43938
44190
  let radius = modelPrim.boundingSphere.radius;
43939
44191
  let centerOffset = modelPrim.boundingSphere.center;
44192
+ const bounds = {
44193
+ center: centerOffset ? Cartesian3.clone(centerOffset) : Cartesian3.ZERO.clone(),
44194
+ radius: modelPrim.boundingSphere.radius
44195
+ };
43940
44196
  if (centerOffset) {
43941
44197
  // Add the center offset to the radius because I am not good at math.
43942
44198
  const magnitude = Cartesian3.magnitude(centerOffset);
@@ -43948,7 +44204,7 @@ var EntityRenderEngineModel3d;
43948
44204
  }
43949
44205
  }
43950
44206
  kill();
43951
- res(radius);
44207
+ res({ radius, bounds });
43952
44208
  };
43953
44209
  let readyEventRemoval = null;
43954
44210
  // Newer Cesium versions have readyEvent. They don't use readyPromise.
@@ -43973,9 +44229,10 @@ var EntityRenderEngineModel3d;
43973
44229
  });
43974
44230
  _fileRadiusCache.Set(fileRadiusKey, heightProm);
43975
44231
  }
43976
- heightProm.then((radius) => {
44232
+ heightProm.then((measured) => {
43977
44233
  if (model) {
43978
- model._radius = radius;
44234
+ model._radius = measured ? measured.radius : null;
44235
+ model._bounds = measured ? measured.bounds : null;
43979
44236
  model._radiusLoaded = true;
43980
44237
  if (params.viewer && params.viewer.scene && !params.viewer.isDestroyed()) {
43981
44238
  VisualRegisterCuller.MarkShouldRecheck(params.viewer);
@@ -49785,9 +50042,6 @@ var StyleEffective;
49785
50042
  StyleEffective.Combine = Combine;
49786
50043
  /**
49787
50044
  * Returns true if every segment in the filter string appears in the subject effective string.
49788
- * A filter segment matches if the subject contains a segment starting with the same key,
49789
- * and either no value filter is given or the value matches exactly.
49790
- * Passing a partial effective value (e.g. just "f0:gradient") also matches.
49791
50045
  */
49792
50046
  function MatchesFilter(effective, filter) {
49793
50047
  if (!filter) {
@@ -51056,7 +51310,7 @@ var StyleUtils;
51056
51310
  StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
51057
51311
  })(StyleUtils || (StyleUtils = {}));
51058
51312
 
51059
- const VERSION = "7.5.2";
51313
+ const VERSION = "7.5.4";
51060
51314
  /**
51061
51315
  * Updates the environment instance used by bruce-cesium to one specified.
51062
51316
  * This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.