bruce-cesium 6.4.0 → 6.4.2

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
- import { Cartes, Entity as Entity$1, Calculator, EntityRelationType, EntityType, Style, ENVIRONMENT, ProjectViewTile, DelayQueue, LRUCache, BruceEvent, ObjectUtils, Geometry, EntityHistoricData, EntityLod, ZoomControl, EntityTag, Tileset, Api, EntityCoords, DataLab, EntitySource, ClientFile, MenuItem, EntityRelation, ProgramKey, Bounds, Carto, ProjectView, ProjectViewBookmark, Camera, ProjectViewLegacyTile, AbstractApi, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, Session } from 'bruce-models';
1
+ import { Cartes, Entity as Entity$1, Calculator, EntityRelationType, EntityType, Style, ENVIRONMENT, ProjectViewTile, DelayQueue, LRUCache, BruceEvent, ObjectUtils, Geometry, EntityHistoricData, EntityLod, ZoomControl, EntityTag, Tileset, Api, EntityCoords, DataLab, EntitySource, ClientFile, MenuItem, EntityRelation, ProgramKey, Bounds, Carto, ProjectView, ProjectViewBookmark, ProjectViewLegacyTile, Camera, AbstractApi, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, Session } from 'bruce-models';
2
2
  import * as Cesium from 'cesium';
3
- import { Cartographic, Entity, DistanceDisplayCondition, ColorMaterialProperty, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, Math as Math$1, Cartesian3, JulianDate, Quaternion, Transforms, HeadingPitchRoll, Matrix4, HeightReference, ColorBlendMode, ShadowMode, ClassificationType, Model, PolygonHierarchy, ConstantPositionProperty, PolylineGraphics, ArcType, CornerType, HorizontalOrigin, VerticalOrigin, Cartesian2, SceneTransforms, NearFarScalar, Matrix3, Rectangle, KmlDataSource, GeoJsonDataSource, SceneMode, Cesium3DTileStyle, HeadingPitchRange, Ion, Cesium3DTileColorBlendMode, Cesium3DTileset, IonResource, OrthographicFrustum, EasingFunction, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, PolygonPipeline, CesiumInspector, ClockRange, BoundingSphere, GeometryInstance, ScreenSpaceEventHandler, ScreenSpaceEventType, Intersect, CzmlDataSource, Fullscreen } from 'cesium';
3
+ import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, DistanceDisplayCondition, HeightReference, ColorBlendMode, HeadingPitchRoll, Math as Math$1, Transforms, ShadowMode, Cartesian3, ClassificationType, Model, PolygonHierarchy, ConstantPositionProperty, PolylineGraphics, ArcType, CornerType, HorizontalOrigin, VerticalOrigin, JulianDate, Quaternion, Matrix4, Cartesian2, SceneTransforms, NearFarScalar, Matrix3, Rectangle, KmlDataSource, GeoJsonDataSource, SceneMode, Cesium3DTileStyle, HeadingPitchRange, Ion, Cesium3DTileColorBlendMode, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, IonResource, OrthographicFrustum, EasingFunction, Cesium3DTileset, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, PolygonPipeline, BoundingSphere, GeometryInstance, CesiumInspector, ClockRange, ScreenSpaceEventHandler, ScreenSpaceEventType, Intersect, CzmlDataSource, Fullscreen } from 'cesium';
4
4
 
5
5
  /**
6
6
  * Ensures a number is returned from a given value.
@@ -757,6 +757,7 @@ var CesiumAnimatedProperty;
757
757
  */
