bruce-cesium 5.9.5 → 5.9.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
- import { BruceEvent, Cartes, Entity as Entity$1, ProjectViewTile, Carto, Geometry, MathUtils, LRUCache, Api, Calculator, ClientFile, EntityTag, EntityType, ObjectUtils, Style, DelayQueue, EntityLod, Bounds, ZoomControl, EntityRelationType, ENVIRONMENT, EntityHistoricData, Tileset, EntityCoords, DataLab, EntitySource, MenuItem, EntityRelation, ProgramKey, ProjectView, ProjectViewBookmark, ProjectViewLegacyTile, Camera, EntityAttachment, EntityAttachmentType, EntityAttribute, Session, AbstractApi } from 'bruce-models';
1
+ import { BruceEvent, Cartes, Entity as Entity$1, ProjectViewTile, Carto, Geometry, MathUtils, LRUCache, Api, Calculator, ClientFile, EntityTag, EntityType, ObjectUtils, Style, DelayQueue, EntityLod, Bounds, ZoomControl, EntityRelationType, ENVIRONMENT, EntityHistoricData, Tileset, EntityCoords, DataLab, EntitySource, MenuItem, EntityRelation, ProgramKey, ProjectView, ProjectViewBookmark, Camera, ProjectViewLegacyTile, EntityAttachment, EntityAttachmentType, EntityAttribute, AbstractApi, Session } from 'bruce-models';
2
2
  import * as Cesium from 'cesium';
3
- import { Cartographic, Cartesian2, Math as Math$1, Cartesian3, CallbackProperty, Color, HeightReference, Rectangle, JulianDate, Entity, DistanceDisplayCondition, ClassificationType, ArcType, CornerType, ShadowMode, ConstantProperty, ConstantPositionProperty, PolygonHierarchy, PolylineGraphics, ColorMaterialProperty, HorizontalOrigin, VerticalOrigin, ColorBlendMode, HeadingPitchRoll, Transforms, Model, SceneMode, Primitive, Cesium3DTileFeature, GeoJsonDataSource, Cesium3DTileStyle, HeadingPitchRange, Cesium3DTileColorBlendMode, Ion, KmlDataSource, SceneTransforms, Quaternion, Matrix3, Matrix4, Cesium3DTileset, IonResource, EllipsoidTerrainProvider, CesiumInspector, OrthographicFrustum, defined, ClockRange, PolygonPipeline, EllipsoidGeodesic, sampleTerrainMostDetailed, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, EasingFunction, NearFarScalar, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, ScreenSpaceEventHandler, ScreenSpaceEventType, BoundingSphere, GeometryInstance, CzmlDataSource, Intersect, Fullscreen } from 'cesium';
3
+ import { Cartographic, Cartesian2, Math as Math$1, Cartesian3, CallbackProperty, Color, HeightReference, Rectangle, JulianDate, Entity, DistanceDisplayCondition, HorizontalOrigin, VerticalOrigin, ConstantProperty, ClassificationType, ConstantPositionProperty, ArcType, CornerType, ShadowMode, PolygonHierarchy, PolylineGraphics, ColorMaterialProperty, ColorBlendMode, HeadingPitchRoll, Transforms, Model, SceneMode, Primitive, Cesium3DTileFeature, GeoJsonDataSource, Cesium3DTileStyle, Cesium3DTileColorBlendMode, HeadingPitchRange, Ion, KmlDataSource, Quaternion, Matrix3, Matrix4, SceneTransforms, OrthographicFrustum, EasingFunction, NearFarScalar, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, IonResource, CesiumInspector, defined, ClockRange, EllipsoidGeodesic, sampleTerrainMostDetailed, Cesium3DTileset, PolygonPipeline, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, BoundingSphere, GeometryInstance, ScreenSpaceEventHandler, ScreenSpaceEventType, CzmlDataSource, Intersect, Fullscreen } from 'cesium';
4
4
 
5
5
  const TIME_LAG = 300;
6
6
  const POSITION_CHECK_TIMER = 950;
@@ -1144,48 +1144,70 @@ var CesiumAnimatedProperty;
1144
1144
  */