758
758
  class AnimatePositionSeries {
759
759
  constructor(params) {
760
+ this.useRealTime = false;
760
761
  this.currentPos = null;
761
762
  this.currentVelocity = new Cartesian3(0, 0, 0);
762
763
  this.smoothingFactor = 0.5;
@@ -771,13 +772,45 @@ var CesiumAnimatedProperty;
771
772
  this.lastCalcSeriesTime = null;
772
773
  this.lastCalcOrient = null;
773
774
  this.lastCalcOrientTime = null;
775
+ // Real-time animation fields (used when useRealTime is true).
776
+ this.realTimeAnimStartPos = null;
777
+ this.realTimeAnimStartTime = null;
778
+ this.realTimeAnimTarget = null;
774
779
  this.viewer = params.viewer;
775
780
  this.positions = params.posses || [];
776
781
  this.pitch = params.pitch || 0;
777
782
  this.roll = params.roll || 0;
783
+ this.useRealTime = Boolean(params.useRealTime);
778
784
  this.processHeadings();
779
785
  this.sortPositions();
780
786
  }
787
+ /**
788
+ * Returns the appropriate time in milliseconds for animation calculations.
789
+ * When useRealTime is true, returns wall-clock time (Date.now()).
790
+ * Otherwise returns the Cesium viewer time.
791
+ */
792
+ getTimeMs() {
793
+ if (this.useRealTime) {
794
+ return Date.now();
795
+ }
796
+ let viewerTime = this.viewer.scene.lastRenderTime;
797
+ if (!viewerTime) {
798
+ viewerTime = this.viewer.clock.currentTime;
799
+ }
800
+ return JulianDate.toDate(viewerTime).getTime();
801
+ }
802
+ SetUseRealTime(value) {
803
+ const changed = this.useRealTime !== value;
804
+ this.useRealTime = value;
805
+ if (changed) {
806
+ // Reset real-time animation state so the next updateRealTimePosition
807
+ // starts fresh from the current position.
808
+ this.realTimeAnimStartPos = null;
809
+ this.realTimeAnimStartTime = null;
810
+ this.realTimeAnimTarget = null;
811
+ this.invalidateCache();
812
+ }
813
+ }
781
814
  AddPosition(pos) {
782
815
  if (!pos || !pos.pos3d || !pos.dateTime) {
783
816
  console.warn("Invalid position provided to AnimatePositionSeries.");
@@ -973,11 +1006,7 @@ var CesiumAnimatedProperty;
973
1006
  };
974
1007
  }
975
1008
  GetValue() {
976
- let viewerTime = this.viewer.scene.lastRenderTime;
977
- if (!viewerTime) {
978
- viewerTime = this.viewer.clock.currentTime;
979
- }
980
- const viewerTimeMs = JulianDate.toDate(viewerTime).getTime();
1009
+ const viewerTimeMs = this.getTimeMs();
981
1010
  const currentRealTimeMs = Date.now();
982
1011
  const expected = this.calculateExpectedPosition(viewerTimeMs);
983
1012
  if (!expected.position) {
@@ -994,6 +1023,10 @@ var CesiumAnimatedProperty;
994
1023
  this.lastUpdateTime = currentRealTimeMs;
995
1024
  return this.currentPos;
996
1025
  }
1026
+ // For real-time mode, use a fixed-duration eased lerp instead of velocity-based smoothing.
1027
+ if (this.useRealTime) {
1028
+ return this.updateRealTimePosition(expected.position, currentRealTimeMs);
1029
+ }
997
1030
  const deltaTime = this.lastUpdateTime ? (currentRealTimeMs - this.lastUpdateTime) / 1000.0 : 0.016;
998
1031
  this.lastUpdateTime = currentRealTimeMs;
999
1032
  const clampedDeltaTime = Math.min(deltaTime, 0.1);
@@ -1018,6 +1051,37 @@ var CesiumAnimatedProperty;
1018
1051
  this.currentPos = Cartesian3.add(this.currentPos, velocityDelta, new Cartesian3());
1019
1052
  return this.currentPos;
1020
1053
  }
1054
+ /**
1055
+ * Fixed-duration eased position animation for real-time (plugin-style) updates.
1056
+ * Instead of velocity-based smoothing, lerps from the current position to the target
1057
+ * over a fixed duration with ease-in-out.
1058
+ */
1059
+ updateRealTimePosition(targetPos, currentTimeMs) {
1060
+ const duration = AnimatePositionSeries.REAL_TIME_ANIM_DURATION_MS;
1061
+ // Detect target change. Start a new animation segment.
1062
+ if (!this.realTimeAnimTarget || !Cartesian3.equals(this.realTimeAnimTarget, targetPos)) {
1063
+ this.realTimeAnimStartPos = this.currentPos.clone();
1064
+ this.realTimeAnimStartTime = currentTimeMs;
1065
+ this.realTimeAnimTarget = targetPos.clone();
1066
+ }
1067
+ const elapsed = currentTimeMs - this.realTimeAnimStartTime;
1068
+ if (elapsed >= duration) {
1069
+ this.currentPos = this.realTimeAnimTarget.clone();
1070
+ this.currentVelocity = new Cartesian3(0, 0, 0);
1071
+ this.lastUpdateTime = currentTimeMs;
1072
+ return this.currentPos;
1073
+ }
1074
+ const t = this.easeInOutQuad(elapsed / duration);
1075
+ const newPos = Cartesian3.lerp(this.realTimeAnimStartPos, this.realTimeAnimTarget, t, new Cartesian3());
1076
+ // Compute velocity from frame delta so GetOrient() can derive heading from movement.
1077
+ const deltaTime = this.lastUpdateTime ? (currentTimeMs - this.lastUpdateTime) / 1000.0 : 0.016;
1078
+ this.lastUpdateTime = currentTimeMs;
1079
+ if (deltaTime > 0.001) {
1080
+ this.currentVelocity = Cartesian3.divideByScalar(Cartesian3.subtract(newPos, this.currentPos, new Cartesian3()), deltaTime, new Cartesian3());
1081
+ }
1082
+ this.currentPos = newPos;
1083
+ return this.currentPos;
1084
+ }
1021
1085
  GetSeries() {
1022
1086
  let doUpdate = this.lastCalcSeriesTime == null;
1023
1087
  if (!doUpdate && this.lastCalcSeriesTime && (new Date().getTime() - this.lastCalcSeriesTime) > 1000 / 30) {
@@ -1027,13 +1091,10 @@ var CesiumAnimatedProperty;
1027
1091
  return this.lastCalcSeriesPos3d;
1028
1092
  }
1029
1093
  this.GetValue();
1030
- let now = this.viewer.scene.lastRenderTime;
1031
- if (!now) {
1032
- now = this.viewer.clock.currentTime;
1033
- }
1034
- const nowDate = JulianDate.toDate(now);
1094
+ const nowTimeMs = this.getTimeMs();
1095
+ const nowDate = new Date(nowTimeMs);
1035
1096
  if (!this.positions || this.positions.length < 2) {
1036
- this.lastCalcSeriesTime = nowDate.getTime();
1097
+ this.lastCalcSeriesTime = nowTimeMs;
1037
1098
  this.lastCalcSeriesPos3d = [];
1038
1099
  return [];
1039
1100
  }
@@ -1054,7 +1115,7 @@ var CesiumAnimatedProperty;
1054
1115
  newPosses.push(pos.pos3d);
1055
1116
  }
1056
1117
  }