1145
1145
  class AnimatePositionSeries {
1146
1146
  constructor(params) {
1147
- // Current position state.
1148
- this.lastCalcPos3d = null;
1149
- this.lastCalcPosIndexLast = -1;
1150
- this.lastCalcPosIndexNext = -1;
1151
- // Series data for rendering path.
1147
+ // Animation state.
1148
+ this.currentAnimatedPos = null;
1149
+ this.currentTargetPos = null;
1150
+ this.animationStartPos = null;
1151
+ this.animationStartTime = null;
1152
+ this.animationDuration = 200;
1153
+ // Cached data for performance.
1154
+ this.lastDesiredPosIndex = -1;
1155
+ this.lastDesiredNextIndex = -1;
1156
+ // Series data for rendering path
1152
1157
  this.lastCalcSeriesPos3d = [];
1153
1158
  this.lastCalcSeriesTime = null;
1154
- // Orientation.
1159
+ // Orientation cache.
1155
1160
  this.lastCalcOrient = null;
1156
1161
  this.lastCalcOrientTime = null;
1157
- // Starting animation state.
1158
- this.animateFromPos3d = null;
1159
- this.animateFromPos3dTimeStart = null;
1160
- this.animationDuration = 200; // milliseconds
1161
1162
  this.viewer = params.viewer;
1162
1163
  this.positions = params.posses || [];
1163
1164
  this.pitch = params.pitch || 0;
1164
1165
  this.roll = params.roll || 0;
1165
- // Animation from a starting position.
1166
+ this.processHeadings();
1167
+ this.sortPositions();
1168
+ // Initialize animation from starting position if provided.
1166
1169
  if (params.animateFromPos3d) {
1167
- this.animateFromPos3d = params.animateFromPos3d;
1170
+ this.animationStartPos = params.animateFromPos3d;
1168
1171
  const currentTime = Date.now();
1169
1172
  const providedTime = params.animateFromPos3dTimeStart || 0;
1170
- // Check if the provided timestamp is stale (animation would have already completed)
1171
- // If so, use current time to restart the animation.
1173
+ // Check if the provided timestamp is stale.
1172
1174
  if (!providedTime || (currentTime - providedTime) >= this.animationDuration) {
1173
- this.animateFromPos3dTimeStart = currentTime;
1175
+ this.animationStartTime = currentTime;
1174
1176
  }
1175
1177
  else {
1176
- this.animateFromPos3dTimeStart = providedTime;
1178
+ this.animationStartTime = providedTime;
1177
1179
  }
1178
1180
  }
1181
+ }
1182
+ AddPosition(pos) {
1183
+ if (!pos || !pos.pos3d || !pos.dateTime) {
1184
+ console.warn("Invalid position provided to AnimatePositionSeries.");
1185
+ return;
1186
+ }
1187
+ this.positions.push(pos);
1179
1188
  this.processHeadings();
1180
- // Order positions by date.
1189
+ this.sortPositions();
1190
+ this.invalidateCache();
1191
+ }
1192
+ UpdatePitchRoll(pitch, roll) {
1193
+ this.pitch = pitch;
1194
+ this.roll = roll;
1195
+ this.invalidateCache();
1196
+ }
1197
+ GetAnimateFromDateTime() {
1198
+ return this.animationStartTime;
1199
+ }
1200
+ GetPositions() {
1201
+ return this.positions;
1202
+ }
1203
+ sortPositions() {
1181
1204
  if (this.positions.length > 0) {
1182
- this.positions.sort((a, b) => {
1183
- return a.dateTime.getTime() - b.dateTime.getTime();
1184
- });
1205
+ this.positions.sort((a, b) => a.dateTime.getTime() - b.dateTime.getTime());
1185
1206
  }
1186
1207
  }
1187
- GetAnimateFromDateTime() {
1188
- return this.animateFromPos3dTimeStart;
1208
+ invalidateCache() {
1209
+ this.lastCalcSeriesTime = null;
1210
+ this.lastCalcOrientTime = null;
1189
1211
  }
1190
1212
  /**
1191
1213
  * Pre-process headings in the series.
@@ -1209,33 +1231,91 @@ var CesiumAnimatedProperty;
1209
1231
  }
1210
1232
  }
1211
1233
  }
1212
- GetPositions() {
1213
- return this.positions;
1214
- }
1215
1234
  easeInOutQuad(t) {
1216
1235
  return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
1217
1236
  }
1218
1237
  /**
1219
- * Interpolates between the starting position and target position.
1238
+ * Calculate the desired position based on current time without any animation smoothing.
1220
1239
  */
1221
- interpolateToTargetPosition(startPos, targetPos, startTimeMs, currentTimeMs) {
1222
- if (!startPos || !targetPos) {
1223
- return startPos || targetPos || new Cartesian3();
1240
+ calculateDesiredPosition(timeMs) {
1241
+ // No positions available
1242
+ if (!this.positions || this.positions.length === 0) {
1243
+ return { position: null, lastIndex: -1, nextIndex: -1 };
1224
1244
  }
1225
- const elapsedTime = currentTimeMs - startTimeMs;
1226
- let progress = Math.min(elapsedTime / this.animationDuration, 1.0);
1227
- // Apply easing for more natural motion.
1228
- progress = this.easeInOutQuad(progress);
1229
- try {
1230
- return Cartesian3.lerp(startPos, targetPos, progress, new Cartesian3());
1245
+ // Only one position.
1246
+ if (this.positions.length === 1) {
1247
+ return {
1248
+ position: this.positions[0].pos3d || null,
1249
+ lastIndex: 0,
1250
+ nextIndex: 0
1251
+ };
1231
1252
  }
1232
- catch (e) {
1233
- console.error("Error in position interpolation: ", e);
1234
- return targetPos;
1253
+ // Before first position - use first two positions for orientation.
1254
+ if (timeMs <= this.positions[0].dateTime.getTime()) {
1255
+ return {
1256
+ position: this.positions[0].pos3d || null,
1257
+ lastIndex: 0,
1258
+ nextIndex: Math.min(1, this.positions.length - 1)
1259
+ };
1260
+ }
1261
+ // After last position - use last two positions for orientation.
1262
+ const lastIdx = this.positions.length - 1;
1263
+ if (timeMs >= this.positions[lastIdx].dateTime.getTime()) {
1264
+ return {
1265
+ position: this.positions[lastIdx].pos3d || null,
1266
+ lastIndex: Math.max(0, lastIdx - 1),
1267
+ nextIndex: lastIdx
1268
+ };
1269
+ }
1270
+ // Find positions to interpolate between.
1271
+ for (let i = 0; i < this.positions.length - 1; i++) {
1272
+ const current = this.positions[i];
1273
+ const next = this.positions[i + 1];
1274
+ if (timeMs >= current.dateTime.getTime() && timeMs < next.dateTime.getTime()) {
1275
+ // Exact match on current position - still use current and next for orientation.
1276
+ if (timeMs === current.dateTime.getTime()) {
1277
+ return {
1278
+ position: current.pos3d || null,
1279
+ lastIndex: i,
1280
+ nextIndex: i + 1
1281
+ };
1282
+ }
1283
+ // Interpolate between current and next.
1284
+ if (!current.pos3d || !next.pos3d) {
1285
+ return {
1286
+ position: current.pos3d || next.pos3d || null,
1287
+ lastIndex: i,
1288
+ nextIndex: i + 1
1289
+ };
1290
+ }
1291
+ try {
1292
+ const progress = (timeMs - current.dateTime.getTime()) / (next.dateTime.getTime() - current.dateTime.getTime());
1293
+ const interpolatedPos = Cartesian3.lerp(current.pos3d, next.pos3d, progress, new Cartesian3());
1294
+ return {
1295
+ position: interpolatedPos,
1296
+ lastIndex: i,
1297
+ nextIndex: i + 1
1298
+ };
1299
+ }
1300
+ catch (e) {
1301
+ console.error("Error in position interpolation: ", e);
1302
+ return {
1303
+ position: current.pos3d,
1304
+ lastIndex: i,
1305
+ nextIndex: i + 1
1306
+ };
1307
+ }
1308
+ }
1235
1309
  }
1310
+ // Fallback to last position with previous position for orientation.
1311
+ return {
1312
+ position: this.positions[lastIdx].pos3d || null,
1313
+ lastIndex: Math.max(0, lastIdx - 1),
1314
+ nextIndex: lastIdx
1315
+ };
1236
1316
  }
1237
1317
  /**
1238
- * Main method to get the current position based on time
1318
+ * Main method to get the current position based on time.
1239
1319
  */
1240
1320
  GetValue() {
1241
1321
  let now = this.viewer.scene.lastRenderTime;
@@ -1244,95 +1324,79 @@ var CesiumAnimatedProperty;
1244
1324
  }
1245
1325
  const nowTimeMs = JulianDate.toDate(now).getTime();
1246
1326
  const currentRealTimeMs = Date.now();
1247
- // Calculate the ideal position based on time.
1248
- const idealPosition = this.calculateIdealPosition(nowTimeMs);
1249
- // First run initialization.
1250
- if (!this.lastCalcPos3d) {
1251
- this.lastCalcPos3d = idealPosition;
1252
- // If we have a starting position, animate from it.
1253
- if (this.animateFromPos3d) {
1254
- return this.interpolateToTargetPosition(this.animateFromPos3d, idealPosition, this.animateFromPos3dTimeStart, currentRealTimeMs);
1255
- }
1256
- return idealPosition;
1257
- }
1258
- // If we're animating from a start position.
1259
- if (this.animateFromPos3d) {
1260
- const elapsedTime = currentRealTimeMs - this.animateFromPos3dTimeStart;
1261
- // If animation is complete, just return ideal position and clear animation state.
1262
- if (elapsedTime >= this.animationDuration) {
1263
- this.animateFromPos3d = null;
1264
- this.lastCalcPos3d = idealPosition;
1265
- return idealPosition;
1266
- }
1267
- // Otherwise continue interpolation.
1268
- const interpolatedPos = this.interpolateToTargetPosition(this.animateFromPos3d, idealPosition, this.animateFromPos3dTimeStart, currentRealTimeMs);
1269
- this.lastCalcPos3d = interpolatedPos;
1270
- return interpolatedPos;
1271
- }
1272
- // If we're not animating, directly use the ideal position.
1273
- // This ensures we're exactly where we should be based on time.
1274
- this.lastCalcPos3d = idealPosition;
1275
- return idealPosition;
1276
- }
1277
- /**
1278
- * Calculate the ideal position based on current time without any smoothing.
1279
- */
1280
- calculateIdealPosition(timeMs) {
1281
- if (!this.positions || this.positions.length === 0) {
1327
+ // Calculate the desired position based on time.
1328
+ const desired = this.calculateDesiredPosition(nowTimeMs);
1329
+ // If no desired position, return empty position.
1330
+ if (!desired.position) {
1331
+ this.currentAnimatedPos = null;
1332
+ this.currentTargetPos = null;
1282
1333
  return new Cartesian3();
1283
1334
  }
1284
- // Before first position.
1285
- if (timeMs <= this.positions[0].dateTime.getTime()) {
1286
- this.lastCalcPosIndexLast = 0;
1287
- this.lastCalcPosIndexNext = 0;
1288
- return this.positions[0].pos3d || new Cartesian3();
1289
- }
1290
- // After last position.
1291
- if (timeMs >= this.positions[this.positions.length - 1].dateTime.getTime()) {
1292
- const lastIndex = this.positions.length - 1;
1293
- this.lastCalcPosIndexLast = lastIndex;
1294
- this.lastCalcPosIndexNext = lastIndex;
1295
- return this.positions[lastIndex].pos3d || new Cartesian3();
1296
- }
1297
- // Find the position before and after the current time.
1298
- let lastIndex = 0;
1299
- for (let i = 0; i < this.positions.length - 1; i++) {
1300
- if (timeMs >= this.positions[i].dateTime.getTime() &&
1301
- timeMs < this.positions[i + 1].dateTime.getTime()) {
1302
- lastIndex = i;
1303
- break;
1335
+ // Cache the desired position info for orientation calculations.
1336
+ this.lastDesiredPosIndex = desired.lastIndex;
1337
+ this.lastDesiredNextIndex = desired.nextIndex;
1338
+ // First time or no previous position - start here.
1339
+ if (!this.currentAnimatedPos) {
1340
+ // If we have a starting animation position, animate from it.
1341
+ if (this.animationStartPos) {
1342
+ this.currentTargetPos = desired.position;
1343
+ const progress = this.getAnimationProgress(currentRealTimeMs, this.animationStartTime);
1344
+ if (progress >= 1.0) {
1345
+ // Animation complete.
1346
+ this.currentAnimatedPos = desired.position;
1347
+ this.animationStartPos = null;
1348
+ this.animationStartTime = null;
1349
+ return desired.position;
1350
+ }
1351
+ else {
1352
+ // Continue animation.
1353
+ const easedProgress = this.easeInOutQuad(progress);
1354
+ this.currentAnimatedPos = Cartesian3.lerp(this.animationStartPos, desired.position, easedProgress, new Cartesian3());
1355
+ return this.currentAnimatedPos;
1356
+ }
1304
1357
  }
1305
- }
1306
- const last = this.positions[lastIndex];
1307
- this.lastCalcPosIndexLast = lastIndex;
1308
- // If we're exactly at this position's time.
1309
- if (timeMs === last.dateTime.getTime()) {
1310
- this.lastCalcPosIndexNext = lastIndex;
1311
- return last.pos3d || new Cartesian3();
1312
- }
1313
- // Interpolate between surrounding positions.
1314
- const next = this.positions[lastIndex + 1];
1315
- if (!next) {
1316
- this.lastCalcPosIndexNext = lastIndex;
1317
- return last.pos3d || new Cartesian3();
1318
- }
1319
- this.lastCalcPosIndexNext = lastIndex + 1;
1320
- try {
1321
- // Ensure both position vectors are valid.
1322
- if (!last.pos3d || !next.pos3d) {
1323
- return last.pos3d || next.pos3d || new Cartesian3();
1358
+ else {
1359
+ // No animation, start at desired position.
1360
+ this.currentAnimatedPos = desired.position;
1361
+ this.currentTargetPos = desired.position;
1362
+ return desired.position;
1324
1363
  }
1325
- const progress = (timeMs - last.dateTime.getTime()) /
1326
- (next.dateTime.getTime() - last.dateTime.getTime());
1327
- return Cartesian3.lerp(last.pos3d, next.pos3d, progress, new Cartesian3());
1328
1364
  }
1329
- catch (e) {
1330
- console.error("Error in ideal position calculation: ", e);
1331
- return last.pos3d || new Cartesian3();
1365
+ // Check if target has changed.
1366
+ const targetChanged = !this.currentTargetPos || !Cartesian3.equals(this.currentTargetPos, desired.position);
1367
+ if (targetChanged) {
1368
+ // Target changed mid-animation - start new animation from current position.
1369
+ this.animationStartPos = this.currentAnimatedPos;
1370
+ this.animationStartTime = currentRealTimeMs;
1371
+ this.currentTargetPos = desired.position;
1372
+ }
1373
+ // Continue or start animation to target.
1374
+ if (this.animationStartPos && this.animationStartTime) {
1375
+ const progress = this.getAnimationProgress(currentRealTimeMs, this.animationStartTime);
1376
+ // Animation complete.
1377
+ if (progress >= 1.0) {
1378
+ this.currentAnimatedPos = desired.position;
1379
+ this.animationStartPos = null;
1380
+ this.animationStartTime = null;
1381
+ return desired.position;
1382
+ }
1383
+ // Continue animation.
1384
+ else {
1385
+ const easedProgress = this.easeInOutQuad(progress);
1386
+ this.currentAnimatedPos = Cartesian3.lerp(this.animationStartPos, desired.position, easedProgress, new Cartesian3());
1387
+ return this.currentAnimatedPos;
1388
+ }
1332
1389
  }
1390
+ // No animation needed - already at target.
1391
+ this.currentAnimatedPos = desired.position;
1392
+ return desired.position;
1393
+ }
1394
+ getAnimationProgress(currentTime, startTime) {
1395
+ const elapsed = currentTime - startTime;
1396
+ return Math.min(elapsed / this.animationDuration, 1.0);
1333
1397
  }
1334
1398
  /**
1335
- * Returns a series of positions to use for rendering the path..
1399
+ * Returns a series of positions to use for rendering the path.
1336
1400
  */
1337
1401
  GetSeries() {
1338
1402
  // Update at 30 fps.
@@ -1370,8 +1434,8 @@ var CesiumAnimatedProperty;
1370
1434
  let add = nowDate >= new Date(pos.dateTime.getTime() - visibilityDuration / 2) &&
1371
1435
  nowDate <= new Date(pos.dateTime.getTime() + visibilityDuration / 2);
1372
1436
  // Also include the segment we're currently traversing.
1373
- if (!add && this.lastCalcPosIndexLast > -1 && this.lastCalcPosIndexNext > -1) {
1374
- add = i >= this.lastCalcPosIndexLast && i <= this.lastCalcPosIndexNext;
1437
+ if (!add && this.lastDesiredPosIndex > -1 && this.lastDesiredNextIndex > -1) {
1438
+ add = i >= this.lastDesiredPosIndex && i <= this.lastDesiredNextIndex;
1375
1439
  }
1376
1440
  if (add) {
1377
1441
  newPosses.push(pos.pos3d);
@@ -1409,10 +1473,10 @@ var CesiumAnimatedProperty;
1409
1473
  }
1410
1474
  const nowTime = JulianDate.toDate(now).getTime();
1411
1475
  try {
1412
- // Get last and next position indices.
1413
- const lastIndex = this.lastCalcPosIndexLast;
1414
- const nextIndex = this.lastCalcPosIndexNext;
1415
- // Validate indices.
1476
+ // Get current position indices.
1477
+ const lastIndex = this.lastDesiredPosIndex;
1478
+ const nextIndex = this.lastDesiredNextIndex;
1479
+ // Invalid indices.
1416
1480
  if (lastIndex < 0 || nextIndex < 0 ||
1417
1481
  lastIndex >= this.positions.length || nextIndex >= this.positions.length) {
1418
1482
  return this.lastCalcOrient || defaultQuaternion;
@@ -1422,6 +1486,17 @@ var CesiumAnimatedProperty;
1422
1486
  if (!lastPos || !nextPos) {
1423
1487
  return this.lastCalcOrient || defaultQuaternion;
1424
1488
  }
1489
+ // Single position case - use its heading if available.
1490
+ if (lastIndex === nextIndex) {
1491
+ if (lastPos.heading !== null) {
1492
+ this.lastCalcOrient = Transforms.headingPitchRollQuaternion(currentPosition, new HeadingPitchRoll(Math$1.toRadians(lastPos.heading), Math$1.toRadians(this.pitch), Math$1.toRadians(this.roll)));
1493
+ this.lastCalcOrientTime = Date.now();
1494
+ return this.lastCalcOrient;
1495
+ }
1496
+ // No heading data and single position, return previous or default.
1497
+ return this.lastCalcOrient || defaultQuaternion;
1498
+ }
1499
+ // Two different positions - we can calculate orientation.
1425
1500
  // Use explicit heading values if available.
1426
1501
  if (lastPos.heading !== null && nextPos.heading !== null) {
1427
1502
  // Calculate interpolated heading.
@@ -1435,8 +1510,7 @@ var CesiumAnimatedProperty;
1435
1510
  }
1436
1511
  // Calculate interpolation factor.
1437
1512
  let factor = 0;
1438
- if (lastIndex !== nextIndex &&
1439
- lastPos.dateTime.getTime() !== nextPos.dateTime.getTime()) {
1513
+ if (lastPos.dateTime.getTime() !== nextPos.dateTime.getTime()) {
1440
1514
  factor = (nowTime - lastPos.dateTime.getTime()) /
1441
1515
  (nextPos.dateTime.getTime() - lastPos.dateTime.getTime());
1442
1516
  factor = Math.max(0, Math.min(1, factor));
@@ -1449,7 +1523,7 @@ var CesiumAnimatedProperty;
1449
1523
  // Create quaternion from heading, pitch, roll.
1450
1524
  this.lastCalcOrient = Transforms.headingPitchRollQuaternion(currentPosition, new HeadingPitchRoll(Math$1.toRadians(interpolatedHeading), Math$1.toRadians(this.pitch), Math$1.toRadians(this.roll)));
1451
1525
  }
1452
- // Calculate heading from position changes if not available.
1526
+ // Calculate heading from position changes if heading data not available.
1453
1527
  else {
1454
1528
  if (!lastPos.pos3d || !nextPos.pos3d) {
1455
1529
  return this.lastCalcOrient || defaultQuaternion;
@@ -6842,7 +6916,9 @@ function removeEntities(viewer, entities) {
6842
6916
  continue;
6843
6917
  }
6844
6918
  entityRemoveAddState.delete(entity.id);
6845
- viewer.entities.remove(entity);
6919
+ if (viewer.entities.contains(entity)) {
6920
+ viewer.entities.remove(entity);
6921
+ }
6846
6922
  }
6847
6923
  });
6848
6924
  }
@@ -6864,7 +6940,9 @@ function addEntities(viewer, entities) {
6864
6940
  continue;
6865
6941
  }
6866
6942
  entityRemoveAddState.delete(entity.id);
6867
- viewer.entities.add(entity);
6943
+ if (!viewer.entities.contains(entity)) {
6944
+ viewer.entities.add(entity);
6945
+ }
6868
6946
  }
6869
6947
  });
6870
6948
  }
@@ -6971,7 +7049,7 @@ var EntityRenderEngineModel3d;
6971
7049
  * @returns
6972
7050
  */
6973
7051
  function Render(params) {
6974
- var _a, _b;
7052
+ var _a, _b, _c, _d;
6975
7053
  const entity = params.entity;
6976
7054
  if (!params.entityHistoric) {
6977
7055
  params.entityHistoric = [];
@@ -7073,6 +7151,7 @@ var EntityRenderEngineModel3d;
7073
7151
  }
7074
7152
  };
7075
7153
  let cEntity = params.rendered;
7154
+ let prevPos3d = cEntity ? GetCValue(params.viewer, cEntity.position) : null;
7076
7155
  if (!cEntity || !cEntity.model) {
7077
7156
  updateShouldShowTrack();
7078
7157
  if (!color) {
@@ -7162,12 +7241,11 @@ var EntityRenderEngineModel3d;
7162
7241
  cEntity.model.colorBlendAmount = new ConstantProperty(blendAmount);
7163
7242
  cEntity.model.colorBlendMode = new ConstantProperty(blendMode);
7164
7243
  cEntity.model.distanceDisplayCondition = new ConstantProperty(EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance));
7165
- const prevPos3d = GetCValue(params.viewer, cEntity.position);
7166
7244
  let prevStartTime = null;
7167
7245
  if (cEntity.position && cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"]) {
7168
7246
  prevStartTime = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"].GetAnimateFromDateTime();
7169
7247
  }
7170
- // If we have a series of time-based positions then we'll animate as time changes.
7248
+ // If we've loaded a set of series positions then we'll animate through them.
7171
7249
  const series = CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(params.viewer, heightRef, heightRef, params.entityHistoric);
7172
7250
  if (series.length > 1) {
7173
7251
  animatePosition = new CesiumAnimatedProperty.AnimatePositionSeries({
@@ -7182,17 +7260,50 @@ var EntityRenderEngineModel3d;
7182
7260
  cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
7183
7261
  }
7184
7262
  else {
7263
+ 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;
7264
+ const dateTime = dateTimeStr ? new Date(dateTimeStr) : null;
7185
7265
  const posChanged = !prevPos3d || !Cartesian3.equals(prevPos3d, pos3d);
7186
7266
  if (posChanged) {
7187
- animatePosition = new CesiumAnimatedProperty.AnimatePosition({
7188
- durationMs: 200,
7189
- targetPos3d: pos3d,
7190
- viewer: params.viewer,
7191
- startPos3d: prevPos3d
7192
- });
7193
- cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
7267
+ if (cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"]) {
7268
+ const prevAnimatePosition = cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"];
7269
+ animatePosition = prevAnimatePosition;
7270
+ animatePosition.AddPosition({
7271
+ pos3d: pos3d,
7272
+ dateTime: dateTime,
7273
+ heading: !EnsureNumber(transform === null || transform === void 0 ? void 0 : transform.heading) ? null : heading
7274
+ });
7275
+ animatePosition.UpdatePitchRoll(pitch, roll);
7276
+ }
7277
+ else {
7278
+ let posses = [];
7279
+ const isLive = ViewUtils.GetTimeDetails({ viewer: params.viewer }).isLive;
7280
+ if (prevPos3d && isLive) {
7281
+ posses.push({
7282
+ pos3d: prevPos3d,
7283
+ // Guess so that we can determine a direction of movement right away :)
7284
+ dateTime: new Date(dateTime.getTime() - 1000),
7285
+ heading: !EnsureNumber(transform === null || transform === void 0 ? void 0 : transform.heading) ? null : heading
7286
+ });
7287
+ }
7288
+ posses.push({
7289
+ pos3d: pos3d,
7290
+ dateTime: dateTime,
7291
+ heading: !EnsureNumber(transform === null || transform === void 0 ? void 0 : transform.heading) ? null : heading
7292
+ });
7293
+ animatePosition = new CesiumAnimatedProperty.AnimatePositionSeries({
7294
+ posses: posses,
7295
+ viewer: params.viewer,
7296
+ pitch: pitch,
7297
+ roll: roll,
7298
+ animateFromPos3d: prevPos3d && !isLive ? prevPos3d : null,
7299
+ animateFromPos3dTimeStart: null
7300
+ });
7301
+ cEntity.position = new CallbackProperty(() => animatePosition.GetValue(), false);
7302
+ cEntity.position["CesiumAnimatedProperty.AnimatePositionSeries"] = animatePosition;
7303
+ }
7194
7304
  }
7195
7305
  }
7306
+ // If we're animating position then we can animate orientation too based on the interpolated position.
7196
7307
  if (animatePosition && animatePosition instanceof CesiumAnimatedProperty.AnimatePositionSeries && animatePosition.GetOrient) {
7197
7308
  cEntity.orientation = new CallbackProperty(() => {
7198
7309
  return animatePosition.GetOrient();
@@ -7200,15 +7311,16 @@ var EntityRenderEngineModel3d;
7200
7311
  }
7201
7312
  else {
7202
7313
  const prevHeading = cEntity.model._heading;
7314
+ // Not worth animating.
7203
7315
  if (prevHeading == null || prevHeading == heading || isNaN(prevHeading)) {
7204
7316
  const hpr = new HeadingPitchRoll(Math$1.toRadians(heading), Math$1.toRadians(pitch), Math$1.toRadians(roll));
7205
7317
  const orient = Transforms.headingPitchRollQuaternion(pos3d, hpr);
7206
7318
  cEntity.orientation = new CallbackProperty(() => orient, true);
7207
7319
  }
7208
- // Animate orientation. We'll calculate the heading based on movement.
7320
+ // Animate the change.
7209
7321
  else {
7210
7322
  const animateHeading = new CesiumAnimatedProperty.AnimateHeading({
7211
- durationMs: 200,
7323
+ durationMs: 500,
7212
7324
  targetHeading: heading,
7213
7325
  viewer: params.viewer,
7214
7326
  startHeading: prevHeading
@@ -7240,7 +7352,7 @@ var EntityRenderEngineModel3d;
7240
7352
  else {
7241
7353
  cEntity.model.scale = new CallbackProperty(() => scale * styleScale, true);
7242
7354
  }
7243
- // We'll use "SetDefaultColor" to updating the internal reference and to allow for an animation.
7355
+ // We'll use "SetDefaultColor" to update the internal reference and to allow for an animation.
7244
7356
  // cEntity.model.color = new Cesium.CallbackProperty(() => color, true);
7245
7357
  CesiumEntityStyler.SetDefaultColor({
7246
7358
  color: color ? color : new Color(),
@@ -7337,7 +7449,7 @@ var EntityRenderEngineModel3d;
7337
7449
  // Generate a polyline 'track' for the historic data.
7338
7450
  // We do this for historic data that exists and is moving.
7339
7451
  if (shouldShowTrack && animatePosition && animatePosition instanceof CesiumAnimatedProperty.AnimatePositionSeries && animatePosition.GetSeries) {
7340
- const lStyle = (_b = (_a = params.fullStyle) === null || _a === void 0 ? void 0 : _a.polylineStyle) !== null && _b !== void 0 ? _b : {};
7452
+ const lStyle = (_d = (_c = params.fullStyle) === null || _c === void 0 ? void 0 : _c.polylineStyle) !== null && _d !== void 0 ? _d : {};
7341
7453
  const bColor = lStyle.lineColor ? Calculator.GetColor(lStyle.lineColor, entity, params.tags) : null;
7342
7454
  const cColor = bColor ? ColorToCColor(bColor) : Color.fromCssColorString("rgba(255, 193, 7, 0.8)");
7343
7455
  let width = lStyle.lineWidth ? EnsureNumber(Calculator.GetNumber(lStyle.lineWidth, entity, params.tags)) : 2;
@@ -33131,7 +33243,7 @@ class WidgetViewBar extends Widget.AWidget {
33131
33243
  }
33132
33244
  }
33133
33245
 
33134
- const VERSION = "5.9.5";
33246
+ const VERSION = "5.9.6";
33135
33247
 
33136
33248
  export { VERSION, CesiumViewMonitor, ViewerUtils, ViewerEventTracker, MenuItemManager, isOutlineChanged, EntityRenderEngine, EntityRenderEnginePoint, EntityRenderEnginePolyline, EntityRenderEnginePolygon, EntityRenderEngineModel3d, MenuItemCreator, VisualsRegister, RenderManager, EntitiesIdsRenderManager, DataLabRenderManager, EntitiesLoadedRenderManager, EntitiesRenderManager, EntityRenderManager, TilesetCadRenderManager, TilesetArbRenderManager, TilesetEntitiesRenderManager, TilesetOsmRenderManager, TilesetPointcloudRenderManager, TilesetGooglePhotosRenderManager, DataSourceStaticKmlManager, GoogleSearchRenderManager, AssemblyRenderManager, RelationsRenderManager, SharedGetters, CesiumParabola, EntityLabel, ViewRenderEngine, TileRenderEngine, TilesetRenderEngine, CESIUM_INSPECTOR_KEY, CESIUM_TIMELINE_KEY, CESIUM_TIMELINE_LIVE_KEY, CESIUM_TIMELINE_LIVE_PADDING_KEY, CESIUM_TIMELINE_INTERVAL_KEY, DEFAULT_LIVE_PADDING_SECONDS, ViewUtils, DrawingUtils, MeasureUtils, EntityUtils, CesiumEntityStyler, CesiumAnimatedProperty, CesiumAnimatedInOut, Draw3dPolygon, Draw3dPolyline, MeasureCreator, Walkthrough, Widget, VIEWER_BOOKMARKS_WIDGET_KEY, WidgetBookmarks, WidgetBranding, WidgetCursorBar, WidgetEmbeddedInfoView, WidgetInfoView, WidgetNavCompass$$1 as WidgetNavCompass, VIEWER_VIEW_BAR_WIDGET_KEY, WidgetViewBar, WidgetControlViewBar, WidgetControlViewBarSearch, VIEWER_LEFT_PANEL_WIDGET_KEY, VIEWER_LEFT_PANEL_CSS_VAR_LEFT, WidgetLeftPanel, WidgetLeftPanelTab, WidgetLeftPanelTabBookmarks };
33137
33249
  //# sourceMappingURL=bruce-cesium.es5.js.map