1057
- this.lastCalcSeriesTime = nowDate.getTime();
1118
+ this.lastCalcSeriesTime = nowTimeMs;
1058
1119
  this.lastCalcSeriesPos3d = newPosses;
1059
1120
  return newPosses;
1060
1121
  }
@@ -1087,11 +1148,7 @@ var CesiumAnimatedProperty;
1087
1148
  this.lastCalcOrientTime = Date.now();
1088
1149
  return this.lastCalcOrient;
1089
1150
  }
1090
- let now = this.viewer.scene.lastRenderTime;
1091
- if (!now) {
1092
- now = this.viewer.clock.currentTime;
1093
- }
1094
- const nowTime = JulianDate.toDate(now).getTime();
1151
+ const nowTime = this.getTimeMs();
1095
1152
  const lastIndex = this.lastDesiredPosIndex;
1096
1153
  const nextIndex = this.lastDesiredNextIndex;
1097
1154
  if (lastIndex < 0 || nextIndex < 0 ||
@@ -1227,6 +1284,7 @@ var CesiumAnimatedProperty;
1227
1284
  return this.positions.length;
1228
1285
  }
1229
1286
  }
1287
+ AnimatePositionSeries.REAL_TIME_ANIM_DURATION_MS = 500;
1230
1288
  CesiumAnimatedProperty.AnimatePositionSeries = AnimatePositionSeries;
1231
1289
  function GetSeriesPossesForHistoricEntity(viewer, dataHeightRef, heightRef, historic) {
1232
1290
  if (!historic || !historic.length) {
@@ -31268,7 +31326,10 @@ var EntityRenderEngineModel3d;
31268
31326
  }
31269
31327
  };
31270
31328
  let cEntity = params.rendered;
31271
- let prevPos3d = cEntity ? GetCValue(params.viewer, cEntity.position) : null;
31329
+ // Use the stored position from the previous render rather than evaluating the position callback.
31330
+ // The callback may reference the entity object which can be mutated in-place (e.g., by plugins)
31331
+ // before Render is called, which would make prevPos3d == pos3d and skip animation.
31332
+ let prevPos3d = (cEntity === null || cEntity === void 0 ? void 0 : cEntity.model) ? cEntity.model._lastPos3d : null;
31272
31333
  if (!cEntity || !cEntity.model) {
31273
31334
  updateShouldShowTrack();
31274
31335
  if (!color) {
@@ -31358,23 +31419,46 @@ var EntityRenderEngineModel3d;
31358
31419
  cEntity.model.colorBlendAmount = new ConstantProperty(blendAmount);
31359
31420
  cEntity.model.colorBlendMode = new ConstantProperty(blendMode);
31360
31421
  cEntity.model.distanceDisplayCondition = new ConstantProperty(EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance));
31361
- const dateTimeStr = (_b = (_a = entity.Bruce.Outline) === null || _a === void 0 ? void 0 : _a.find(x => !!x.DateTime)) === null || _b === void 0 ? void 0 : _b.DateTime;
31362
- const dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
31422
+ let dateTimeStr = (_b = (_a = entity.Bruce.Outline) === null || _a === void 0 ? void 0 : _a.find(x => !!x.DateTime)) === null || _b === void 0 ? void 0 : _b.DateTime;
31423
+ const isHistoric = Boolean(dateTimeStr);
31424
+ let dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
31425
+ // Initial animation check to see if we're continuing an animation.
31426
+ // This requires historic data, otherwise we continue on and later animate a before vs after movement based on the rendered data.
31363
31427
  if (cEntity.position && cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"]) {
31364
31428
  animatePosition = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"];
31365
31429
  const animateSeries = animatePosition;
31366
31430
  const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
31431
+ // Ensure the time mode matches the current data situation.
31432
+ // Historic/timestamped data should use viewer time; plugin-style direct updates should use real time.
31433
+ const shouldUseRealTime = !dateTime && !series.length;
31434
+ animateSeries.SetUseRealTime(shouldUseRealTime);
31367
31435
  if (series.length) {
31368
31436
  animateSeries.SupplementSeries(series);
31369
31437
  }
31370
31438
  if (dateTime) {
31371
31439
  animateSeries.UpdatePositionForDateTime(pos3d, dateTime, !EnsureNumber(transform === null || transform === void 0 ? void 0 : transform.heading) ? null : heading);
31372
31440
  }
31373
- // We don't have a date-stamped incoming position.
31374
- if (!dateTime && !series.length) {
31375
- animatePosition = null;
31441
+ // We don't have a date-stamped incoming position (e.g., plugin-style direct updates).
31442
+ // Add the new position to the existing series so the fixed-duration animation can handle it.
31443
+ if (shouldUseRealTime) {
31444
+ const posChanged = !prevPos3d || !Cartesian3.equals(prevPos3d, pos3d);
31445
+ if (posChanged && pos3d) {
31446
+ animateSeries.AddPosition({
31447
+ pos3d: pos3d,
31448
+ dateTime: new Date(),
31449
+ heading: !EnsureNumber(transform === null || transform === void 0 ? void 0 : transform.heading) ? null : heading
31450
+ });
31451
+ }
31376
31452
  }
31377
31453
  }
31454
+ // Now that main animation check is passed we'll ensure a date does exist so any further usage knows when 'now' is.
31455
+ if (!dateTimeStr) {
31456
+ dateTimeStr = entity.Bruce.Updated || entity.Bruce.Created;
31457
+ }
31458
+ dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
31459
+ if (!dateTime) {
31460
+ dateTime = new Date();
31461
+ }
31378
31462
  if (!animatePosition) {
31379
31463
  // If we've loaded a set of series positions then we'll animate through them.
31380
31464
  const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
@@ -31392,25 +31476,31 @@ var EntityRenderEngineModel3d;
31392
31476
  const posChanged = !prevPos3d || !Cartesian3.equals(prevPos3d, pos3d);
31393
31477
  if (posChanged) {
31394
31478
  let posses = [];
31395
- const isLive = ViewUtils.GetTimeDetails({ viewer: params.viewer }).isLive;
31479
+ // On true to allow for animation through direct plugin changes.
31480
+ // Will see if there are side-effects and make it more elaborate if needed.
31481
+ const isLive = true || ViewUtils.GetTimeDetails({ viewer: params.viewer }).isLive;
31482
+ // Just using real time for animation to avoid it teleporting on first movement.
31483
+ // If the prior update was too long ago our velocity estimate will make it teleport.
31484
+ const animStartTime = new Date();
31396
31485
  if (prevPos3d && isLive) {
31397
31486
  posses.push({
31398
31487
  pos3d: prevPos3d,
31399
- // Guess so that we can determine a direction of movement right away :)
31400
- dateTime: new Date(dateTime.getTime() - 1000),
31488
+ dateTime: animStartTime,
31401
31489
  heading: !EnsureNumber(transform === null || transform === void 0 ? void 0 : transform.heading) ? null : heading
31402
31490
  });
31403
31491
  }
31404
31492
  posses.push({
31405
31493
  pos3d: pos3d,
31406
- dateTime: dateTime,
31494
+ // Target time is in the future so the series interpolates forward.
31495
+ dateTime: new Date(animStartTime.getTime() + 1000),
31407
31496
  heading: !EnsureNumber(transform === null || transform === void 0 ? void 0 : transform.heading) ? null : heading
31408
31497
  });
31409
31498
  animatePosition = new CesiumAnimatedProperty.AnimatePositionSeries({
31410
31499
  posses: posses,
31411
31500
  viewer: params.viewer,
31412
31501
  pitch: pitch,
31413
- roll: roll
31502
+ roll: roll,
31503
+ useRealTime: true
31414
31504
  });
31415
31505
  cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
31416
31506
  cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
@@ -31418,7 +31508,14 @@ var EntityRenderEngineModel3d;
31418
31508
  }
31419
31509
  }
31420
31510
  // If we're animating position then we can animate orientation too based on the interpolated position.
31421
- if (dateTime && animatePosition && animatePosition instanceof CesiumAnimatedProperty.AnimatePositionSeries && animatePosition.GetOrient) {
31511
+ // We avoid interpolating it when there is no historic data AND the created/updated dates have changed.
31512
+ // This is to avoid interpolating towards a just-saved Entity after the user moves it.
31513
+ const prevUpdatedTime = cEntity.model._lastUpdatedTime;
31514
+ const curUpdatedTime = entity.Bruce.Updated || entity.Bruce.Created;
31515
+ const updatedTimeChanged = prevUpdatedTime && curUpdatedTime && prevUpdatedTime !== curUpdatedTime;
31516
+ const hasHistoricSeries = isHistoric || (params.entityHistoric && params.entityHistoric.length > 0);
31517
+ const shouldInterpolateHeading = !updatedTimeChanged || hasHistoricSeries;
31518
+ if (shouldInterpolateHeading && dateTime && animatePosition && animatePosition instanceof CesiumAnimatedProperty.AnimatePositionSeries && animatePosition.GetOrient) {
31422
31519
  cEntity.orientation = new CallbackProperty(() => {
31423
31520
  return animatePosition.GetOrient();
31424
31521
  }, false);
@@ -31560,6 +31657,8 @@ var EntityRenderEngineModel3d;
31560
31657
  style: styleScale
31561
31658
  };
31562
31659
  model._heading = heading;
31660
+ model._lastPos3d = pos3d;
31661
+ model._lastUpdatedTime = entity.Bruce.Updated || entity.Bruce.Created;
31563
31662
  // Generate a polyline 'track' for the historic data.
31564
31663
  // We do this for historic data that exists and is moving.
31565
31664
  if (shouldShowTrack && animatePosition && animatePosition instanceof CesiumAnimatedProperty.AnimatePositionSeries && animatePosition.GetSeries) {
@@ -34716,7 +34815,7 @@ class WidgetViewBar extends Widget.AWidget {
34716
34815
  }
34717
34816
  }
34718
34817
 
34719
- const VERSION = "6.4.0";
34818
+ const VERSION = "6.4.2";
34720
34819
  /**
34721
34820
  * Updates the environment instance used by bruce-cesium to one specified.
34722
34821
  * This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.