bruce-cesium 7.6.0 → 7.6.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.
- package/dist/bruce-cesium.es5.js +2057 -348
- package/dist/bruce-cesium.es5.js.map +1 -1
- package/dist/bruce-cesium.umd.js +2050 -345
- package/dist/bruce-cesium.umd.js.map +1 -1
- package/dist/lib/bruce-cesium.js +2 -1
- package/dist/lib/bruce-cesium.js.map +1 -1
- package/dist/lib/rendering/cesium-animated-property.js +63 -107
- package/dist/lib/rendering/cesium-animated-property.js.map +1 -1
- package/dist/lib/rendering/entity-gatherer.js +11 -3
- package/dist/lib/rendering/entity-gatherer.js.map +1 -1
- package/dist/lib/rendering/menu-item-manager.js +83 -10
- package/dist/lib/rendering/menu-item-manager.js.map +1 -1
- package/dist/lib/rendering/relation-render-engine.js +24 -5
- package/dist/lib/rendering/relation-render-engine.js.map +1 -1
- package/dist/lib/rendering/render-managers/common/entity-label-group.js +13 -9
- package/dist/lib/rendering/render-managers/common/entity-label-group.js.map +1 -1
- package/dist/lib/rendering/render-managers/common/entity-label.js +94 -12
- package/dist/lib/rendering/render-managers/common/entity-label.js.map +1 -1
- package/dist/lib/rendering/render-managers/common/entity-rerender-maintain-state.js +44 -5
- package/dist/lib/rendering/render-managers/common/entity-rerender-maintain-state.js.map +1 -1
- package/dist/lib/rendering/render-managers/common/entity-simulation.js +483 -0
- package/dist/lib/rendering/render-managers/common/entity-simulation.js.map +1 -0
- package/dist/lib/rendering/render-managers/common/historic-trail.js +123 -0
- package/dist/lib/rendering/render-managers/common/historic-trail.js.map +1 -0
- package/dist/lib/rendering/render-managers/common/historic-utils.js +17 -6
- package/dist/lib/rendering/render-managers/common/historic-utils.js.map +1 -1
- package/dist/lib/rendering/render-managers/entities/entities-datalab-render-manager.js +3 -2
- package/dist/lib/rendering/render-managers/entities/entities-datalab-render-manager.js.map +1 -1
- package/dist/lib/rendering/render-managers/entities/entities-ids-render-manager.js +10 -2
- package/dist/lib/rendering/render-managers/entities/entities-ids-render-manager.js.map +1 -1
- package/dist/lib/rendering/render-managers/entities/entities-loaded-render-manager.js +3 -2
- package/dist/lib/rendering/render-managers/entities/entities-loaded-render-manager.js.map +1 -1
- package/dist/lib/rendering/render-managers/entities/entities-render-manager.js +18 -5
- package/dist/lib/rendering/render-managers/entities/entities-render-manager.js.map +1 -1
- package/dist/lib/rendering/render-managers/entities/entity-render-manager.js +1 -1
- package/dist/lib/rendering/render-managers/entities/entity-render-manager.js.map +1 -1
- package/dist/lib/rendering/render-managers/other/assembly-render-manager.js +745 -106
- package/dist/lib/rendering/render-managers/other/assembly-render-manager.js.map +1 -1
- package/dist/lib/rendering/render-managers/other/legacy-relations-render-manager.js +23 -1
- package/dist/lib/rendering/render-managers/other/legacy-relations-render-manager.js.map +1 -1
- package/dist/lib/rendering/render-managers/other/relations-render-manager.js +23 -1
- package/dist/lib/rendering/render-managers/other/relations-render-manager.js.map +1 -1
- package/dist/lib/rendering/render-managers/render-manager.js.map +1 -1
- package/dist/lib/rendering/render-managers/tilesets/tileset-arb-render-manager.js +2 -0
- package/dist/lib/rendering/render-managers/tilesets/tileset-arb-render-manager.js.map +1 -1
- package/dist/lib/rendering/render-managers/tilesets/tileset-cad-render-manager.js +130 -40
- package/dist/lib/rendering/render-managers/tilesets/tileset-cad-render-manager.js.map +1 -1
- package/dist/lib/rendering/render-managers/tilesets/tileset-entities-render-manager.js +3 -0
- package/dist/lib/rendering/render-managers/tilesets/tileset-entities-render-manager.js.map +1 -1
- package/dist/lib/rendering/tileset-styler.js +1 -1
- package/dist/lib/rendering/tileset-styler.js.map +1 -1
- package/dist/lib/rendering/visual-register-culler.js +2 -0
- package/dist/lib/rendering/visual-register-culler.js.map +1 -1
- package/dist/lib/rendering/visuals-register.js +89 -20
- package/dist/lib/rendering/visuals-register.js.map +1 -1
- package/dist/lib/utils/entity-utils.js +43 -2
- package/dist/lib/utils/entity-utils.js.map +1 -1
- package/dist/lib/widgets/widget-info-view.js +20 -7
- package/dist/lib/widgets/widget-info-view.js.map +1 -1
- package/dist/types/bruce-cesium.d.ts +2 -1
- package/dist/types/rendering/cesium-animated-property.d.ts +8 -6
- package/dist/types/rendering/entity-gatherer.d.ts +6 -1
- package/dist/types/rendering/menu-item-manager.d.ts +33 -1
- package/dist/types/rendering/relation-render-engine.d.ts +10 -0
- package/dist/types/rendering/render-managers/common/entity-label-group.d.ts +3 -2
- package/dist/types/rendering/render-managers/common/entity-label.d.ts +5 -0
- package/dist/types/rendering/render-managers/common/entity-rerender-maintain-state.d.ts +20 -0
- package/dist/types/rendering/render-managers/common/entity-simulation.d.ts +126 -0
- package/dist/types/rendering/render-managers/common/historic-trail.d.ts +30 -0
- package/dist/types/rendering/render-managers/common/historic-utils.d.ts +4 -0
- package/dist/types/rendering/render-managers/entities/entities-datalab-render-manager.d.ts +2 -0
- package/dist/types/rendering/render-managers/entities/entities-ids-render-manager.d.ts +2 -0
- package/dist/types/rendering/render-managers/entities/entities-loaded-render-manager.d.ts +2 -0
- package/dist/types/rendering/render-managers/entities/entities-render-manager.d.ts +2 -0
- package/dist/types/rendering/render-managers/entities/entity-render-manager.d.ts +2 -0
- package/dist/types/rendering/render-managers/other/assembly-render-manager.d.ts +79 -2
- package/dist/types/rendering/render-managers/other/legacy-relations-render-manager.d.ts +5 -0
- package/dist/types/rendering/render-managers/other/relations-render-manager.d.ts +5 -0
- package/dist/types/rendering/render-managers/render-manager.d.ts +4 -0
- package/dist/types/rendering/render-managers/tilesets/tileset-arb-render-manager.d.ts +3 -0
- package/dist/types/rendering/render-managers/tilesets/tileset-cad-render-manager.d.ts +11 -1
- package/dist/types/rendering/render-managers/tilesets/tileset-entities-render-manager.d.ts +3 -0
- package/dist/types/rendering/tileset-styler.d.ts +2 -0
- package/dist/types/rendering/visuals-register.d.ts +13 -0
- package/dist/types/utils/entity-utils.d.ts +6 -0
- package/dist/types/widgets/widget-info-view.d.ts +1 -0
- package/package.json +2 -2
package/dist/bruce-cesium.umd.js
CHANGED
|
@@ -2621,13 +2621,13 @@
|
|
|
2621
2621
|
class AnimateTPositionSeries {
|
|
2622
2622
|
constructor(params) {
|
|
2623
2623
|
this.lastCalcAttitude = null;
|
|
2624
|
-
// Cache for calculated values.
|
|
2624
|
+
// Cache for calculated values: the scene time and set of positions they were worked out for.
|
|
2625
2625
|
this.lastCalcTime = null;
|
|
2626
|
+
this.lastCalcRevision = -1;
|
|
2626
2627
|
this.lastCalcPos3d = null;
|
|
2627
|
-
this.lastCalcPosApplyOverTime = false;
|
|
2628
|
-
this.lastCalcPos3dApplyOverTimeTarget = null;
|
|
2629
|
-
this.lastCalcPos3dApplyOverTimeTimeStart = null;
|
|
2630
2628
|
this.lastCalcHeading = null;
|
|
2629
|
+
// Bumped whenever the positions change, so a still clock re-applies a changed series.
|
|
2630
|
+
this.revision = 0;
|
|
2631
2631
|
this.removal = null;
|
|
2632
2632
|
// Track the timeline range for which we have data.
|
|
2633
2633
|
this.minDate = null;
|
|
@@ -2657,6 +2657,7 @@
|
|
|
2657
2657
|
if (!newPositions || newPositions.length === 0) {
|
|
2658
2658
|
return;
|
|
2659
2659
|
}
|
|
2660
|
+
this.revision++;
|
|
2660
2661
|
// Add new positions to our map, overwriting any existing positions with the same timestamp
|
|
2661
2662
|
for (const pos of newPositions) {
|
|
2662
2663
|
const key = pos.dateTime.toISOString();
|
|
@@ -2680,6 +2681,17 @@
|
|
|
2680
2681
|
}
|
|
2681
2682
|
}
|
|
2682
2683
|
}
|
|
2684
|
+
/**
|
|
2685
|
+
* Replaces every position with these, eg. once some that were simulated have been cleared.
|
|
2686
|
+
* @param positions Positions to animate through from now on.
|
|
2687
|
+
*/
|
|
2688
|
+
replacePositions(positions) {
|
|
2689
|
+
this.positionsMap.clear();
|
|
2690
|
+
this.positions = [];
|
|
2691
|
+
this.minDate = null;
|
|
2692
|
+
this.maxDate = null;
|
|
2693
|
+
this.addPositions(positions);
|
|
2694
|
+
}
|
|
2683
2695
|
/**
|
|
2684
2696
|
* Get the current date range covered by our positions.
|
|
2685
2697
|
*/
|
|
@@ -2727,106 +2739,47 @@
|
|
|
2727
2739
|
/*
|
|
2728
2740
|
* Returns the attitude of the record at an index, or nothing recorded when it names none.
|
|
2729
2741
|
*/
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2742
|
+
/*
|
|
2743
|
+
* Pitch and roll between two positions the short way round; an angle only one names is held.
|
|
2744
|
+
*/
|
|
2745
|
+
attitudeBetween(indexLast, indexNext, timeMs) {
|
|
2746
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2747
|
+
const a = indexLast >= 0 ? (_a = this.positions) === null || _a === void 0 ? void 0 : _a[indexLast] : null;
|
|
2748
|
+
const b = indexNext >= 0 ? (_b = this.positions) === null || _b === void 0 ? void 0 : _b[indexNext] : null;
|
|
2749
|
+
const span = a && b ? b.dateTime.getTime() - a.dateTime.getTime() : 0;
|
|
2750
|
+
const t = span > 0 ? (timeMs - a.dateTime.getTime()) / span : 0;
|
|
2751
|
+
const turn = (x, y) => {
|
|
2752
|
+
if (x == null || y == null) {
|
|
2753
|
+
return x !== null && x !== void 0 ? x : null;
|
|
2754
|
+
}
|
|
2755
|
+
return x + (Cesium.Math.negativePiToPi(Cesium.Math.toRadians(y - x)) * 180 / Math.PI) * t;
|
|
2756
|
+
};
|
|
2757
|
+
return { pitch: turn((_c = a === null || a === void 0 ? void 0 : a.pitch) !== null && _c !== void 0 ? _c : null, (_d = b === null || b === void 0 ? void 0 : b.pitch) !== null && _d !== void 0 ? _d : null), roll: turn((_e = a === null || a === void 0 ? void 0 : a.roll) !== null && _e !== void 0 ? _e : null, (_f = b === null || b === void 0 ? void 0 : b.roll) !== null && _f !== void 0 ? _f : null) };
|
|
2734
2758
|
}
|
|
2759
|
+
/*
|
|
2760
|
+
* Places the series at the scene's time, exactly rather than eased, so what moves with it never lags the clock.
|
|
2761
|
+
*/
|
|
2735
2762
|
update() {
|
|
2763
|
+
var _a;
|
|
2736
2764
|
if (!this.positions || this.positions.length === 0) {
|
|
2737
2765
|
return;
|
|
2738
2766
|
}
|
|
2739
|
-
//
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
const currentRealTimeMs = Date.now();
|
|
2744
|
-
// Calculate the ideal position based on current time.
|
|
2745
|
-
const targetPosition = this.calculatePosition(nowTimeMs);
|
|
2746
|
-
// Initialize if this is the first update.
|
|
2747
|
-
if (!this.lastCalcPos3d) {
|
|
2748
|
-
this.lastCalcPos3d = targetPosition.pos3d;
|
|
2749
|
-
this.lastCalcAttitude = this.attitudeAt(targetPosition.indexLast);
|
|
2750
|
-
this.onUpdate(targetPosition.pos3d, this.lastCalcHeading, this.lastCalcAttitude);
|
|
2751
|
-
return;
|
|
2752
|
-
}
|
|
2753
|
-
// Check if target has changed.
|
|
2754
|
-
if (!this.lastCalcPos3dApplyOverTimeTarget ||
|
|
2755
|
-
!Cesium.Cartesian3.equals(this.lastCalcPos3dApplyOverTimeTarget, targetPosition.pos3d)) {
|
|
2756
|
-
// If we were already interpolating, get the current interpolated position as our new starting point.
|
|
2757
|
-
if (this.lastCalcPosApplyOverTime && this.lastCalcPos3dApplyOverTimeTimeStart) {
|
|
2758
|
-
// Calculate where we are in the current interpolation.
|
|
2759
|
-
const currentInterpolatedPos = this.interpolatePos3dOverTime(this.lastCalcPos3dApplyOverTimeTimeStart, currentRealTimeMs);
|
|
2760
|
-
// Use this as our new starting position.
|
|
2761
|
-
this.lastCalcPos3d = currentInterpolatedPos;
|
|
2762
|
-
}
|
|
2763
|
-
// Set new target and start time.
|
|
2764
|
-
this.lastCalcPos3dApplyOverTimeTarget = targetPosition.pos3d;
|
|
2765
|
-
this.lastCalcPos3dApplyOverTimeTimeStart = currentRealTimeMs;
|
|
2766
|
-
this.lastCalcPosApplyOverTime = true;
|
|
2767
|
-
}
|
|
2768
|
-
// Calculate the interpolated position.
|
|
2769
|
-
let pos3d;
|
|
2770
|
-
if (this.lastCalcPosApplyOverTime) {
|
|
2771
|
-
pos3d = this.interpolatePos3dOverTime(this.lastCalcPos3dApplyOverTimeTimeStart, currentRealTimeMs);
|
|
2772
|
-
// If we've reached the target, update our base position.
|
|
2773
|
-
if (Cesium.Cartesian3.distance(pos3d, this.lastCalcPos3dApplyOverTimeTarget) < 0.01) {
|
|
2774
|
-
this.lastCalcPos3d = this.lastCalcPos3dApplyOverTimeTarget;
|
|
2775
|
-
this.lastCalcPosApplyOverTime = false;
|
|
2776
|
-
}
|
|
2767
|
+
// The clock's time is the frame being drawn; the last render time is the frame before it.
|
|
2768
|
+
const nowTimeMs = Cesium.JulianDate.toDate(this.viewer.clock.currentTime).getTime();
|
|
2769
|
+
if (nowTimeMs === this.lastCalcTime && this.revision === this.lastCalcRevision) {
|
|
2770
|
+
return;
|
|
2777
2771
|
}
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
const currentToTargetDistance = Cesium.Cartesian3.distance(this.lastCalcPos3d, this.lastCalcPos3dApplyOverTimeTarget);
|
|
2786
|
-
if (currentToTargetDistance >= minDistanceThreshold) {
|
|
2787
|
-
const lastPos = {
|
|
2788
|
-
pos3d: this.lastCalcPos3d,
|
|
2789
|
-
dateTime: this.lastCalcPos3dApplyOverTimeTimeStart ? new Date(this.lastCalcPos3dApplyOverTimeTimeStart) : null,
|
|
2790
|
-
heading: null
|
|
2791
|
-
};
|
|
2792
|
-
const nextPos = {
|
|
2793
|
-
pos3d: this.lastCalcPos3dApplyOverTimeTarget,
|
|
2794
|
-
dateTime: null,
|
|
2795
|
-
heading: null
|
|
2796
|
-
};
|
|
2797
|
-
if (lastPos && nextPos) {
|
|
2798
|
-
this.lastCalcHeading = this.calculateHeading(nowTimeMs, lastPos, nextPos);
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
}
|
|
2772
|
+
this.lastCalcTime = nowTimeMs;
|
|
2773
|
+
this.lastCalcRevision = this.revision;
|
|
2774
|
+
const target = this.calculatePosition(nowTimeMs);
|
|
2775
|
+
const lastPos = this.positions[target.indexLast];
|
|
2776
|
+
const nextPos = this.positions[target.indexNext];
|
|
2777
|
+
if (lastPos && nextPos) {
|
|
2778
|
+
this.lastCalcHeading = (_a = this.calculateHeading(nowTimeMs, lastPos, nextPos)) !== null && _a !== void 0 ? _a : this.lastCalcHeading;
|
|
2802
2779
|
}
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
const nextPos = posses[targetPosition.indexNext];
|
|
2807
|
-
if (lastPos && nextPos) {
|
|
2808
|
-
this.lastCalcHeading = this.calculateHeading(nowTimeMs, lastPos, nextPos);
|
|
2809
|
-
}
|
|
2810
|
-
}
|
|
2811
|
-
// Provide the calculated values to the caller.
|
|
2812
|
-
this.lastCalcAttitude = this.attitudeAt(targetPosition.indexLast);
|
|
2813
|
-
this.onUpdate(pos3d, this.lastCalcHeading, this.lastCalcAttitude);
|
|
2814
|
-
}
|
|
2815
|
-
interpolatePos3dOverTime(startTimeMs, currentTimeMs) {
|
|
2816
|
-
const lastPos = this.lastCalcPos3d;
|
|
2817
|
-
const targetPos = this.lastCalcPos3dApplyOverTimeTarget;
|
|
2818
|
-
if (!lastPos || !targetPos) {
|
|
2819
|
-
return lastPos ? lastPos : targetPos;
|
|
2820
|
-
}
|
|
2821
|
-
const interpolationDuration = 200;
|
|
2822
|
-
const elapsedTime = currentTimeMs - startTimeMs;
|
|
2823
|
-
let progress = Math.min(elapsedTime / interpolationDuration, 1.0);
|
|
2824
|
-
// Optional step to make it smoother.
|
|
2825
|
-
progress = this.easeInOutQuad(progress);
|
|
2826
|
-
return Cesium.Cartesian3.lerp(lastPos, targetPos, progress, new Cesium.Cartesian3());
|
|
2827
|
-
}
|
|
2828
|
-
easeInOutQuad(t) {
|
|
2829
|
-
return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
|
|
2780
|
+
this.lastCalcPos3d = target.pos3d;
|
|
2781
|
+
this.lastCalcAttitude = this.attitudeBetween(target.indexLast, target.indexNext, nowTimeMs);
|
|
2782
|
+
this.onUpdate(target.pos3d, this.lastCalcHeading, this.lastCalcAttitude);
|
|
2830
2783
|
}
|
|
2831
2784
|
/**
|
|
2832
2785
|
* Pre-process headings in the series.
|
|
@@ -2852,14 +2805,14 @@
|
|
|
2852
2805
|
* Calculate the position at the given time.
|
|
2853
2806
|
*/
|
|
2854
2807
|
calculatePosition(currentTimeMs) {
|
|
2808
|
+
var _a, _b;
|
|
2855
2809
|
const posses = this.positions;
|
|
2856
2810
|
// No positions to interpolate
|
|
2857
2811
|
if (!posses || posses.length === 0) {
|
|
2858
2812
|
return {
|
|
2859
2813
|
pos3d: new Cesium.Cartesian3(),
|
|
2860
2814
|
indexLast: -1,
|
|
2861
|
-
indexNext: -1
|
|
2862
|
-
applyOverTime: false
|
|
2815
|
+
indexNext: -1
|
|
2863
2816
|
};
|
|
2864
2817
|
}
|
|
2865
2818
|
// See if we're before the first position.
|
|
@@ -2867,8 +2820,7 @@
|
|
|
2867
2820
|
return {
|
|
2868
2821
|
pos3d: posses[0].pos3d,
|
|
2869
2822
|
indexLast: 0,
|
|
2870
|
-
indexNext: 0
|
|
2871
|
-
applyOverTime: true
|
|
2823
|
+
indexNext: 0
|
|
2872
2824
|
};
|
|
2873
2825
|
}
|
|
2874
2826
|
// See if we're after the last position.
|
|
@@ -2877,8 +2829,7 @@
|
|
|
2877
2829
|
return {
|
|
2878
2830
|
pos3d: posses[lastIndex].pos3d,
|
|
2879
2831
|
indexLast: lastIndex,
|
|
2880
|
-
indexNext: lastIndex
|
|
2881
|
-
applyOverTime: true
|
|
2832
|
+
indexNext: lastIndex
|
|
2882
2833
|
};
|
|
2883
2834
|
}
|
|
2884
2835
|
// Binary search to find the closest position.
|
|
@@ -2904,20 +2855,25 @@
|
|
|
2904
2855
|
return {
|
|
2905
2856
|
pos3d: last.pos3d,
|
|
2906
2857
|
indexLast: lastIndex,
|
|
2907
|
-
indexNext: lastIndex
|
|
2908
|
-
applyOverTime: true
|
|
2858
|
+
indexNext: lastIndex
|
|
2909
2859
|
};
|
|
2910
2860
|
}
|
|
2911
2861
|
// Interpolate between the two closest positions.
|
|
2912
2862
|
const lastTime = last.dateTime.getTime();
|
|
2913
2863
|
const nextTime = next.dateTime.getTime();
|
|
2914
2864
|
const progress = (currentTimeMs - lastTime) / (nextTime - lastTime);
|
|
2915
|
-
|
|
2865
|
+
// A straight line between two points on a curved path cuts inside it, so it is lifted back out.
|
|
2866
|
+
const chord = Cesium.Cartesian3.lerp(last.pos3d, next.pos3d, progress, new Cesium.Cartesian3());
|
|
2867
|
+
const chordCarto = Cesium.Cartographic.fromCartesian(chord);
|
|
2868
|
+
const lastHeight = (_a = Cesium.Cartographic.fromCartesian(last.pos3d)) === null || _a === void 0 ? void 0 : _a.height;
|
|
2869
|
+
const nextHeight = (_b = Cesium.Cartographic.fromCartesian(next.pos3d)) === null || _b === void 0 ? void 0 : _b.height;
|
|
2870
|
+
const interpolatedPos = chordCarto && lastHeight != null && nextHeight != null
|
|
2871
|
+
? Cesium.Cartesian3.fromRadians(chordCarto.longitude, chordCarto.latitude, Cesium.Math.lerp(lastHeight, nextHeight, progress))
|
|
2872
|
+
: chord;
|
|
2916
2873
|
return {
|
|
2917
2874
|
pos3d: interpolatedPos,
|
|
2918
2875
|
indexLast: lastIndex,
|
|
2919
|
-
indexNext: nextIndex
|
|
2920
|
-
applyOverTime: false
|
|
2876
|
+
indexNext: nextIndex
|
|
2921
2877
|
};
|
|
2922
2878
|
}
|
|
2923
2879
|
/**
|
|
@@ -5481,6 +5437,481 @@
|
|
|
5481
5437
|
return result;
|
|
5482
5438
|
}
|
|
5483
5439
|
|
|
5440
|
+
(function (EntitySimulation) {
|
|
5441
|
+
const VIEWER_KEY = "_nextspaceSimulation";
|
|
5442
|
+
/**
|
|
5443
|
+
* The set used when a caller does not name one.
|
|
5444
|
+
*/
|
|
5445
|
+
EntitySimulation.DEFAULT_SET = "default";
|
|
5446
|
+
// Record parts replaced whole rather than merged field by field, eg. a matrix or a geometry.
|
|
5447
|
+
const WHOLE_RECORD_PARTS = new Set(["geometry", "boundaries", "assemblyPosition"]);
|
|
5448
|
+
// Record parts that carry Entity data, in the order they are laid over an Entity.
|
|
5449
|
+
const RECORD_PARTS = ["location", "transform", "geometry", "boundaries", "assemblyPosition", "data"];
|
|
5450
|
+
/**
|
|
5451
|
+
* The store for a viewer, made on first use and shared by everything drawing into that scene.
|
|
5452
|
+
*/
|
|
5453
|
+
function Get(viewer) {
|
|
5454
|
+
if (!viewer) {
|
|
5455
|
+
return null;
|
|
5456
|
+
}
|
|
5457
|
+
if (!viewer[VIEWER_KEY]) {
|
|
5458
|
+
viewer[VIEWER_KEY] = new Store();
|
|
5459
|
+
}
|
|
5460
|
+
return viewer[VIEWER_KEY];
|
|
5461
|
+
}
|
|
5462
|
+
EntitySimulation.Get = Get;
|
|
5463
|
+
function isPlainObject(value) {
|
|
5464
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
5465
|
+
}
|
|
5466
|
+
// Entity values replaced whole by a patch, since half of one geometry over another describes neither.
|
|
5467
|
+
const WHOLE_ENTITY_PARTS = new Set(["VectorGeometry", "Boundaries", "AssemblyPosition"]);
|
|
5468
|
+
/**
|
|
5469
|
+
* Returns base with patch laid over it: objects merge key by key, anything else replaces.
|
|
5470
|
+
* @param whole names keys at the top level, or under Bruce, whose value is replaced rather than merged.
|
|
5471
|
+
*/
|
|
5472
|
+
function Overlay(base, patch, whole = WHOLE_ENTITY_PARTS) {
|
|
5473
|
+
const lay = (into, over, depth, underBruce) => {
|
|
5474
|
+
if (over === undefined) {
|
|
5475
|
+
return into;
|
|
5476
|
+
}
|
|
5477
|
+
if (!isPlainObject(over) || !isPlainObject(into)) {
|
|
5478
|
+
return over;
|
|
5479
|
+
}
|
|
5480
|
+
const merged = { ...into };
|
|
5481
|
+
for (const key of Object.keys(over)) {
|
|
5482
|
+
const replace = (depth === 0 || underBruce) && whole.has(key);
|
|
5483
|
+
merged[key] = replace ? over[key] : lay(merged[key], over[key], depth + 1, depth === 0 && key === "Bruce");
|
|
5484
|
+
}
|
|
5485
|
+
return merged;
|
|
5486
|
+
};
|
|
5487
|
+
return lay(base, patch, 0, false);
|
|
5488
|
+
}
|
|
5489
|
+
EntitySimulation.Overlay = Overlay;
|
|
5490
|
+
/*
|
|
5491
|
+
* Lays one record's parts over another's, each part replaced whole where a part is one value.
|
|
5492
|
+
*/
|
|
5493
|
+
function overlayRecord(base, over) {
|
|
5494
|
+
return Overlay(base, over, WHOLE_RECORD_PARTS);
|
|
5495
|
+
}
|
|
5496
|
+
/**
|
|
5497
|
+
* Each value a record names, as a path into the record. A part replaced whole is one value.
|
|
5498
|
+
*/
|
|
5499
|
+
function recordPaths(record) {
|
|
5500
|
+
const paths = [];
|
|
5501
|
+
const walk = (value, path) => {
|
|
5502
|
+
if (isPlainObject(value) && !(path.length === 1 && WHOLE_RECORD_PARTS.has(path[0]))) {
|
|
5503
|
+
for (const key of Object.keys(value)) {
|
|
5504
|
+
walk(value[key], [...path, key]);
|
|
5505
|
+
}
|
|
5506
|
+
return;
|
|
5507
|
+
}
|
|
5508
|
+
if (value !== undefined) {
|
|
5509
|
+
paths.push(path);
|
|
5510
|
+
}
|
|
5511
|
+
};
|
|
5512
|
+
for (const part of RECORD_PARTS) {
|
|
5513
|
+
if ((record === null || record === void 0 ? void 0 : record[part]) != null) {
|
|
5514
|
+
walk(record[part], [part]);
|
|
5515
|
+
}
|
|
5516
|
+
}
|
|
5517
|
+
return paths;
|
|
5518
|
+
}
|
|
5519
|
+
/*
|
|
5520
|
+
* A copy of the record without the values at these paths, dropping objects left empty.
|
|
5521
|
+
*/
|
|
5522
|
+
function withoutPaths(record, paths) {
|
|
5523
|
+
const strip = (value, path) => {
|
|
5524
|
+
if (!isPlainObject(value) || !(path[0] in value)) {
|
|
5525
|
+
return value;
|
|
5526
|
+
}
|
|
5527
|
+
const copy = { ...value };
|
|
5528
|
+
if (path.length === 1) {
|
|
5529
|
+
delete copy[path[0]];
|
|
5530
|
+
}
|
|
5531
|
+
else {
|
|
5532
|
+
const inner = strip(copy[path[0]], path.slice(1));
|
|
5533
|
+
if (isPlainObject(inner) && !Object.keys(inner).length) {
|
|
5534
|
+
delete copy[path[0]];
|
|
5535
|
+
}
|
|
5536
|
+
else {
|
|
5537
|
+
copy[path[0]] = inner;
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
5540
|
+
return copy;
|
|
5541
|
+
};
|
|
5542
|
+
let stripped = record;
|
|
5543
|
+
for (const path of paths) {
|
|
5544
|
+
stripped = strip(stripped, path);
|
|
5545
|
+
}
|
|
5546
|
+
return stripped;
|
|
5547
|
+
}
|
|
5548
|
+
/*
|
|
5549
|
+
* Whether a record still says anything about its Entity.
|
|
5550
|
+
*/
|
|
5551
|
+
function hasContent(record) {
|
|
5552
|
+
return RECORD_PARTS.some(part => {
|
|
5553
|
+
const value = record === null || record === void 0 ? void 0 : record[part];
|
|
5554
|
+
return value != null && !(isPlainObject(value) && !Object.keys(value).length);
|
|
5555
|
+
});
|
|
5556
|
+
}
|
|
5557
|
+
function timeOf(record) {
|
|
5558
|
+
return (record === null || record === void 0 ? void 0 : record.dateTime) ? new Date(record.dateTime).getTime() : NaN;
|
|
5559
|
+
}
|
|
5560
|
+
class Store {
|
|
5561
|
+
constructor() {
|
|
5562
|
+
// Insertion order is precedence: a later set wins.
|
|
5563
|
+
this.sets = new Map();
|
|
5564
|
+
this.onChanged = new BModels.BruceEvent();
|
|
5565
|
+
}
|
|
5566
|
+
/**
|
|
5567
|
+
* Triggered after anything is simulated or cleared, naming the Entities affected.
|
|
5568
|
+
*/
|
|
5569
|
+
get OnChanged() {
|
|
5570
|
+
return this.onChanged;
|
|
5571
|
+
}
|
|
5572
|
+
/**
|
|
5573
|
+
* Whether anything at all is simulated, so a caller can skip the work when nothing is.
|
|
5574
|
+
*/
|
|
5575
|
+
get IsEmpty() {
|
|
5576
|
+
for (const set of this.sets.values()) {
|
|
5577
|
+
if (set.entities.size || set.patches.size || set.records.size) {
|
|
5578
|
+
return false;
|
|
5579
|
+
}
|
|
5580
|
+
}
|
|
5581
|
+
return true;
|
|
5582
|
+
}
|
|
5583
|
+
/**
|
|
5584
|
+
* The names of the sets holding anything, oldest first.
|
|
5585
|
+
*/
|
|
5586
|
+
GetSets() {
|
|
5587
|
+
return Array.from(this.sets.keys());
|
|
5588
|
+
}
|
|
5589
|
+
/**
|
|
5590
|
+
* The time each set's records span, oldest set first, for sets holding any records.
|
|
5591
|
+
*/
|
|
5592
|
+
GetSetRanges() {
|
|
5593
|
+
const ranges = [];
|
|
5594
|
+
for (const [name, set] of this.sets) {
|
|
5595
|
+
let startMs = Infinity;
|
|
5596
|
+
let endMs = -Infinity;
|
|
5597
|
+
for (const byTime of set.records.values()) {
|
|
5598
|
+
for (const ms of byTime.keys()) {
|
|
5599
|
+
startMs = Math.min(startMs, ms);
|
|
5600
|
+
endMs = Math.max(endMs, ms);
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
if (set.records.size) {
|
|
5604
|
+
ranges.push({ set: name, startMs, endMs, entityIds: Array.from(set.records.keys()) });
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5607
|
+
return ranges;
|
|
5608
|
+
}
|
|
5609
|
+
setFor(name) {
|
|
5610
|
+
const key = name || EntitySimulation.DEFAULT_SET;
|
|
5611
|
+
let set = this.sets.get(key);
|
|
5612
|
+
if (!set) {
|
|
5613
|
+
set = { entities: new Map(), patches: new Map(), records: new Map() };
|
|
5614
|
+
this.sets.set(key, set);
|
|
5615
|
+
}
|
|
5616
|
+
return set;
|
|
5617
|
+
}
|
|
5618
|
+
/**
|
|
5619
|
+
* Replaces the stand-in for each Entity given, within a set.
|
|
5620
|
+
*/
|
|
5621
|
+
SetEntities(entities, set) {
|
|
5622
|
+
this.setBlobs(entities, set, false);
|
|
5623
|
+
}
|
|
5624
|
+
/**
|
|
5625
|
+
* Replaces the patch for each Entity given, within a set. A patch names only the data it changes.
|
|
5626
|
+
*/
|
|
5627
|
+
SetPatches(patches, set) {
|
|
5628
|
+
this.setBlobs(patches, set, true);
|
|
5629
|
+
}
|
|
5630
|
+
setBlobs(blobs, set, patch) {
|
|
5631
|
+
var _a;
|
|
5632
|
+
const ids = [];
|
|
5633
|
+
for (const blob of blobs || []) {
|
|
5634
|
+
const id = (_a = blob === null || blob === void 0 ? void 0 : blob.Bruce) === null || _a === void 0 ? void 0 : _a.ID;
|
|
5635
|
+
if (!id) {
|
|
5636
|
+
continue;
|
|
5637
|
+
}
|
|
5638
|
+
const held = this.setFor(set);
|
|
5639
|
+
(patch ? held.patches : held.entities).set(id, blob);
|
|
5640
|
+
ids.push(id);
|
|
5641
|
+
}
|
|
5642
|
+
if (ids.length) {
|
|
5643
|
+
this.onChanged.Trigger({ entityIds: ids, entities: true, records: false, set: set || EntitySimulation.DEFAULT_SET });
|
|
5644
|
+
}
|
|
5645
|
+
}
|
|
5646
|
+
/**
|
|
5647
|
+
* Adds records to each Entity's simulated history within a set, replacing any at the same instant.
|
|
5648
|
+
*/
|
|
5649
|
+
SetRecords(records, set) {
|
|
5650
|
+
const ids = new Set();
|
|
5651
|
+
for (const record of records || []) {
|
|
5652
|
+
const id = record === null || record === void 0 ? void 0 : record.entityId;
|
|
5653
|
+
const timeMs = timeOf(record);
|
|
5654
|
+
if (!id || isNaN(timeMs)) {
|
|
5655
|
+
continue;
|
|
5656
|
+
}
|
|
5657
|
+
const held = this.setFor(set);
|
|
5658
|
+
let byTime = held.records.get(id);
|
|
5659
|
+
if (!byTime) {
|
|
5660
|
+
byTime = new Map();
|
|
5661
|
+
held.records.set(id, byTime);
|
|
5662
|
+
}
|
|
5663
|
+
byTime.set(timeMs, record);
|
|
5664
|
+
ids.add(id);
|
|
5665
|
+
}
|
|
5666
|
+
if (ids.size) {
|
|
5667
|
+
this.onChanged.Trigger({ entityIds: Array.from(ids), entities: false, records: true, set: set || EntitySimulation.DEFAULT_SET });
|
|
5668
|
+
}
|
|
5669
|
+
}
|
|
5670
|
+
/**
|
|
5671
|
+
* Forgets what is simulated for these Entities, or for every Entity when none are named.
|
|
5672
|
+
* @param params.set the set to clear. By default every set.
|
|
5673
|
+
* @param params.entities clear the stand-ins and patches. Default true.
|
|
5674
|
+
* @param params.records clear the historic records. Default true.
|
|
5675
|
+
*/
|
|
5676
|
+
Clear(params) {
|
|
5677
|
+
var _a, _b;
|
|
5678
|
+
const entities = (params === null || params === void 0 ? void 0 : params.entities) !== false;
|
|
5679
|
+
const records = (params === null || params === void 0 ? void 0 : params.records) !== false;
|
|
5680
|
+
const names = (params === null || params === void 0 ? void 0 : params.set) ? [params.set] : Array.from(this.sets.keys());
|
|
5681
|
+
const cleared = new Set();
|
|
5682
|
+
for (const name of names) {
|
|
5683
|
+
const set = this.sets.get(name);
|
|
5684
|
+
if (!set) {
|
|
5685
|
+
continue;
|
|
5686
|
+
}
|
|
5687
|
+
const ids = (_a = params === null || params === void 0 ? void 0 : params.entityIds) !== null && _a !== void 0 ? _a : [...set.entities.keys(), ...set.patches.keys(), ...set.records.keys()];
|
|
5688
|
+
for (const id of ids) {
|
|
5689
|
+
const hadEntity = entities && set.entities.delete(id);
|
|
5690
|
+
const hadPatch = entities && set.patches.delete(id);
|
|
5691
|
+
const hadRecords = records && set.records.delete(id);
|
|
5692
|
+
if (hadEntity || hadPatch || hadRecords) {
|
|
5693
|
+
cleared.add(id);
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
if (!set.entities.size && !set.patches.size && !set.records.size) {
|
|
5697
|
+
this.sets.delete(name);
|
|
5698
|
+
}
|
|
5699
|
+
}
|
|
5700
|
+
if (cleared.size) {
|
|
5701
|
+
this.onChanged.Trigger({ entityIds: Array.from(cleared), entities, records, set: (_b = params === null || params === void 0 ? void 0 : params.set) !== null && _b !== void 0 ? _b : null });
|
|
5702
|
+
}
|
|
5703
|
+
}
|
|
5704
|
+
/**
|
|
5705
|
+
* The Entity's stand-in, with any patches laid over it, or null when nothing stands in for it.
|
|
5706
|
+
*/
|
|
5707
|
+
GetEntity(entityId) {
|
|
5708
|
+
var _a;
|
|
5709
|
+
let standIn = null;
|
|
5710
|
+
for (const set of this.sets.values()) {
|
|
5711
|
+
standIn = (_a = set.entities.get(entityId)) !== null && _a !== void 0 ? _a : standIn;
|
|
5712
|
+
}
|
|
5713
|
+
return standIn ? this.Overlay(standIn) : null;
|
|
5714
|
+
}
|
|
5715
|
+
/**
|
|
5716
|
+
* Whether a stand-in replaces this Entity, so the API need not be asked for it.
|
|
5717
|
+
*/
|
|
5718
|
+
HasEntity(entityId) {
|
|
5719
|
+
for (const set of this.sets.values()) {
|
|
5720
|
+
if (set.entities.has(entityId)) {
|
|
5721
|
+
return true;
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
return false;
|
|
5725
|
+
}
|
|
5726
|
+
/**
|
|
5727
|
+
* Whether a stand-in or a patch changes this Entity.
|
|
5728
|
+
*/
|
|
5729
|
+
HasBlob(entityId) {
|
|
5730
|
+
for (const set of this.sets.values()) {
|
|
5731
|
+
if (set.entities.has(entityId) || set.patches.has(entityId)) {
|
|
5732
|
+
return true;
|
|
5733
|
+
}
|
|
5734
|
+
}
|
|
5735
|
+
return false;
|
|
5736
|
+
}
|
|
5737
|
+
/**
|
|
5738
|
+
* Returns an Entity with its simulated patches laid over it. The input is never changed.
|
|
5739
|
+
*/
|
|
5740
|
+
Overlay(entity) {
|
|
5741
|
+
var _a;
|
|
5742
|
+
const id = (_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID;
|
|
5743
|
+
if (!id) {
|
|
5744
|
+
return entity;
|
|
5745
|
+
}
|
|
5746
|
+
let result = entity;
|
|
5747
|
+
for (const set of this.sets.values()) {
|
|
5748
|
+
const patch = set.patches.get(id);
|
|
5749
|
+
if (patch) {
|
|
5750
|
+
result = Overlay(result, patch);
|
|
5751
|
+
}
|
|
5752
|
+
}
|
|
5753
|
+
return result;
|
|
5754
|
+
}
|
|
5755
|
+
/**
|
|
5756
|
+
* The Entity as simulated: its stand-in if there is one, otherwise what was read with patches laid over it.
|
|
5757
|
+
* @param entityId the Entity.
|
|
5758
|
+
* @param read what the API gave for it, if it was asked.
|
|
5759
|
+
*/
|
|
5760
|
+
Resolve(entityId, read) {
|
|
5761
|
+
var _a;
|
|
5762
|
+
return (_a = this.GetEntity(entityId)) !== null && _a !== void 0 ? _a : (read ? this.Overlay(read) : null);
|
|
5763
|
+
}
|
|
5764
|
+
/**
|
|
5765
|
+
* Every Entity with a simulated stand-in or patch.
|
|
5766
|
+
*/
|
|
5767
|
+
GetEntityIds() {
|
|
5768
|
+
const ids = new Set();
|
|
5769
|
+
for (const set of this.sets.values()) {
|
|
5770
|
+
set.entities.forEach((_, id) => ids.add(id));
|
|
5771
|
+
set.patches.forEach((_, id) => ids.add(id));
|
|
5772
|
+
}
|
|
5773
|
+
return Array.from(ids);
|
|
5774
|
+
}
|
|
5775
|
+
/**
|
|
5776
|
+
* Every Entity with simulated history.
|
|
5777
|
+
*/
|
|
5778
|
+
GetRecordEntityIds() {
|
|
5779
|
+
const ids = new Set();
|
|
5780
|
+
for (const set of this.sets.values()) {
|
|
5781
|
+
set.records.forEach((_, id) => ids.add(id));
|
|
5782
|
+
}
|
|
5783
|
+
return Array.from(ids);
|
|
5784
|
+
}
|
|
5785
|
+
/**
|
|
5786
|
+
* An Entity's simulated history for a historic key, oldest first, every set's record at an instant merged.
|
|
5787
|
+
*/
|
|
5788
|
+
GetRecords(entityId, attrKey) {
|
|
5789
|
+
const byTime = new Map();
|
|
5790
|
+
for (const set of this.sets.values()) {
|
|
5791
|
+
for (const [ms, record] of set.records.get(entityId) || []) {
|
|
5792
|
+
if (attrKey && record.attrKey && record.attrKey !== attrKey) {
|
|
5793
|
+
continue;
|
|
5794
|
+
}
|
|
5795
|
+
const held = byTime.get(ms);
|
|
5796
|
+
byTime.set(ms, held ? overlayRecord(held, record) : record);
|
|
5797
|
+
}
|
|
5798
|
+
}
|
|
5799
|
+
return Array.from(byTime.entries()).sort(([a], [b]) => a - b).map(([, record]) => record);
|
|
5800
|
+
}
|
|
5801
|
+
/**
|
|
5802
|
+
* Each value an Entity's simulated history names, with the instant from which it is the simulation's.
|
|
5803
|
+
*/
|
|
5804
|
+
ownedFrom(records) {
|
|
5805
|
+
const owned = new Map();
|
|
5806
|
+
for (const record of records) {
|
|
5807
|
+
const ms = timeOf(record);
|
|
5808
|
+
for (const path of recordPaths(record)) {
|
|
5809
|
+
const key = path.join("\u0000");
|
|
5810
|
+
if (!owned.has(key)) {
|
|
5811
|
+
owned.set(key, { path, fromMs: ms });
|
|
5812
|
+
}
|
|
5813
|
+
}
|
|
5814
|
+
}
|
|
5815
|
+
return Array.from(owned.values());
|
|
5816
|
+
}
|
|
5817
|
+
/**
|
|
5818
|
+
* An Entity as it stands at an instant, with each value its simulated records have named by then laid over it.
|
|
5819
|
+
* @param entity the Entity as read for that instant, from the API or a simulated stand-in.
|
|
5820
|
+
* @param timeMs the instant in milliseconds.
|
|
5821
|
+
* @param attrKey the historic attribute key being followed, or null for any.
|
|
5822
|
+
*/
|
|
5823
|
+
ApplyAt(entity, timeMs, attrKey) {
|
|
5824
|
+
var _a, _b, _c;
|
|
5825
|
+
const id = (_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID;
|
|
5826
|
+
if (!id || timeMs == null || isNaN(timeMs)) {
|
|
5827
|
+
return entity;
|
|
5828
|
+
}
|
|
5829
|
+
// Laid oldest first, each record replacing only what it names, which leaves every value at its newest.
|
|
5830
|
+
let state = null;
|
|
5831
|
+
let stateMs = -Infinity;
|
|
5832
|
+
for (const record of this.GetRecords(id, attrKey)) {
|
|
5833
|
+
const ms = timeOf(record);
|
|
5834
|
+
if (ms > timeMs) {
|
|
5835
|
+
break;
|
|
5836
|
+
}
|
|
5837
|
+
const parts = {};
|
|
5838
|
+
for (const part of RECORD_PARTS) {
|
|
5839
|
+
if (record[part] != null) {
|
|
5840
|
+
parts[part] = record[part];
|
|
5841
|
+
}
|
|
5842
|
+
}
|
|
5843
|
+
state = state ? overlayRecord(state, parts) : parts;
|
|
5844
|
+
stateMs = ms;
|
|
5845
|
+
}
|
|
5846
|
+
if (!state) {
|
|
5847
|
+
return entity;
|
|
5848
|
+
}
|
|
5849
|
+
const bruce = { ...entity.Bruce };
|
|
5850
|
+
if (state.location) {
|
|
5851
|
+
bruce.Location = Overlay(bruce.Location, state.location);
|
|
5852
|
+
}
|
|
5853
|
+
if (state.transform) {
|
|
5854
|
+
bruce.Transform = Overlay(bruce.Transform, state.transform);
|
|
5855
|
+
}
|
|
5856
|
+
if (state.geometry) {
|
|
5857
|
+
bruce.VectorGeometry = state.geometry;
|
|
5858
|
+
}
|
|
5859
|
+
if (state.boundaries) {
|
|
5860
|
+
bruce.Boundaries = state.boundaries;
|
|
5861
|
+
}
|
|
5862
|
+
if (state.assemblyPosition) {
|
|
5863
|
+
bruce.AssemblyPosition = state.assemblyPosition;
|
|
5864
|
+
}
|
|
5865
|
+
// Says the newest record the Entity now reflects, the way a read at a point does.
|
|
5866
|
+
const outline = entity.Bruce.Outline || [];
|
|
5867
|
+
const readAt = outline.find(x => !!x.DateTime);
|
|
5868
|
+
const readMs = readAt ? new Date(readAt.DateTime).getTime() : -Infinity;
|
|
5869
|
+
const dated = {
|
|
5870
|
+
...(readAt || { "Source.Name": "Simulation", Editable: false, Kind: BModels.Entity.EOutlineKind.Attribute }),
|
|
5871
|
+
DateTime: new Date(Math.max(readMs, stateMs)).toISOString(),
|
|
5872
|
+
ForeignKey: (_b = readAt === null || readAt === void 0 ? void 0 : readAt.ForeignKey) !== null && _b !== void 0 ? _b : attrKey
|
|
5873
|
+
};
|
|
5874
|
+
bruce.Outline = [dated, ...outline.filter(x => x !== readAt)];
|
|
5875
|
+
const data = state.data ? Overlay({ ...entity, Bruce: undefined }, state.data) : entity;
|
|
5876
|
+
return { ...data, Bruce: Overlay(bruce, (_c = state.data) === null || _c === void 0 ? void 0 : _c.Bruce) };
|
|
5877
|
+
}
|
|
5878
|
+
/**
|
|
5879
|
+
* API records with the simulated ones joined in, for the named Entities or every simulated one.
|
|
5880
|
+
*/
|
|
5881
|
+
MergeRecords(recordsByIds, attrKey, entityIds) {
|
|
5882
|
+
if (this.IsEmpty) {
|
|
5883
|
+
return recordsByIds;
|
|
5884
|
+
}
|
|
5885
|
+
const ids = entityIds !== null && entityIds !== void 0 ? entityIds : this.GetRecordEntityIds();
|
|
5886
|
+
let merged = null;
|
|
5887
|
+
for (const id of ids) {
|
|
5888
|
+
const simulated = this.GetRecords(id, attrKey);
|
|
5889
|
+
if (!simulated.length) {
|
|
5890
|
+
continue;
|
|
5891
|
+
}
|
|
5892
|
+
const owned = this.ownedFrom(simulated);
|
|
5893
|
+
const byTime = new Map();
|
|
5894
|
+
for (const record of (recordsByIds === null || recordsByIds === void 0 ? void 0 : recordsByIds[id]) || []) {
|
|
5895
|
+
const ms = timeOf(record);
|
|
5896
|
+
const kept = withoutPaths(record, owned.filter(x => x.fromMs <= ms).map(x => x.path));
|
|
5897
|
+
if (hasContent(kept)) {
|
|
5898
|
+
byTime.set(ms, kept);
|
|
5899
|
+
}
|
|
5900
|
+
}
|
|
5901
|
+
for (const record of simulated) {
|
|
5902
|
+
const ms = timeOf(record);
|
|
5903
|
+
const held = byTime.get(ms);
|
|
5904
|
+
byTime.set(ms, held ? overlayRecord(held, record) : record);
|
|
5905
|
+
}
|
|
5906
|
+
merged = merged !== null && merged !== void 0 ? merged : { ...(recordsByIds || {}) };
|
|
5907
|
+
merged[id] = Array.from(byTime.entries()).sort(([a], [b]) => a - b).map(([, record]) => record);
|
|
5908
|
+
}
|
|
5909
|
+
return merged !== null && merged !== void 0 ? merged : recordsByIds;
|
|
5910
|
+
}
|
|
5911
|
+
}
|
|
5912
|
+
EntitySimulation.Store = Store;
|
|
5913
|
+
})(exports.EntitySimulation || (exports.EntitySimulation = {}));
|
|
5914
|
+
|
|
5484
5915
|
/**
|
|
5485
5916
|
* Returns cesium property's value.
|
|
5486
5917
|
* This will check if it's one that changes over time, or just a fixed value.
|
|
@@ -5532,6 +5963,8 @@
|
|
|
5532
5963
|
const PHOTO_CACHE_KEY = "_nextspaceLabelPhotos";
|
|
5533
5964
|
// Clearance around a model's box, as a share of its half-extent on each axis.
|
|
5534
5965
|
const MODEL_BOX_PADDING = 0.02;
|
|
5966
|
+
// How far a moving model's box may drift before it is moved, in metres.
|
|
5967
|
+
const MODEL_BOX_FOLLOW_TOLERANCE = 0.001;
|
|
5535
5968
|
// Share of each edge drawn out from its corners while the box is idle. Hovering draws the edges whole.
|
|
5536
5969
|
const MODEL_BOX_BRACKET = 0.2;
|
|
5537
5970
|
// Under this many pixels across, a box reads as a smudge and the ring takes over.
|
|
@@ -6420,6 +6853,22 @@
|
|
|
6420
6853
|
return centre && half && (axes === null || axes === void 0 ? void 0 : axes.every(Boolean)) ? { centre, axes, half } : null;
|
|
6421
6854
|
}
|
|
6422
6855
|
EntityLabel.ReadOrientedBox = ReadOrientedBox;
|
|
6856
|
+
/*
|
|
6857
|
+
* Whether a placement turns what it places. Scale alone does not count.
|
|
6858
|
+
*/
|
|
6859
|
+
function isRotated(matrix) {
|
|
6860
|
+
for (let column = 0; column < 3; column++) {
|
|
6861
|
+
const axis = [0, 1, 2].map(row => Number(matrix[row][column]));
|
|
6862
|
+
const length = Math.hypot(...axis);
|
|
6863
|
+
if (!(length > 0)) {
|
|
6864
|
+
return false;
|
|
6865
|
+
}
|
|
6866
|
+
if (axis.some((value, row) => Math.abs(value / length - Number(row === column)) > 1e-5)) {
|
|
6867
|
+
return true;
|
|
6868
|
+
}
|
|
6869
|
+
}
|
|
6870
|
+
return false;
|
|
6871
|
+
}
|
|
6423
6872
|
/*
|
|
6424
6873
|
* The eight padded world corners of an assembly Entity's geometry box, or null when the record cannot give one.
|
|
6425
6874
|
*
|
|
@@ -6438,6 +6887,10 @@
|
|
|
6438
6887
|
if ((matrix === null || matrix === void 0 ? void 0 : matrix.length) !== 4 || !matrix.every(row => (row === null || row === void 0 ? void 0 : row.length) === 4) || !Number.isFinite(+(root === null || root === void 0 ? void 0 : root.latitude)) || !Number.isFinite(+(root === null || root === void 0 ? void 0 : root.longitude))) {
|
|
6439
6888
|
return null;
|
|
6440
6889
|
}
|
|
6890
|
+
// A pure translation can hide a rotation baked into the vertices, which leaves the extents loose.
|
|
6891
|
+
if (!shape && !isRotated(matrix)) {
|
|
6892
|
+
return null;
|
|
6893
|
+
}
|
|
6441
6894
|
const pivot = ((_c = bruce.GeometryPivot) === null || _c === void 0 ? void 0 : _c.length) >= 3 ? bruce.GeometryPivot.slice(0, 3).map(Number) : [0, 0, 0];
|
|
6442
6895
|
// Without an oriented box the record's own extents are an axis-aligned box about the pivot.
|
|
6443
6896
|
const box = shape || { centre: pivot, axes: [[1, 0, 0], [0, 1, 0], [0, 0, 1]], half: half };
|
|
@@ -6728,6 +7181,7 @@
|
|
|
6728
7181
|
constructor() {
|
|
6729
7182
|
// Whether a box competes for space comes only from inputs (camera distance, hover, the pointer), never from
|
|
6730
7183
|
// how its label was laid out, so an animation moving a label cannot feed back into which boxes show.
|
|
7184
|
+
// corners is where the box is drawn this frame: stored, moved by the entry's motion.
|
|
6731
7185
|
this.entries = new Map();
|
|
6732
7186
|
this.groups = null;
|
|
6733
7187
|
// The hovered Entity. Its box is drawn on its own and whole, ahead of every other box.
|
|
@@ -6745,7 +7199,7 @@
|
|
|
6745
7199
|
Set(id, corners, entityId, inputs) {
|
|
6746
7200
|
const current = this.entries.get(id);
|
|
6747
7201
|
// A label rebuilt over the same box keeps its place and how far it has animated.
|
|
6748
|
-
if (corners && (current === null || current === void 0 ? void 0 : current.
|
|
7202
|
+
if (corners && (current === null || current === void 0 ? void 0 : current.stored) === corners && current.entityId === entityId) {
|
|
6749
7203
|
Object.assign(current, inputs);
|
|
6750
7204
|
return;
|
|
6751
7205
|
}
|
|
@@ -6753,17 +7207,41 @@
|
|
|
6753
7207
|
return;
|
|
6754
7208
|
}
|
|
6755
7209
|
if (corners) {
|
|
6756
|
-
this.entries.set(id, { ...inputs, corners, entityId, active: false, wanted: false });
|
|
7210
|
+
this.entries.set(id, { ...inputs, corners, stored: corners, entityId, active: false, wanted: false });
|
|
6757
7211
|
}
|
|
6758
7212
|
else
|
|
6759
7213
|
this.entries.delete(id);
|
|
6760
7214
|
this.since.delete(id);
|
|
7215
|
+
this.forget(id);
|
|
7216
|
+
}
|
|
7217
|
+
/*
|
|
7218
|
+
* Drops the groups and outlines a box was in, so they are worked out again from where it is now.
|
|
7219
|
+
*/
|
|
7220
|
+
forget(id) {
|
|
6761
7221
|
this.groups = null;
|
|
6762
7222
|
for (const [key, group] of this.outlines) {
|
|
6763
7223
|
if (group.members.includes(id))
|
|
6764
7224
|
this.outlines.delete(key);
|
|
6765
7225
|
}
|
|
6766
7226
|
}
|
|
7227
|
+
/*
|
|
7228
|
+
* Moves each box whose model moved since the last frame, keeping where it has got to in showing.
|
|
7229
|
+
*/
|
|
7230
|
+
follow() {
|
|
7231
|
+
var _a;
|
|
7232
|
+
for (const [id, entry] of this.entries) {
|
|
7233
|
+
const motion = (_a = entry.motion) === null || _a === void 0 ? void 0 : _a.call(entry);
|
|
7234
|
+
if (!motion) {
|
|
7235
|
+
continue;
|
|
7236
|
+
}
|
|
7237
|
+
const moved = entry.stored.map(corner => Cesium.Matrix4.multiplyByPoint(motion, corner, new Cesium.Cartesian3()));
|
|
7238
|
+
if (moved.every((corner, i) => Cesium.Cartesian3.equalsEpsilon(corner, entry.corners[i], 0, MODEL_BOX_FOLLOW_TOLERANCE))) {
|
|
7239
|
+
continue;
|
|
7240
|
+
}
|
|
7241
|
+
entry.corners = moved;
|
|
7242
|
+
this.forget(id);
|
|
7243
|
+
}
|
|
7244
|
+
}
|
|
6767
7245
|
/*
|
|
6768
7246
|
* The box drawn for a label this frame, or undefined when it has none or lost its place.
|
|
6769
7247
|
*/
|
|
@@ -6831,6 +7309,7 @@
|
|
|
6831
7309
|
*/
|
|
6832
7310
|
Resolve(viewer, pointer) {
|
|
6833
7311
|
var _a, _b, _c, _d;
|
|
7312
|
+
this.follow();
|
|
6834
7313
|
this.frames.clear();
|
|
6835
7314
|
this.hulls.clear();
|
|
6836
7315
|
this.drawn = [];
|
|
@@ -7253,9 +7732,16 @@
|
|
|
7253
7732
|
}
|
|
7254
7733
|
if (this.api) {
|
|
7255
7734
|
const rego = this.rego;
|
|
7735
|
+
const simulation = exports.EntitySimulation.Get(this.viewer);
|
|
7736
|
+
let simulated = null;
|
|
7737
|
+
if (simulation === null || simulation === void 0 ? void 0 : simulation.HasBlob(rego.entityId)) {
|
|
7738
|
+
// A stand-in is named as is; a patch is laid over the API's record first.
|
|
7739
|
+
const read = simulation.HasEntity(rego.entityId) ? null : (await BModels.Entity.Get({ api: this.api, entityId: rego.entityId, migrated: true })).entity;
|
|
7740
|
+
simulated = simulation.Resolve(rego.entityId, read);
|
|
7741
|
+
}
|
|
7256
7742
|
this.rego.name = await BModels.Entity.CalculateNameAsync({
|
|
7257
7743
|
api: this.api,
|
|
7258
|
-
entity:
|
|
7744
|
+
entity: simulated,
|
|
7259
7745
|
entityId: rego.entityId,
|
|
7260
7746
|
defaultToId: false
|
|
7261
7747
|
});
|
|
@@ -7284,6 +7770,21 @@
|
|
|
7284
7770
|
clearTimeout(this.loadTimeout);
|
|
7285
7771
|
this.loadTimeout = null;
|
|
7286
7772
|
}
|
|
7773
|
+
/*
|
|
7774
|
+
* Reads the label's Entity again and redraws it, eg. after what is simulated for it changed.
|
|
7775
|
+
*/
|
|
7776
|
+
Reload() {
|
|
7777
|
+
if (this.disposed) {
|
|
7778
|
+
return;
|
|
7779
|
+
}
|
|
7780
|
+
this.additionalTexts = this.options.text != null ? [] : null;
|
|
7781
|
+
this.loadedName = false;
|
|
7782
|
+
// The name came from whatever record the visual was drawn from, so it is worked out again too.
|
|
7783
|
+
if (this.rego) {
|
|
7784
|
+
this.rego.name = null;
|
|
7785
|
+
}
|
|
7786
|
+
this.createLabel();
|
|
7787
|
+
}
|
|
7287
7788
|
/*
|
|
7288
7789
|
* Rebuilds only when configuration changes; state refreshes must not recreate DOM every frame.
|
|
7289
7790
|
*/
|
|
@@ -7514,7 +8015,7 @@
|
|
|
7514
8015
|
}
|
|
7515
8016
|
clearTimeout(this.loadTimeout);
|
|
7516
8017
|
this.loadTimeout = setTimeout(async () => {
|
|
7517
|
-
var _a, _b, _c, _d, _e;
|
|
8018
|
+
var _a, _b, _c, _d, _e, _f;
|
|
7518
8019
|
if (this.disposed || this.counter != counter) {
|
|
7519
8020
|
return;
|
|
7520
8021
|
}
|
|
@@ -7541,16 +8042,19 @@
|
|
|
7541
8042
|
const attributes = (_a = this.options.attributes) !== null && _a !== void 0 ? _a : (_c = (_b = entityType === null || entityType === void 0 ? void 0 : entityType.VisualSettings) === null || _b === void 0 ? void 0 : _b.label) === null || _c === void 0 ? void 0 : _c.attributes;
|
|
7542
8043
|
// Read for its attributes, and for a photo when the label has no icon of its own.
|
|
7543
8044
|
if (attributes || !HasIcon(this.options)) {
|
|
7544
|
-
|
|
8045
|
+
// A simulated stand-in is labelled as is, and a patch is laid over the API's.
|
|
8046
|
+
const simulation = exports.EntitySimulation.Get(this.viewer);
|
|
8047
|
+
let entity = (_d = simulation === null || simulation === void 0 ? void 0 : simulation.GetEntity(this.rego.entityId)) !== null && _d !== void 0 ? _d : null;
|
|
7545
8048
|
try {
|
|
7546
|
-
entity = (await BModels.Entity.Get({
|
|
8049
|
+
entity = entity !== null && entity !== void 0 ? entity : (await BModels.Entity.Get({
|
|
7547
8050
|
api: this.api,
|
|
7548
8051
|
entityId: this.rego.entityId,
|
|
7549
|
-
scenario: ((
|
|
8052
|
+
scenario: ((_e = this.rego.outline) === null || _e === void 0 ? void 0 : _e.length) ? (_f = this.rego.outline.find(x => !!x.Scenario)) === null || _f === void 0 ? void 0 : _f.Scenario : null,
|
|
7550
8053
|
schemaId: this.rego.schema,
|
|
7551
8054
|
migrated: true,
|
|
7552
8055
|
expandAttachments: !HasIcon(this.options)
|
|
7553
8056
|
})).entity;
|
|
8057
|
+
entity = entity && simulation ? simulation.Resolve(this.rego.entityId, entity) : entity;
|
|
7554
8058
|
}
|
|
7555
8059
|
// Entity doesn't exist.
|
|
7556
8060
|
// Likely a graphic with an ID that points to no record.
|
|
@@ -7934,11 +8438,17 @@
|
|
|
7934
8438
|
// Past the wait a slow record no longer holds the label back; a box that comes later eases in.
|
|
7935
8439
|
setTimeout(settle, MODEL_BOX_WAIT);
|
|
7936
8440
|
this.modelBox.load.then(corners => {
|
|
8441
|
+
var _a, _b;
|
|
7937
8442
|
if (!corners || this.disposed || this.counter !== counter) {
|
|
7938
8443
|
return;
|
|
7939
8444
|
}
|
|
7940
8445
|
// A record that disagrees with where the model was found describes another placement, such as a moved tileset.
|
|
8446
|
+
// A part known to have moved from its record, eg. a joint turning, is judged where it has moved to.
|
|
8447
|
+
const motion = (_b = (_a = this.rego) === null || _a === void 0 ? void 0 : _a.motion) === null || _b === void 0 ? void 0 : _b.call(_a, this.viewer.clock.currentTime);
|
|
7941
8448
|
const centre = Cesium.Cartesian3.midpoint(corners[0], corners[7], new Cesium.Cartesian3());
|
|
8449
|
+
if (motion) {
|
|
8450
|
+
Cesium.Matrix4.multiplyByPoint(motion, centre, centre);
|
|
8451
|
+
}
|
|
7942
8452
|
if (Cesium.Cartesian3.distance(centre, found) > Cesium.Cartesian3.distance(corners[0], corners[7]) * 0.75 + 2) {
|
|
7943
8453
|
return;
|
|
7944
8454
|
}
|
|
@@ -7946,6 +8456,7 @@
|
|
|
7946
8456
|
// Let go a little further out than it is taken, so a camera resting at the limit does not flip it.
|
|
7947
8457
|
let near = false;
|
|
7948
8458
|
layer.modelBoxes.Set(this.id, corners, rego.entityId, {
|
|
8459
|
+
motion: rego.motion ? () => rego.motion(this.viewer.clock.currentTime) : undefined,
|
|
7949
8460
|
selected: () => { var _a, _b; return Boolean((_b = (_a = this.visualRegister) === null || _a === void 0 ? void 0 : _a.GetState({ entityId: rego.entityId, menuItemId: rego.menuItemId })) === null || _b === void 0 ? void 0 : _b.selected); },
|
|
7950
8461
|
hovered: () => { var _a, _b; return Boolean((_b = (_a = this.visualRegister) === null || _a === void 0 ? void 0 : _a.IsLabelHovered) === null || _b === void 0 ? void 0 : _b.call(_a, rego.entityId, rego.menuItemId)); },
|
|
7951
8462
|
near: () => {
|
|
@@ -8152,7 +8663,7 @@
|
|
|
8152
8663
|
viewportWidth: this.viewer.canvas.clientWidth,
|
|
8153
8664
|
id: this.id, entityKey: ((_x = this.rego) === null || _x === void 0 ? void 0 : _x.entityId) ? JSON.stringify([this.rego.entityId, this.rego.menuItemId || "", this.Transient && !this.Persistent ? "hover" : ""]) : undefined,
|
|
8154
8665
|
entityId: (_y = this.rego) === null || _y === void 0 ? void 0 : _y.entityId, group: this.groupId, x, y, width, height, anchorX, anchorY, depth, leaderLength, leaderRun, leaderSide, distance,
|
|
8155
|
-
iconRadius: icon === null || icon === void 0 ? void 0 : icon.radius, modelBox: boxed || undefined, boxBlend,
|
|
8666
|
+
iconRadius: icon === null || icon === void 0 ? void 0 : icon.radius, modelBox: boxed || undefined, boxBlend, follows: !pinned && Boolean(transientPivot || cursorMotion.target),
|
|
8156
8667
|
// Priority shows the hovered member individually without dragging its cluster along.
|
|
8157
8668
|
opacity: waiting ? 0 : opacity, priority: hover || cursorAmount > 0 || (state === null || state === void 0 ? void 0 : state.highlighted) ? 1 : 0, options: this.options, text: originalText, clusterBounds
|
|
8158
8669
|
};
|
|
@@ -8279,15 +8790,16 @@
|
|
|
8279
8790
|
const normalRadius = (!leader || (ring === null || ring === void 0 ? void 0 : ring.enabled) === false ? 0 : Math.max(frame.distance <= ((_l = ring === null || ring === void 0 ? void 0 : ring.nearDistance) !== null && _l !== void 0 ? _l : 100) ? ((_m = ring === null || ring === void 0 ? void 0 : ring.nearRadius) !== null && _m !== void 0 ? _m : 10) : 0, hovered ? ((_o = ring === null || ring === void 0 ? void 0 : ring.hoverRadius) !== null && _o !== void 0 ? _o : (this.Transient && !this.Persistent ? 15.4 : 22)) : 0));
|
|
8280
8791
|
const targetRadius = normalRadius + (15.4 - normalRadius) * cursorMotion.amount;
|
|
8281
8792
|
// Around a glyph the ring keeps the glyph's size and only fades its stroke in, so it never covers the icon.
|
|
8282
|
-
|
|
8283
|
-
const
|
|
8793
|
+
// A ring around the cursor marks nothing, so a riding label keeps only its leader.
|
|
8794
|
+
const targetOuter = frame.follows ? 0 : iconRing || targetRadius || 2.5;
|
|
8795
|
+
const targetInner = frame.follows ? 0 : iconRing ? (targetRadius > 0 ? iconRing - 2 : iconRing) : Math.max(0, targetRadius - 2);
|
|
8284
8796
|
const now = Date.now();
|
|
8285
8797
|
const duration = (_p = ring === null || ring === void 0 ? void 0 : ring.transitionDuration) !== null && _p !== void 0 ? _p : 200;
|
|
8286
8798
|
const sharedRing = this.Transient && !this.Persistent && this.hoverFade.to === 1 ? this.hoverRingState : undefined;
|
|
8287
8799
|
let ringAnimation = sharedRing ? sharedRing.animation : this.ringAnimation;
|
|
8288
8800
|
if (!ringAnimation) {
|
|
8289
|
-
const outer = iconRing || 2.5;
|
|
8290
|
-
const inner = iconRing;
|
|
8801
|
+
const outer = frame.follows ? 0 : iconRing || 2.5;
|
|
8802
|
+
const inner = frame.follows ? 0 : iconRing;
|
|
8291
8803
|
ringAnimation = { target: outer, targetInner: inner, start: now, outer, inner, fromOuter: outer, fromInner: inner };
|
|
8292
8804
|
}
|
|
8293
8805
|
const animateRing = () => {
|
|
@@ -8925,9 +9437,9 @@
|
|
|
8925
9437
|
* Reads the entities behind items that name one without carrying it, attachments included,
|
|
8926
9438
|
* so a label's default image needs no second request.
|
|
8927
9439
|
*/
|
|
8928
|
-
async function Hydrate(items, api, signal) {
|
|
9440
|
+
async function Hydrate(items, api, signal, simulation) {
|
|
8929
9441
|
var _a, _b;
|
|
8930
|
-
if (!api) {
|
|
9442
|
+
if (!api && !simulation) {
|
|
8931
9443
|
return items;
|
|
8932
9444
|
}
|
|
8933
9445
|
const hydrated = items.slice();
|
|
@@ -8938,12 +9450,15 @@
|
|
|
8938
9450
|
}
|
|
8939
9451
|
const chunk = missing.slice(start, start + HYDRATE_AT_ONCE);
|
|
8940
9452
|
try {
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
}
|
|
9453
|
+
// A simulated stand-in is labelled as is, never from the API, and a patch is laid over the API's.
|
|
9454
|
+
const asked = chunk.filter(at => !(simulation === null || simulation === void 0 ? void 0 : simulation.HasEntity(hydrated[at].entityId)));
|
|
9455
|
+
const { entities } = api && asked.length ? await BModels.Entity.GetListByIds({
|
|
9456
|
+
api, entityIds: asked.map(at => hydrated[at].entityId), expandAttachments: true, migrated: true
|
|
9457
|
+
}) : { entities: [] };
|
|
8944
9458
|
const found = new Map((entities !== null && entities !== void 0 ? entities : []).map(entity => { var _a; return [(_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID, entity]; }));
|
|
8945
9459
|
for (const at of chunk) {
|
|
8946
|
-
const
|
|
9460
|
+
const id = hydrated[at].entityId;
|
|
9461
|
+
const entity = simulation ? simulation.Resolve(id, found.get(id)) : found.get(id);
|
|
8947
9462
|
if (entity) {
|
|
8948
9463
|
// The API leaves Attachment out when there are none; an empty list records that they were read.
|
|
8949
9464
|
hydrated[at] = { ...hydrated[at], entity: { ...entity, Bruce: { ...entity.Bruce, Attachment: (_b = (_a = entity.Bruce) === null || _a === void 0 ? void 0 : _a.Attachment) !== null && _b !== void 0 ? _b : [] } } };
|
|
@@ -8960,7 +9475,7 @@
|
|
|
8960
9475
|
/*
|
|
8961
9476
|
* Uses the existing DataLab grammar, discarding actions and projections that cannot return Entities.
|
|
8962
9477
|
*/
|
|
8963
|
-
async function Loader(source, api, signal, hydrate) {
|
|
9478
|
+
async function Loader(source, api, signal, hydrate, simulation) {
|
|
8964
9479
|
var _a;
|
|
8965
9480
|
var _b;
|
|
8966
9481
|
if (source.type === "provider") {
|
|
@@ -8975,7 +9490,7 @@
|
|
|
8975
9490
|
return async (offset, limit, signal) => {
|
|
8976
9491
|
const page = items.slice(offset, offset + limit);
|
|
8977
9492
|
return {
|
|
8978
|
-
items: hydrate ? await Hydrate(page, api, signal) : page,
|
|
9493
|
+
items: hydrate ? await Hydrate(page, api, signal, simulation) : page,
|
|
8979
9494
|
hasMore: offset + limit < items.length
|
|
8980
9495
|
};
|
|
8981
9496
|
};
|
|
@@ -9117,7 +9632,7 @@
|
|
|
9117
9632
|
const items = new Map();
|
|
9118
9633
|
let truncated = false;
|
|
9119
9634
|
try {
|
|
9120
|
-
const load = await Loader(definition.source, (_g = this.register.ApiGetters) === null || _g === void 0 ? void 0 : _g.GetBruceApi(), controller.signal, NeedsEntityData(definition.options));
|
|
9635
|
+
const load = await Loader(definition.source, (_g = this.register.ApiGetters) === null || _g === void 0 ? void 0 : _g.GetBruceApi(), controller.signal, NeedsEntityData(definition.options), exports.EntitySimulation.Get(this.register.Viewer));
|
|
9121
9636
|
let offset = 0;
|
|
9122
9637
|
for (let pageNumber = 0;; pageNumber++) {
|
|
9123
9638
|
if (controller.signal.aborted || this.register.Disposed) {
|
|
@@ -9939,8 +10454,25 @@
|
|
|
9939
10454
|
return `${relation["Related.Entity.ID"]}-${relation["Principal.Entity.ID"]}-${relation["Relation.Type.ID"]}`;
|
|
9940
10455
|
}
|
|
9941
10456
|
RelationRenderEngine.GetRenderGroupId = GetRenderGroupId;
|
|
10457
|
+
/**
|
|
10458
|
+
* Removes and returns a menu item's drawn relations that end at, or carry data from, any of these Entities.
|
|
10459
|
+
*/
|
|
10460
|
+
function TakeRelationsTouching(params) {
|
|
10461
|
+
const { visualRegister, menuItemId } = params;
|
|
10462
|
+
const ids = new Set(params.entityIds);
|
|
10463
|
+
const touched = visualRegister.GetRegos({ menuItemId }).filter((rego) => {
|
|
10464
|
+
const relation = rego.relation;
|
|
10465
|
+
return relation && [relation["Principal.Entity.ID"], relation["Related.Entity.ID"], relation["Data.Entity.ID"]]
|
|
10466
|
+
.some(id => id && ids.has(id));
|
|
10467
|
+
});
|
|
10468
|
+
for (const rego of touched) {
|
|
10469
|
+
visualRegister.RemoveRegos({ relation: rego.relation, entityId: rego.entityId, menuItemId, requestRender: false });
|
|
10470
|
+
}
|
|
10471
|
+
return touched.map(rego => rego.relation);
|
|
10472
|
+
}
|
|
10473
|
+
RelationRenderEngine.TakeRelationsTouching = TakeRelationsTouching;
|
|
9942
10474
|
async function Render(params) {
|
|
9943
|
-
const { apiGetter, viewer, visualRegister, menuItemId, relations } = params;
|
|
10475
|
+
const { apiGetter, viewer, visualRegister, menuItemId, relations, simulation } = params;
|
|
9944
10476
|
const api = apiGetter.getApi(apiGetter.accountId);
|
|
9945
10477
|
const rendered = {};
|
|
9946
10478
|
let neededEntityIdsIds = [];
|
|
@@ -9958,13 +10490,15 @@
|
|
|
9958
10490
|
}
|
|
9959
10491
|
// Turn the set unique.
|
|
9960
10492
|
neededEntityIdsIds = neededEntityIdsIds.filter((v, i, a) => a.indexOf(v) === i);
|
|
9961
|
-
// Get all entities in one go.
|
|
9962
|
-
|
|
9963
|
-
|
|
10493
|
+
// Get all entities in one go, other than the simulated ones.
|
|
10494
|
+
const simulated = neededEntityIdsIds.map(id => simulation === null || simulation === void 0 ? void 0 : simulation.GetEntity(id)).filter(x => !!x);
|
|
10495
|
+
const apiIds = neededEntityIdsIds.filter(id => !(simulation === null || simulation === void 0 ? void 0 : simulation.HasEntity(id)));
|
|
10496
|
+
let entities = (apiIds.length ? (await BModels.Entity.GetListByIds({
|
|
10497
|
+
entityIds: apiIds,
|
|
9964
10498
|
api: apiGetter.getApi(),
|
|
9965
10499
|
migrated: true,
|
|
9966
10500
|
maxSearchTimeSec: 60 * 2
|
|
9967
|
-
})).entities;
|
|
10501
|
+
})).entities.map(entity => simulation ? simulation.Overlay(entity) : entity) : []).concat(simulated);
|
|
9968
10502
|
// Create a map for quick reference.
|
|
9969
10503
|
const entitiesMap = {};
|
|
9970
10504
|
for (let i = 0; i < entities.length; i++) {
|
|
@@ -10458,6 +10992,8 @@
|
|
|
10458
10992
|
rego.overrideShow != null ||
|
|
10459
10993
|
// Being dragged around right now, so its extent is changing faster than we could measure it.
|
|
10460
10994
|
rego.editing == true ||
|
|
10995
|
+
// Placed by the clock every frame, so no measurement of where it is stays true.
|
|
10996
|
+
rego.moving == true ||
|
|
10461
10997
|
// Part of a collection we did not create, eg. a geojson data source, which may have its own rules for
|
|
10462
10998
|
// what is in the scene. Our own per-menu-item data sources are fair game.
|
|
10463
10999
|
exports.VisualsRegister.IsForeignCollection(rego) ||
|
|
@@ -11292,6 +11828,7 @@
|
|
|
11292
11828
|
}
|
|
11293
11829
|
get OnLabelGroupsChanged() { return this.labelGroupsChanged; }
|
|
11294
11830
|
constructor(params) {
|
|
11831
|
+
var _a, _b;
|
|
11295
11832
|
this.id = BModels.ObjectUtils.UId();
|
|
11296
11833
|
this.disposed = false;
|
|
11297
11834
|
this.rego = {};
|
|
@@ -11331,6 +11868,8 @@
|
|
|
11331
11868
|
this.menuItemSources = new Map();
|
|
11332
11869
|
// Set when the data source order no longer matches the zIndexes seen, so it is re-applied once.
|
|
11333
11870
|
this.sourceOrderDirty = false;
|
|
11871
|
+
// Stops following what is simulated, so labels can read it again when it changes.
|
|
11872
|
+
this.simulationRemoval = null;
|
|
11334
11873
|
this.labelGroupsChanged = new BModels.BruceEvent();
|
|
11335
11874
|
this.hoverRingState = {};
|
|
11336
11875
|
this.retiringHoverLabels = new Map();
|
|
@@ -11365,6 +11904,33 @@
|
|
|
11365
11904
|
this.cameraCullerDispose = exports.VisualRegisterCuller.Monitor({
|
|
11366
11905
|
register: this
|
|
11367
11906
|
});
|
|
11907
|
+
this.simulationRemoval = (_b = (_a = exports.EntitySimulation.Get(this.viewer)) === null || _a === void 0 ? void 0 : _a.OnChanged.Subscribe((change) => this.reloadLabels(change.entityIds))) !== null && _b !== void 0 ? _b : null;
|
|
11908
|
+
}
|
|
11909
|
+
/*
|
|
11910
|
+
* Tells labels to read their Entity again, since they keep what they read when drawn.
|
|
11911
|
+
*/
|
|
11912
|
+
reloadLabels(entityIds) {
|
|
11913
|
+
var _a, _b, _c, _d;
|
|
11914
|
+
if (this.disposed) {
|
|
11915
|
+
return;
|
|
11916
|
+
}
|
|
11917
|
+
const groupIds = new Set();
|
|
11918
|
+
for (const entityId of entityIds) {
|
|
11919
|
+
for (const rego of this.GetRegos({ entityId })) {
|
|
11920
|
+
(_a = exports.EntityLabel.GetLabel({ rego })) === null || _a === void 0 ? void 0 : _a.Reload();
|
|
11921
|
+
}
|
|
11922
|
+
if (((_b = this.labelHover) === null || _b === void 0 ? void 0 : _b.entityId) === entityId) {
|
|
11923
|
+
(_c = this.hoverLabel) === null || _c === void 0 ? void 0 : _c.Reload();
|
|
11924
|
+
}
|
|
11925
|
+
const groupId = (_d = this.GetLabelGroupFor(entityId)) === null || _d === void 0 ? void 0 : _d.groupId;
|
|
11926
|
+
if (groupId) {
|
|
11927
|
+
groupIds.add(groupId);
|
|
11928
|
+
}
|
|
11929
|
+
}
|
|
11930
|
+
// A group may have read the Entity in with its items, so the group is read again as a whole.
|
|
11931
|
+
for (const groupId of groupIds) {
|
|
11932
|
+
this.RefreshLabelGroup(groupId).catch((e) => console.error(e));
|
|
11933
|
+
}
|
|
11368
11934
|
}
|
|
11369
11935
|
/**
|
|
11370
11936
|
* Returns the collection a menu item's visuals belong in, creating its data source on first use.
|
|
@@ -12267,7 +12833,9 @@
|
|
|
12267
12833
|
(_b = this.viewer.scene) === null || _b === void 0 ? void 0 : _b.requestRender();
|
|
12268
12834
|
}
|
|
12269
12835
|
Dispose() {
|
|
12270
|
-
var _a, _b, _c, _d, _e;
|
|
12836
|
+
var _a, _b, _c, _d, _e, _f;
|
|
12837
|
+
(_a = this.simulationRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
12838
|
+
this.simulationRemoval = null;
|
|
12271
12839
|
exports.CesiumEntityStyler.SetDimming(this.viewer, this.id, false);
|
|
12272
12840
|
this.ClearLabelHover();
|
|
12273
12841
|
clearTimeout(this.hoverRingReset);
|
|
@@ -12277,14 +12845,14 @@
|
|
|
12277
12845
|
exports.EntityLabel.Detatch({ label });
|
|
12278
12846
|
}
|
|
12279
12847
|
this.retiringHoverLabels.clear();
|
|
12280
|
-
(
|
|
12848
|
+
(_b = this.labelGroups) === null || _b === void 0 ? void 0 : _b.Dispose();
|
|
12281
12849
|
this.labelGroupsChanged.Clear();
|
|
12282
12850
|
for (const regos of Object.values(this.rego)) {
|
|
12283
12851
|
for (const rego of regos)
|
|
12284
12852
|
exports.EntityLabel.Detatch({ rego, force: true });
|
|
12285
12853
|
}
|
|
12286
12854
|
exports.EntityLabel.SetContainer(this.viewer);
|
|
12287
|
-
(
|
|
12855
|
+
(_c = this.cameraCullerDispose) === null || _c === void 0 ? void 0 : _c.call(this, {
|
|
12288
12856
|
uncull: false
|
|
12289
12857
|
});
|
|
12290
12858
|
this.cameraCullerDispose = null;
|
|
@@ -12300,7 +12868,7 @@
|
|
|
12300
12868
|
this.disposed = true;
|
|
12301
12869
|
for (const record of Array.from(this.menuItemSources.values())) {
|
|
12302
12870
|
try {
|
|
12303
|
-
(
|
|
12871
|
+
(_e = (_d = this.viewer) === null || _d === void 0 ? void 0 : _d.dataSources) === null || _e === void 0 ? void 0 : _e.remove(record.source, true);
|
|
12304
12872
|
}
|
|
12305
12873
|
catch (e) {
|
|
12306
12874
|
console.error(e);
|
|
@@ -12309,7 +12877,7 @@
|
|
|
12309
12877
|
this.menuItemSources.clear();
|
|
12310
12878
|
// Hand every tileset's show flag back to its owner, holding whatever the owner last asked for.
|
|
12311
12879
|
for (const rego of Array.from(this.tilesets.values())) {
|
|
12312
|
-
(
|
|
12880
|
+
(_f = rego.gate) === null || _f === void 0 ? void 0 : _f.Release();
|
|
12313
12881
|
}
|
|
12314
12882
|
this.tilesets.clear();
|
|
12315
12883
|
}
|
|
@@ -12483,21 +13051,7 @@
|
|
|
12483
13051
|
changed = true;
|
|
12484
13052
|
}
|
|
12485
13053
|
if (editing) {
|
|
12486
|
-
|
|
12487
|
-
entity: rego.visual
|
|
12488
|
-
});
|
|
12489
|
-
for (let k = 0; k < parts.length; k++) {
|
|
12490
|
-
const part = parts[k];
|
|
12491
|
-
if (!(part instanceof Cesium.Entity)) {
|
|
12492
|
-
continue;
|
|
12493
|
-
}
|
|
12494
|
-
const removalPending = exports.VisualRegisterCuller.CancelQueuedSceneChange(part);
|
|
12495
|
-
const wasCulled = part[exports.VisualRegisterCuller.VISUAL_CULL_KEY] == true || removalPending;
|
|
12496
|
-
delete part[exports.VisualRegisterCuller.VISUAL_CULL_KEY];
|
|
12497
|
-
if (wasCulled && !IsCEntityInScene(this.viewer, part)) {
|
|
12498
|
-
AddCEntityToScene(this.viewer, part, rego.collection);
|
|
12499
|
-
}
|
|
12500
|
-
}
|
|
13054
|
+
this.restoreCulled(rego);
|
|
12501
13055
|
}
|
|
12502
13056
|
else {
|
|
12503
13057
|
exports.VisualRegisterCuller.InvalidateBounds(rego.visual);
|
|
@@ -12509,6 +13063,56 @@
|
|
|
12509
13063
|
(_b = (_a = this.viewer) === null || _a === void 0 ? void 0 : _a.scene) === null || _b === void 0 ? void 0 : _b.requestRender();
|
|
12510
13064
|
}
|
|
12511
13065
|
}
|
|
13066
|
+
/**
|
|
13067
|
+
* Marks visuals as placed by the clock, exempting them from culling until it is turned back off.
|
|
13068
|
+
*/
|
|
13069
|
+
SetMoving(params) {
|
|
13070
|
+
const { entityIds, moving, menuItemId } = params;
|
|
13071
|
+
if (!(entityIds === null || entityIds === void 0 ? void 0 : entityIds.length)) {
|
|
13072
|
+
return;
|
|
13073
|
+
}
|
|
13074
|
+
for (let i = 0; i < entityIds.length; i++) {
|
|
13075
|
+
const regos = this.rego[entityIds[i]];
|
|
13076
|
+
if (!regos) {
|
|
13077
|
+
continue;
|
|
13078
|
+
}
|
|
13079
|
+
for (let j = 0; j < regos.length; j++) {
|
|
13080
|
+
const rego = regos[j];
|
|
13081
|
+
if ((menuItemId && rego.menuItemId !== menuItemId) || Boolean(rego.moving) == Boolean(moving)) {
|
|
13082
|
+
continue;
|
|
13083
|
+
}
|
|
13084
|
+
// Null rather than false, so the rego does not carry a key for every visual that ever moved.
|
|
13085
|
+
rego.moving = moving ? true : null;
|
|
13086
|
+
if (moving) {
|
|
13087
|
+
this.restoreCulled(rego);
|
|
13088
|
+
}
|
|
13089
|
+
else {
|
|
13090
|
+
exports.VisualRegisterCuller.InvalidateBounds(rego.visual);
|
|
13091
|
+
exports.VisualRegisterCuller.MarkShouldRecheck(this.viewer);
|
|
13092
|
+
}
|
|
13093
|
+
}
|
|
13094
|
+
}
|
|
13095
|
+
}
|
|
13096
|
+
/*
|
|
13097
|
+
* Puts back whatever the culler took out of a visual that is now exempt from it.
|
|
13098
|
+
*/
|
|
13099
|
+
restoreCulled(rego) {
|
|
13100
|
+
const parts = exports.EntityUtils.GatherEntity({
|
|
13101
|
+
entity: rego.visual
|
|
13102
|
+
});
|
|
13103
|
+
for (let k = 0; k < parts.length; k++) {
|
|
13104
|
+
const part = parts[k];
|
|
13105
|
+
if (!(part instanceof Cesium.Entity)) {
|
|
13106
|
+
continue;
|
|
13107
|
+
}
|
|
13108
|
+
const removalPending = exports.VisualRegisterCuller.CancelQueuedSceneChange(part);
|
|
13109
|
+
const wasCulled = part[exports.VisualRegisterCuller.VISUAL_CULL_KEY] == true || removalPending;
|
|
13110
|
+
delete part[exports.VisualRegisterCuller.VISUAL_CULL_KEY];
|
|
13111
|
+
if (wasCulled && !IsCEntityInScene(this.viewer, part)) {
|
|
13112
|
+
AddCEntityToScene(this.viewer, part, rego.collection);
|
|
13113
|
+
}
|
|
13114
|
+
}
|
|
13115
|
+
}
|
|
12512
13116
|
SetSelected(params) {
|
|
12513
13117
|
var _a;
|
|
12514
13118
|
let { entityIds, selected, refreshIfSelected, requestRender, menuItemId, source } = params;
|
|
@@ -14001,12 +14605,41 @@
|
|
|
14001
14605
|
* Tracks per-entity rerender state for unsaved blob maintenance and stale API response suppression.
|
|
14002
14606
|
*/
|
|
14003
14607
|
class EntityReRenderMaintainState {
|
|
14004
|
-
constructor() {
|
|
14608
|
+
constructor(simulation, pointInTime) {
|
|
14609
|
+
this.simulation = simulation;
|
|
14610
|
+
this.pointInTime = pointInTime;
|
|
14005
14611
|
this.maintainedEntitiesById = {};
|
|
14006
14612
|
this.apiRevisionByEntityId = {};
|
|
14007
14613
|
this.skipApiRunByEntityId = {};
|
|
14008
14614
|
this.checkRunId = 0;
|
|
14009
14615
|
}
|
|
14616
|
+
/*
|
|
14617
|
+
* Lays each Entity's simulated record for the current point over it, when the manager reads at one.
|
|
14618
|
+
*/
|
|
14619
|
+
atPoint(entities) {
|
|
14620
|
+
var _a;
|
|
14621
|
+
const point = (_a = this.pointInTime) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
14622
|
+
if (!point || !this.simulation || this.simulation.IsEmpty || !(entities === null || entities === void 0 ? void 0 : entities.length)) {
|
|
14623
|
+
return entities;
|
|
14624
|
+
}
|
|
14625
|
+
return entities.map((entity) => this.simulation.ApplyAt(entity, point.timeMs, point.attrKey));
|
|
14626
|
+
}
|
|
14627
|
+
/**
|
|
14628
|
+
* Whether a simulated stand-in replaces this Entity, so nothing from the API may replace what is drawn.
|
|
14629
|
+
*/
|
|
14630
|
+
isSimulated(entityId) {
|
|
14631
|
+
var _a;
|
|
14632
|
+
return Boolean(entityId && ((_a = this.simulation) === null || _a === void 0 ? void 0 : _a.HasEntity(entityId)));
|
|
14633
|
+
}
|
|
14634
|
+
/**
|
|
14635
|
+
* Swaps any simulated Entity in a set the API returned for its stand-in, or lays its patch over it.
|
|
14636
|
+
*/
|
|
14637
|
+
withSimulated(entities) {
|
|
14638
|
+
if (!this.simulation || this.simulation.IsEmpty || !(entities === null || entities === void 0 ? void 0 : entities.length)) {
|
|
14639
|
+
return entities;
|
|
14640
|
+
}
|
|
14641
|
+
return this.atPoint(entities.map((entity) => { var _a, _b; return (_b = this.simulation.Resolve((_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID, entity)) !== null && _b !== void 0 ? _b : entity; }));
|
|
14642
|
+
}
|
|
14010
14643
|
setMaintainedEntities(entities) {
|
|
14011
14644
|
var _a;
|
|
14012
14645
|
for (let i = 0; i < entities.length; i++) {
|
|
@@ -14028,15 +14661,21 @@
|
|
|
14028
14661
|
}
|
|
14029
14662
|
}
|
|
14030
14663
|
getMaintainedEntities(entityIds) {
|
|
14664
|
+
var _a;
|
|
14031
14665
|
const entities = [];
|
|
14032
14666
|
for (let i = 0; i < entityIds.length; i++) {
|
|
14033
14667
|
const id = entityIds[i];
|
|
14034
14668
|
if (!id) {
|
|
14035
14669
|
continue;
|
|
14036
14670
|
}
|
|
14037
|
-
|
|
14038
|
-
|
|
14039
|
-
|
|
14671
|
+
// A maintained blob is this manager's own unsaved edit, newer than a simulation, so it is drawn as is.
|
|
14672
|
+
const maintained = this.maintainedEntitiesById[id];
|
|
14673
|
+
const simulated = maintained ? null : (_a = this.simulation) === null || _a === void 0 ? void 0 : _a.GetEntity(id);
|
|
14674
|
+
if (maintained) {
|
|
14675
|
+
entities.push(maintained);
|
|
14676
|
+
}
|
|
14677
|
+
else if (simulated) {
|
|
14678
|
+
entities.push(this.atPoint([simulated])[0]);
|
|
14040
14679
|
}
|
|
14041
14680
|
}
|
|
14042
14681
|
return entities;
|
|
@@ -14093,7 +14732,7 @@
|
|
|
14093
14732
|
}
|
|
14094
14733
|
filterCurrentApiEntities(params) {
|
|
14095
14734
|
const { entities, revisions, runId } = params;
|
|
14096
|
-
|
|
14735
|
+
const current = entities.filter((entity) => {
|
|
14097
14736
|
var _a, _b, _c;
|
|
14098
14737
|
const id = (_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID;
|
|
14099
14738
|
if (!id) {
|
|
@@ -14102,8 +14741,12 @@
|
|
|
14102
14741
|
if (runId != null && this.shouldSkipInRun(id, runId)) {
|
|
14103
14742
|
return false;
|
|
14104
14743
|
}
|
|
14744
|
+
if (this.isSimulated(id)) {
|
|
14745
|
+
return false;
|
|
14746
|
+
}
|
|
14105
14747
|
return ((_b = this.apiRevisionByEntityId[id]) !== null && _b !== void 0 ? _b : 0) === ((_c = revisions[id]) !== null && _c !== void 0 ? _c : 0);
|
|
14106
14748
|
});
|
|
14749
|
+
return this.atPoint(this.simulation && !this.simulation.IsEmpty ? current.map((entity) => this.simulation.Overlay(entity)) : current);
|
|
14107
14750
|
}
|
|
14108
14751
|
}
|
|
14109
14752
|
|
|
@@ -15539,7 +16182,7 @@
|
|
|
15539
16182
|
this.cache = new HistoricRangeCache();
|
|
15540
16183
|
}
|
|
15541
16184
|
get Records() {
|
|
15542
|
-
return this.cache.GetRecords();
|
|
16185
|
+
return this.withSimulated(this.cache.GetRecords());
|
|
15543
16186
|
}
|
|
15544
16187
|
Clear() {
|
|
15545
16188
|
this.cache.Clear();
|
|
@@ -15563,8 +16206,9 @@
|
|
|
15563
16206
|
*/
|
|
15564
16207
|
async Gather(entities) {
|
|
15565
16208
|
var _a;
|
|
15566
|
-
|
|
15567
|
-
|
|
16209
|
+
const entityIds = entities === null || entities === void 0 ? void 0 : entities.map(x => { var _a; return (_a = x.Bruce) === null || _a === void 0 ? void 0 : _a.ID; }).filter(x => !!x);
|
|
16210
|
+
if (this.disposed || !(entities === null || entities === void 0 ? void 0 : entities.length) || this.params.simulatedOnly) {
|
|
16211
|
+
return { fetched: false, recordsByIds: this.withSimulated(this.cache.GetRecords(), entityIds) };
|
|
15568
16212
|
}
|
|
15569
16213
|
const { viewer } = this.params;
|
|
15570
16214
|
const windowStart = new Date(viewer.clock.startTime.toString()).getTime();
|
|
@@ -15584,7 +16228,7 @@
|
|
|
15584
16228
|
if (plan.coarse && !this.cache.CoversCoarse(plan.coarse.start, plan.coarse.stop)) {
|
|
15585
16229
|
const records = await this.request(plan.coarse.start, plan.coarse.stop, entities, plan.coarse.sample);
|
|
15586
16230
|
if (this.disposed) {
|
|
15587
|
-
return { fetched, recordsByIds: this.cache.GetRecords() };
|
|
16231
|
+
return { fetched, recordsByIds: this.withSimulated(this.cache.GetRecords(), entityIds) };
|
|
15588
16232
|
}
|
|
15589
16233
|
this.cache.Absorb(plan.coarse.start, this.holdTo(plan.coarse.stop, liveEdge, liveTail), records, true);
|
|
15590
16234
|
fetched = true;
|
|
@@ -15619,7 +16263,17 @@
|
|
|
15619
16263
|
}
|
|
15620
16264
|
}
|
|
15621
16265
|
}
|
|
15622
|
-
return { fetched, recordsByIds: this.cache.GetRecords() };
|
|
16266
|
+
return { fetched, recordsByIds: this.withSimulated(this.cache.GetRecords(), entityIds) };
|
|
16267
|
+
}
|
|
16268
|
+
/*
|
|
16269
|
+
* Joins the simulated records for these Entities, or every simulated Entity, into an answer.
|
|
16270
|
+
*/
|
|
16271
|
+
withSimulated(recordsByIds, entityIds) {
|
|
16272
|
+
const simulation = this.params.simulation;
|
|
16273
|
+
if (!simulation || simulation.IsEmpty) {
|
|
16274
|
+
return recordsByIds;
|
|
16275
|
+
}
|
|
16276
|
+
return simulation.MergeRecords(recordsByIds, this.params.getAttrKey(), entityIds);
|
|
15623
16277
|
}
|
|
15624
16278
|
/*
|
|
15625
16279
|
* Returns how much of a range that was just read may be marked as held.
|
|
@@ -15654,7 +16308,7 @@
|
|
|
15654
16308
|
}).then((res) => {
|
|
15655
16309
|
const records = res.recordsByIds || {};
|
|
15656
16310
|
if (!this.disposed && this.params.onRecords) {
|
|
15657
|
-
this.params.onRecords(records);
|
|
16311
|
+
this.params.onRecords(this.withSimulated(records, entityIds));
|
|
15658
16312
|
}
|
|
15659
16313
|
return records;
|
|
15660
16314
|
}).finally(() => {
|
|
@@ -15726,7 +16380,6 @@
|
|
|
15726
16380
|
this.renderQueue = [];
|
|
15727
16381
|
this.renderQueueInterval = null;
|
|
15728
16382
|
this.sources = [];
|
|
15729
|
-
this.reRenderState = new EntityReRenderMaintainState();
|
|
15730
16383
|
// Highly experimental flag to try improve rendering large sets of polygons and polylines.
|
|
15731
16384
|
// Many things are not supported when this is enabled.
|
|
15732
16385
|
this.useGeojson = false;
|
|
@@ -15746,6 +16399,14 @@
|
|
|
15746
16399
|
*/
|
|
15747
16400
|
this.renderAsIndividualsChain = Promise.resolve();
|
|
15748
16401
|
const { viewer, apiGetter, monitor, item, register: visualsManager, sharedGetters, onSeriesDiscovered } = params;
|
|
16402
|
+
this.reRenderState = new EntityReRenderMaintainState(params.simulation, () => {
|
|
16403
|
+
// Read at the scene's time only when the item follows history, the same choice its reads make.
|
|
16404
|
+
const bruceEntity = this.item.BruceEntity;
|
|
16405
|
+
if (!(bruceEntity === null || bruceEntity === void 0 ? void 0 : bruceEntity.historic) && !(bruceEntity === null || bruceEntity === void 0 ? void 0 : bruceEntity.historicAttrKey)) {
|
|
16406
|
+
return null;
|
|
16407
|
+
}
|
|
16408
|
+
return { timeMs: Cesium.JulianDate.toDate(this.viewer.clock.currentTime).getTime(), attrKey: bruceEntity.historicAttrKey };
|
|
16409
|
+
});
|
|
15749
16410
|
this.viewer = viewer;
|
|
15750
16411
|
this.sharedGetters = sharedGetters;
|
|
15751
16412
|
this.monitor = monitor;
|
|
@@ -15759,7 +16420,8 @@
|
|
|
15759
16420
|
getApi: () => this.apiGetter.getApi(),
|
|
15760
16421
|
getAttrKey: () => { var _a; return (_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historicAttrKey; },
|
|
15761
16422
|
// A second either side, to take in records sitting exactly on the boundary.
|
|
15762
|
-
paddingMs: 1000
|
|
16423
|
+
paddingMs: 1000,
|
|
16424
|
+
simulation: params.simulation
|
|
15763
16425
|
});
|
|
15764
16426
|
this.useGeojson = item.renderAsGeojson == true;
|
|
15765
16427
|
if (item.enableClustering) {
|
|
@@ -15940,8 +16602,9 @@
|
|
|
15940
16602
|
return;
|
|
15941
16603
|
}
|
|
15942
16604
|
this.updateAllowedTypeIDs(data.entityTypeSources, data.entities);
|
|
16605
|
+
// The getter answers from the API, so a simulated Entity in its answer is swapped for its blob.
|
|
15943
16606
|
if (isTagItem) {
|
|
15944
|
-
this.distributeForRender(data.entities.filter((entity) => {
|
|
16607
|
+
this.distributeForRender(this.reRenderState.withSimulated(data.entities.filter((entity) => {
|
|
15945
16608
|
let entityTags = entity.Bruce["Layer.ID"];
|
|
15946
16609
|
if (!entityTags) {
|
|
15947
16610
|
entityTags = [];
|
|
@@ -15949,10 +16612,10 @@
|
|
|
15949
16612
|
return entityTags.some((tag) => {
|
|
15950
16613
|
return tagsToRender.indexOf(tag) !== -1;
|
|
15951
16614
|
});
|
|
15952
|
-
}));
|
|
16615
|
+
})));
|
|
15953
16616
|
}
|
|
15954
16617
|
else {
|
|
15955
|
-
this.distributeForRender(data.entities);
|
|
16618
|
+
this.distributeForRender(this.reRenderState.withSimulated(data.entities));
|
|
15956
16619
|
}
|
|
15957
16620
|
});
|
|
15958
16621
|
// Cesium itself handles single-visibility ranges very well.
|
|
@@ -15992,6 +16655,10 @@
|
|
|
15992
16655
|
// Remove regos directly.
|
|
15993
16656
|
if (event.action === "D") {
|
|
15994
16657
|
for (const entityId of affectedIds) {
|
|
16658
|
+
// Deleted in the API, but drawn from its simulation until that is cleared.
|
|
16659
|
+
if (this.reRenderState.isSimulated(entityId)) {
|
|
16660
|
+
continue;
|
|
16661
|
+
}
|
|
15995
16662
|
this.visualsManager.RemoveRegos({
|
|
15996
16663
|
entityId,
|
|
15997
16664
|
menuItemId: this.item.id,
|
|
@@ -17115,7 +17782,6 @@
|
|
|
17115
17782
|
// Highly experimental flag to try improve rendering large sets of polygons and polylines.
|
|
17116
17783
|
// Many things are not supported when this is enabled.
|
|
17117
17784
|
this.useGeojson = false;
|
|
17118
|
-
this.reRenderState = new EntityReRenderMaintainState();
|
|
17119
17785
|
/**
|
|
17120
17786
|
* Our default path.
|
|
17121
17787
|
* We render each entity individually.
|
|
@@ -17125,6 +17791,7 @@
|
|
|
17125
17791
|
*/
|
|
17126
17792
|
this.renderAsIndividualsChain = Promise.resolve();
|
|
17127
17793
|
const { viewer, apiGetter, monitor, item, register: visualsManager } = params;
|
|
17794
|
+
this.reRenderState = new EntityReRenderMaintainState(params.simulation);
|
|
17128
17795
|
this.viewer = viewer;
|
|
17129
17796
|
this.apiGetter = apiGetter;
|
|
17130
17797
|
this.monitor = monitor;
|
|
@@ -17247,7 +17914,8 @@
|
|
|
17247
17914
|
continue;
|
|
17248
17915
|
}
|
|
17249
17916
|
}
|
|
17250
|
-
|
|
17917
|
+
// The item carries its own Entities, so a simulated one among them is swapped for its blob.
|
|
17918
|
+
this.onGetterUpdate(this.reRenderState.withSimulated(entities));
|
|
17251
17919
|
});
|
|
17252
17920
|
this.getter.Start();
|
|
17253
17921
|
}
|
|
@@ -17889,8 +18557,15 @@
|
|
|
17889
18557
|
this.renderQueueActive = false;
|
|
17890
18558
|
this.renderQueue = [];
|
|
17891
18559
|
this.renderQueueForceFlags = [];
|
|
17892
|
-
this.reRenderState = new EntityReRenderMaintainState();
|
|
17893
18560
|
const { viewer, apiGetter, monitor, item, register: visualsManager } = params;
|
|
18561
|
+
this.reRenderState = new EntityReRenderMaintainState(params.simulation, () => {
|
|
18562
|
+
// Read at the scene's time only when the item follows history, the same choice its reads make.
|
|
18563
|
+
const bruceEntity = this.item.BruceEntity;
|
|
18564
|
+
if (!(bruceEntity === null || bruceEntity === void 0 ? void 0 : bruceEntity.historic) && !(bruceEntity === null || bruceEntity === void 0 ? void 0 : bruceEntity.historicAttrKey)) {
|
|
18565
|
+
return null;
|
|
18566
|
+
}
|
|
18567
|
+
return { timeMs: Cesium.JulianDate.toDate(this.viewer.clock.currentTime).getTime(), attrKey: bruceEntity.historicAttrKey };
|
|
18568
|
+
});
|
|
17894
18569
|
this.sharedHistoric = params.sharedHistoric;
|
|
17895
18570
|
this.viewer = viewer;
|
|
17896
18571
|
this.apiGetter = apiGetter;
|
|
@@ -17903,7 +18578,8 @@
|
|
|
17903
18578
|
getApi: () => this.apiGetter.getApi(),
|
|
17904
18579
|
getAttrKey: () => { var _a; return (_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historicAttrKey; },
|
|
17905
18580
|
// Padding either side of a requested range, for clock desync between us and the server.
|
|
17906
|
-
paddingMs: 15000
|
|
18581
|
+
paddingMs: 15000,
|
|
18582
|
+
simulation: params.simulation
|
|
17907
18583
|
});
|
|
17908
18584
|
if (this.item.enableClustering) {
|
|
17909
18585
|
this.clustering = new PointClustering(visualsManager, this.item.id, (_a = this.item) === null || _a === void 0 ? void 0 : _a.clustering);
|
|
@@ -18361,9 +19037,9 @@
|
|
|
18361
19037
|
this.getter = null;
|
|
18362
19038
|
this.getterSub = null;
|
|
18363
19039
|
this.disposed = false;
|
|
18364
|
-
this.reRenderState = new EntityReRenderMaintainState();
|
|
18365
19040
|
this.renderEntityChain = Promise.resolve();
|
|
18366
19041
|
const { viewer, apiGetter, monitor, item, register: visualsManager } = params;
|
|
19042
|
+
this.reRenderState = new EntityReRenderMaintainState(params.simulation);
|
|
18367
19043
|
this.viewer = viewer;
|
|
18368
19044
|
this.apiGetter = apiGetter;
|
|
18369
19045
|
this.monitor = monitor;
|
|
@@ -18637,6 +19313,122 @@
|
|
|
18637
19313
|
];
|
|
18638
19314
|
})(CodeLogicExceptions || (CodeLogicExceptions = {}));
|
|
18639
19315
|
|
|
19316
|
+
/**
|
|
19317
|
+
* The line drawn through where a moving thing has been, from its history, as a whole path or a fading trail.
|
|
19318
|
+
*/
|
|
19319
|
+
var HistoricTrail;
|
|
19320
|
+
(function (HistoricTrail) {
|
|
19321
|
+
const BANDS = 12;
|
|
19322
|
+
const SAMPLES = 96;
|
|
19323
|
+
const WIDTH_FAR = 1;
|
|
19324
|
+
const ALPHA_FAR = 0.05;
|
|
19325
|
+
// Above 1, so width and alpha hold near the head and give way further back.
|
|
19326
|
+
const WIDTH_CURVE = 1.4;
|
|
19327
|
+
const ALPHA_CURVE = 1.25;
|
|
19328
|
+
// How strongly a line is drawn where it passes behind the globe, against its own alpha.
|
|
19329
|
+
const BEHIND_ALPHA = 0.35;
|
|
19330
|
+
/**
|
|
19331
|
+
* The trail length a menu item asks for in its historicTrackSeconds, or null for the whole path.
|
|
19332
|
+
*/
|
|
19333
|
+
function GetTrailSeconds(item) {
|
|
19334
|
+
const seconds = Number(item === null || item === void 0 ? void 0 : item.historicTrackSeconds);
|
|
19335
|
+
return seconds > 0 ? seconds : null;
|
|
19336
|
+
}
|
|
19337
|
+
HistoricTrail.GetTrailSeconds = GetTrailSeconds;
|
|
19338
|
+
class Trail {
|
|
19339
|
+
constructor(params) {
|
|
19340
|
+
this.entities = [];
|
|
19341
|
+
this.memoTime = null;
|
|
19342
|
+
this.memo = [];
|
|
19343
|
+
this.viewer = params.viewer;
|
|
19344
|
+
const trailing = params.trailSeconds > 0 && Boolean(params.positionAt);
|
|
19345
|
+
if (!trailing) {
|
|
19346
|
+
this.add(params, params.color, params.width, () => { var _a; return (_a = params.getSeries()) !== null && _a !== void 0 ? _a : []; });
|
|
19347
|
+
return;
|
|
19348
|
+
}
|
|
19349
|
+
for (let band = 0; band < BANDS; band++) {
|
|
19350
|
+
// 0 at the head, 1 at the far end.
|
|
19351
|
+
const age = band / (BANDS - 1);
|
|
19352
|
+
const width = params.width - (params.width - WIDTH_FAR) * Math.pow(age, WIDTH_CURVE);
|
|
19353
|
+
const alpha = params.color.alpha - (params.color.alpha - ALPHA_FAR) * Math.pow(age, ALPHA_CURVE);
|
|
19354
|
+
this.add(params, params.color.withAlpha(alpha), Math.max(WIDTH_FAR, width), (time) => {
|
|
19355
|
+
return this.slice(this.sample(params, time), band);
|
|
19356
|
+
});
|
|
19357
|
+
}
|
|
19358
|
+
}
|
|
19359
|
+
/*
|
|
19360
|
+
* One polyline of the trail, its positions read from the scene's time.
|
|
19361
|
+
*/
|
|
19362
|
+
add(params, color, width, positions) {
|
|
19363
|
+
const entity = this.viewer.entities.add({
|
|
19364
|
+
id: BModels.ObjectUtils.UId(15),
|
|
19365
|
+
polyline: {
|
|
19366
|
+
positions: new Cesium.CallbackProperty((time) => positions(time), false),
|
|
19367
|
+
width: width,
|
|
19368
|
+
material: color,
|
|
19369
|
+
clampToGround: params.clampToGround,
|
|
19370
|
+
// A path through the air follows no ellipsoid, so it is drawn as the straight runs it holds.
|
|
19371
|
+
arcType: params.clampToGround ? Cesium.ArcType.GEODESIC : Cesium.ArcType.NONE,
|
|
19372
|
+
// Drawn dimmer behind the globe rather than winking in and out against the terrain along the limb.
|
|
19373
|
+
depthFailMaterial: params.clampToGround ? undefined : color.withAlpha(color.alpha * BEHIND_ALPHA),
|
|
19374
|
+
zIndex: 0,
|
|
19375
|
+
classificationType: params.clampToGround ? Cesium.ClassificationType.BOTH : undefined
|
|
19376
|
+
}
|
|
19377
|
+
});
|
|
19378
|
+
entity._noEdit = true;
|
|
19379
|
+
entity._noSelect = true;
|
|
19380
|
+
exports.CesiumEntityStyler.SetDefaultColor({
|
|
19381
|
+
color: color,
|
|
19382
|
+
entity: entity,
|
|
19383
|
+
viewer: this.viewer,
|
|
19384
|
+
override: true,
|
|
19385
|
+
requestRender: false
|
|
19386
|
+
});
|
|
19387
|
+
this.entities.push(entity);
|
|
19388
|
+
}
|
|
19389
|
+
/*
|
|
19390
|
+
* The trail's positions at a time, newest last, kept for that time so every band reads the same set.
|
|
19391
|
+
*/
|
|
19392
|
+
sample(params, time) {
|
|
19393
|
+
var _a;
|
|
19394
|
+
if (this.memoTime && Cesium.JulianDate.equals(this.memoTime, time)) {
|
|
19395
|
+
return this.memo;
|
|
19396
|
+
}
|
|
19397
|
+
this.memoTime = Cesium.JulianDate.clone(time, (_a = this.memoTime) !== null && _a !== void 0 ? _a : new Cesium.JulianDate());
|
|
19398
|
+
const nowMs = Cesium.JulianDate.toDate(time).getTime();
|
|
19399
|
+
const spanMs = params.trailSeconds * 1000;
|
|
19400
|
+
const points = [];
|
|
19401
|
+
for (let i = 0; i <= SAMPLES; i++) {
|
|
19402
|
+
const pos3d = params.positionAt(nowMs - spanMs + spanMs * i / SAMPLES);
|
|
19403
|
+
if (pos3d) {
|
|
19404
|
+
points.push(pos3d);
|
|
19405
|
+
}
|
|
19406
|
+
}
|
|
19407
|
+
this.memo = points;
|
|
19408
|
+
return points;
|
|
19409
|
+
}
|
|
19410
|
+
/*
|
|
19411
|
+
* A band's run of the samples, counted back from the head, sharing its end point with the next band.
|
|
19412
|
+
*/
|
|
19413
|
+
slice(points, band) {
|
|
19414
|
+
if (points.length < 2) {
|
|
19415
|
+
return [];
|
|
19416
|
+
}
|
|
19417
|
+
const per = (points.length - 1) / BANDS;
|
|
19418
|
+
const end = points.length - 1 - Math.round(band * per);
|
|
19419
|
+
const start = points.length - 1 - Math.round((band + 1) * per);
|
|
19420
|
+
return start < end ? points.slice(Math.max(0, start), end + 1) : [];
|
|
19421
|
+
}
|
|
19422
|
+
Dispose() {
|
|
19423
|
+
for (const entity of this.entities) {
|
|
19424
|
+
RemoveCEntityFromScene(this.viewer, entity);
|
|
19425
|
+
}
|
|
19426
|
+
this.entities = [];
|
|
19427
|
+
}
|
|
19428
|
+
}
|
|
19429
|
+
HistoricTrail.Trail = Trail;
|
|
19430
|
+
})(HistoricTrail || (HistoricTrail = {}));
|
|
19431
|
+
|
|
18640
19432
|
class EntityGatherer {
|
|
18641
19433
|
get OnQueueProgress() {
|
|
18642
19434
|
if (!this._onQueueProgress) {
|
|
@@ -18644,7 +19436,11 @@
|
|
|
18644
19436
|
}
|
|
18645
19437
|
return this._onQueueProgress;
|
|
18646
19438
|
}
|
|
18647
|
-
|
|
19439
|
+
/**
|
|
19440
|
+
* @param simulation what callers simulate in place of API data, used where this gatherer has no override.
|
|
19441
|
+
*/
|
|
19442
|
+
constructor(api, viewer, emitEntities, simulation) {
|
|
19443
|
+
this.simulation = simulation;
|
|
18648
19444
|
this.disposed = false;
|
|
18649
19445
|
this.expandSources = false;
|
|
18650
19446
|
this.historic = null;
|
|
@@ -19221,15 +20017,19 @@
|
|
|
19221
20017
|
}
|
|
19222
20018
|
}
|
|
19223
20019
|
}
|
|
20020
|
+
// A historic read is as at the scene's time, and so is any simulated record laid over it.
|
|
20021
|
+
const rTimeMs = new Date(rTime).getTime();
|
|
20022
|
+
const atPoint = (entity) => this.historic && this.simulation ? this.simulation.ApplyAt(entity, rTimeMs) : entity;
|
|
19224
20023
|
for (const entityId of requestedBatch || []) {
|
|
20024
|
+
// A caller's own override is an unsaved edit, newer than any simulation, so it is used as is.
|
|
19225
20025
|
const override = this.entityDataOverrides.get(entityId);
|
|
19226
20026
|
if (override) {
|
|
19227
20027
|
resolvedEntities.push(override);
|
|
19228
20028
|
continue;
|
|
19229
20029
|
}
|
|
19230
|
-
const found = foundById.get(entityId);
|
|
20030
|
+
const found = this.simulation ? this.simulation.Resolve(entityId, foundById.get(entityId)) : foundById.get(entityId);
|
|
19231
20031
|
if (found) {
|
|
19232
|
-
resolvedEntities.push(found);
|
|
20032
|
+
resolvedEntities.push(atPoint(found));
|
|
19233
20033
|
continue;
|
|
19234
20034
|
}
|
|
19235
20035
|
resolvedEntities.push(this.createFallbackEntity(entityId));
|
|
@@ -19593,7 +20393,7 @@
|
|
|
19593
20393
|
this.styleTilesetFeatureFullData(feature, entity, eTags);
|
|
19594
20394
|
}
|
|
19595
20395
|
}
|
|
19596
|
-
});
|
|
20396
|
+
}, params.simulation);
|
|
19597
20397
|
// Only Entities Sets bake coordinates into their features, so for any other source this
|
|
19598
20398
|
// resolves nothing and the gather stays in insertion order.
|
|
19599
20399
|
this.entityGatherer.SetViewMonitor(this.monitor);
|
|
@@ -21117,6 +21917,9 @@
|
|
|
21117
21917
|
return this.modelTreeUpdate;
|
|
21118
21918
|
}
|
|
21119
21919
|
constructor(params) {
|
|
21920
|
+
this.simulationRemoval = null;
|
|
21921
|
+
// Where the tileset stands by its own record, so a cleared simulation puts it back without a refetch.
|
|
21922
|
+
this.restCoords = null;
|
|
21120
21923
|
this.disposed = false;
|
|
21121
21924
|
this.modelSpace = false;
|
|
21122
21925
|
this.cTileset = null;
|
|
@@ -21154,7 +21957,7 @@
|
|
|
21154
21957
|
this.historicAnimation = null;
|
|
21155
21958
|
this.historicGatherer = null;
|
|
21156
21959
|
// The trail through the root's own movement, which describes the whole assembly.
|
|
21157
|
-
this.
|
|
21960
|
+
this.trail = null;
|
|
21158
21961
|
this.historicPossesLoadingProm = null;
|
|
21159
21962
|
// Queue of loaded in features that we haven't processed yet.
|
|
21160
21963
|
this.featureQueue = [];
|
|
@@ -21168,6 +21971,7 @@
|
|
|
21168
21971
|
this.featurePropCache = new Map();
|
|
21169
21972
|
this.featurePropsChecked = 0;
|
|
21170
21973
|
const { viewer, register: visualsManager, getters, item, modelSpace, initQueue } = params;
|
|
21974
|
+
this.simulation = params.simulation;
|
|
21171
21975
|
this.viewer = viewer;
|
|
21172
21976
|
this.getters = getters;
|
|
21173
21977
|
this.item = item;
|
|
@@ -21332,7 +22136,7 @@
|
|
|
21332
22136
|
historicPoint: this.viewer.clock.currentTime.toString(),
|
|
21333
22137
|
api: api
|
|
21334
22138
|
});
|
|
21335
|
-
coords = coordsFromHistoricEntity(root);
|
|
22139
|
+
coords = coordsFromHistoricEntity(this.atClock(root));
|
|
21336
22140
|
}
|
|
21337
22141
|
catch (e) {
|
|
21338
22142
|
console.error(e);
|
|
@@ -21373,6 +22177,10 @@
|
|
|
21373
22177
|
this.doDispose();
|
|
21374
22178
|
return;
|
|
21375
22179
|
}
|
|
22180
|
+
this.restCoords = coords;
|
|
22181
|
+
if (this.simulatedRootCoords()) {
|
|
22182
|
+
this.applyCoords(tileset, this.simulatedRootCoords());
|
|
22183
|
+
}
|
|
21376
22184
|
this.viewer.scene.requestRender();
|
|
21377
22185
|
exports.TilesetRenderEngine.OnTilesetReady(this.cTileset).then(() => {
|
|
21378
22186
|
var _a, _b, _c;
|
|
@@ -21392,6 +22200,7 @@
|
|
|
21392
22200
|
}
|
|
21393
22201
|
this.onCTilesetLoad();
|
|
21394
22202
|
this.styler.Init({
|
|
22203
|
+
simulation: this.simulation,
|
|
21395
22204
|
viewer: this.viewer,
|
|
21396
22205
|
// Default API instead of the account one.
|
|
21397
22206
|
// Records are driven by the account loading it instead of the source account.
|
|
@@ -21407,6 +22216,7 @@
|
|
|
21407
22216
|
});
|
|
21408
22217
|
this.viewer.scene.requestRender();
|
|
21409
22218
|
// Monitor scene time changes and update the root Entity.
|
|
22219
|
+
this.followSimulation(tileset);
|
|
21410
22220
|
this.viewerDateTimeSub(tileset);
|
|
21411
22221
|
}
|
|
21412
22222
|
catch (e) {
|
|
@@ -21462,7 +22272,7 @@
|
|
|
21462
22272
|
*/
|
|
21463
22273
|
applyRootTrack() {
|
|
21464
22274
|
var _a;
|
|
21465
|
-
if (!this.item.historicDrawTrack || !this.historicAnimation || this.
|
|
22275
|
+
if (!this.item.historicDrawTrack || !this.historicAnimation || this.trail) {
|
|
21466
22276
|
return;
|
|
21467
22277
|
}
|
|
21468
22278
|
const trackStyle = exports.EntityRenderEngine.GetTrackStyle({
|
|
@@ -21472,26 +22282,14 @@
|
|
|
21472
22282
|
if (!trackStyle) {
|
|
21473
22283
|
return;
|
|
21474
22284
|
}
|
|
21475
|
-
this
|
|
21476
|
-
|
|
21477
|
-
polyline: {
|
|
21478
|
-
positions: new Cesium.CallbackProperty(() => { var _a, _b; return (_b = (_a = this.historicAnimation) === null || _a === void 0 ? void 0 : _a.GetSeries()) !== null && _b !== void 0 ? _b : []; }, false),
|
|
21479
|
-
width: trackStyle.width,
|
|
21480
|
-
material: trackStyle.color,
|
|
21481
|
-
clampToGround: exports.EntityRenderEngine.GetHeightRefPolyline(Manager.GetRecordHeightRef(this.item))
|
|
21482
|
-
== Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
21483
|
-
zIndex: 0,
|
|
21484
|
-
classificationType: Cesium.ClassificationType.BOTH
|
|
21485
|
-
}
|
|
21486
|
-
});
|
|
21487
|
-
this.trackEntity._noEdit = true;
|
|
21488
|
-
this.trackEntity._noSelect = true;
|
|
21489
|
-
exports.CesiumEntityStyler.SetDefaultColor({
|
|
21490
|
-
color: trackStyle.color,
|
|
21491
|
-
entity: this.trackEntity,
|
|
22285
|
+
// The series has no position at an arbitrary time, so this draws the whole path rather than a trail.
|
|
22286
|
+
this.trail = new HistoricTrail.Trail({
|
|
21492
22287
|
viewer: this.viewer,
|
|
21493
|
-
|
|
21494
|
-
|
|
22288
|
+
color: trackStyle.color,
|
|
22289
|
+
width: trackStyle.width,
|
|
22290
|
+
clampToGround: exports.EntityRenderEngine.GetHeightRefPolyline(Manager.GetRecordHeightRef(this.item))
|
|
22291
|
+
== Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
22292
|
+
getSeries: () => { var _a, _b; return (_b = (_a = this.historicAnimation) === null || _a === void 0 ? void 0 : _a.GetSeries()) !== null && _b !== void 0 ? _b : []; }
|
|
21495
22293
|
});
|
|
21496
22294
|
}
|
|
21497
22295
|
/**
|
|
@@ -21544,7 +22342,7 @@
|
|
|
21544
22342
|
}
|
|
21545
22343
|
// Returns null (fall back to a normal Dispose()) if the tileset hasn't finished loading yet.
|
|
21546
22344
|
PrepareHandoff() {
|
|
21547
|
-
var _a, _b, _c, _d;
|
|
22345
|
+
var _a, _b, _c, _d, _e;
|
|
21548
22346
|
if (this.disposed || !this.cTileset || this.cTileset.isDestroyed()) {
|
|
21549
22347
|
return null;
|
|
21550
22348
|
}
|
|
@@ -21562,9 +22360,11 @@
|
|
|
21562
22360
|
this.handoffSourceMenuItemId = null;
|
|
21563
22361
|
// Not transferred, the receiving manager sets up its own fresh copy if needed.
|
|
21564
22362
|
this.viewerDateTimeDispose();
|
|
21565
|
-
(_b = this.
|
|
21566
|
-
|
|
21567
|
-
(
|
|
22363
|
+
(_b = this.simulationRemoval) === null || _b === void 0 ? void 0 : _b.call(this);
|
|
22364
|
+
this.simulationRemoval = null;
|
|
22365
|
+
(_c = this.styler) === null || _c === void 0 ? void 0 : _c.Dispose();
|
|
22366
|
+
(_d = this.tileLoadRemoval) === null || _d === void 0 ? void 0 : _d.call(this);
|
|
22367
|
+
(_e = this.tileUnloadRemoval) === null || _e === void 0 ? void 0 : _e.call(this);
|
|
21568
22368
|
this.tileLoadRemoval = null;
|
|
21569
22369
|
this.tileUnloadRemoval = null;
|
|
21570
22370
|
const payload = {
|
|
@@ -21585,7 +22385,7 @@
|
|
|
21585
22385
|
// Takes ownership of a PrepareHandoff() payload instead of loading via Init().
|
|
21586
22386
|
// Then re-parents every already-registered rego to this menu item and re-runs styling.
|
|
21587
22387
|
AdoptHandoff(payload) {
|
|
21588
|
-
var _a, _b, _c, _d;
|
|
22388
|
+
var _a, _b, _c, _d, _e;
|
|
21589
22389
|
this.cTileset = payload.cTileset;
|
|
21590
22390
|
this.tileset = payload.tileset;
|
|
21591
22391
|
this.rootId = payload.rootId;
|
|
@@ -21616,6 +22416,7 @@
|
|
|
21616
22416
|
// Same visuals, same registrations so just re-keyed onto this menu item.
|
|
21617
22417
|
this.onCTilesetLoad();
|
|
21618
22418
|
this.styler.Init({
|
|
22419
|
+
simulation: this.simulation,
|
|
21619
22420
|
viewer: this.viewer,
|
|
21620
22421
|
api: this.getters.GetBruceApi(),
|
|
21621
22422
|
cTileset: this.cTileset,
|
|
@@ -21649,7 +22450,9 @@
|
|
|
21649
22450
|
}
|
|
21650
22451
|
});
|
|
21651
22452
|
this.viewer.scene.requestRender();
|
|
21652
|
-
if (
|
|
22453
|
+
if (this.tileset) {
|
|
22454
|
+
this.restCoords = (_d = this.restCoords) !== null && _d !== void 0 ? _d : (_e = this.cTileset) === null || _e === void 0 ? void 0 : _e._bruceCoords;
|
|
22455
|
+
this.followSimulation(this.tileset);
|
|
21653
22456
|
this.viewerDateTimeSub(this.tileset);
|
|
21654
22457
|
}
|
|
21655
22458
|
}
|
|
@@ -22237,7 +23040,7 @@
|
|
|
22237
23040
|
this.doDispose();
|
|
22238
23041
|
}
|
|
22239
23042
|
doDispose() {
|
|
22240
|
-
var _a, _b, _c;
|
|
23043
|
+
var _a, _b, _c, _d;
|
|
22241
23044
|
if (this.featureQueueInterval) {
|
|
22242
23045
|
clearInterval(this.featureQueueInterval);
|
|
22243
23046
|
this.featureQueueInterval = null;
|
|
@@ -22272,6 +23075,8 @@
|
|
|
22272
23075
|
this.handoffSourceMenuItemId = null;
|
|
22273
23076
|
}
|
|
22274
23077
|
this.viewerDateTimeDispose();
|
|
23078
|
+
(_d = this.simulationRemoval) === null || _d === void 0 ? void 0 : _d.call(this);
|
|
23079
|
+
this.simulationRemoval = null;
|
|
22275
23080
|
}
|
|
22276
23081
|
async ReRender(params) {
|
|
22277
23082
|
let { entityIds, force, entities, maintain } = params;
|
|
@@ -22297,8 +23102,11 @@
|
|
|
22297
23102
|
* @param tileset
|
|
22298
23103
|
*/
|
|
22299
23104
|
viewerDateTimeSub(tileset) {
|
|
22300
|
-
var _a;
|
|
22301
|
-
|
|
23105
|
+
var _a, _b;
|
|
23106
|
+
// An item that is not historic still follows the root's simulated records, once there are some.
|
|
23107
|
+
const historic = Boolean((_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historic);
|
|
23108
|
+
const simulated = Boolean(this.rootId && ((_b = this.simulation) === null || _b === void 0 ? void 0 : _b.GetRecords(this.rootId).length));
|
|
23109
|
+
if ((!historic && !simulated) || this.historicGatherer || this.viewerDateTimeChangeRemoval) {
|
|
22302
23110
|
return;
|
|
22303
23111
|
}
|
|
22304
23112
|
const accountId = this.accountIdForRecords();
|
|
@@ -22327,6 +23135,8 @@
|
|
|
22327
23135
|
// Time padding in milliseconds (15 seconds).
|
|
22328
23136
|
// Helps account for desync between client and server.
|
|
22329
23137
|
paddingMs: 15000,
|
|
23138
|
+
simulation: this.simulation,
|
|
23139
|
+
simulatedOnly: !historic,
|
|
22330
23140
|
onRecords: (recordsByIds) => {
|
|
22331
23141
|
var _a;
|
|
22332
23142
|
const records = recordsByIds[this.rootId];
|
|
@@ -22354,6 +23164,7 @@
|
|
|
22354
23164
|
*/
|
|
22355
23165
|
const getInitialPosses = async () => {
|
|
22356
23166
|
this.historicPossesLoadingProm = new Promise(async (res) => {
|
|
23167
|
+
var _a;
|
|
22357
23168
|
try {
|
|
22358
23169
|
if (this.disposed) {
|
|
22359
23170
|
res(false);
|
|
@@ -22366,11 +23177,16 @@
|
|
|
22366
23177
|
return;
|
|
22367
23178
|
}
|
|
22368
23179
|
// Also request the current position in case its not in the range.
|
|
22369
|
-
|
|
23180
|
+
// A simulated root, or an item that is not historic, has no current record to ask the API for.
|
|
23181
|
+
if (!historic || ((_a = this.simulation) === null || _a === void 0 ? void 0 : _a.HasEntity(this.rootId))) {
|
|
23182
|
+
res(positions);
|
|
23183
|
+
return;
|
|
23184
|
+
}
|
|
23185
|
+
const entity = this.atClock((await BModels.Entity.Get({
|
|
22370
23186
|
entityId: this.rootId,
|
|
22371
23187
|
historicPoint: this.viewer.clock.currentTime.toString(),
|
|
22372
23188
|
api: api
|
|
22373
|
-
});
|
|
23189
|
+
})).entity);
|
|
22374
23190
|
if (this.disposed || !entity) {
|
|
22375
23191
|
res(false);
|
|
22376
23192
|
return;
|
|
@@ -22450,14 +23266,15 @@
|
|
|
22450
23266
|
}
|
|
22451
23267
|
});
|
|
22452
23268
|
this.viewerDateTimeChangeRemoval = () => {
|
|
23269
|
+
var _a, _b;
|
|
22453
23270
|
if (this.historicAnimation) {
|
|
22454
23271
|
this.historicAnimation.stop();
|
|
22455
23272
|
this.historicAnimation = null;
|
|
22456
23273
|
}
|
|
22457
|
-
|
|
22458
|
-
|
|
22459
|
-
|
|
22460
|
-
|
|
23274
|
+
(_a = this.historicGatherer) === null || _a === void 0 ? void 0 : _a.Dispose();
|
|
23275
|
+
this.historicGatherer = null;
|
|
23276
|
+
(_b = this.trail) === null || _b === void 0 ? void 0 : _b.Dispose();
|
|
23277
|
+
this.trail = null;
|
|
22461
23278
|
if (clockTickRemoval) {
|
|
22462
23279
|
clockTickRemoval();
|
|
22463
23280
|
clockTickRemoval = null;
|
|
@@ -22465,10 +23282,84 @@
|
|
|
22465
23282
|
};
|
|
22466
23283
|
});
|
|
22467
23284
|
}
|
|
22468
|
-
|
|
23285
|
+
/*
|
|
23286
|
+
* Keeps the tileset in line with what is simulated for its root Entity.
|
|
23287
|
+
*/
|
|
23288
|
+
followSimulation(tileset) {
|
|
23289
|
+
if (this.simulationRemoval || !this.simulation) {
|
|
23290
|
+
return;
|
|
23291
|
+
}
|
|
23292
|
+
this.simulationRemoval = this.simulation.OnChanged.Subscribe((change) => {
|
|
23293
|
+
var _a;
|
|
23294
|
+
if (this.disposed || !this.rootId || !change.entityIds.includes(this.rootId)) {
|
|
23295
|
+
return;
|
|
23296
|
+
}
|
|
23297
|
+
if (change.records) {
|
|
23298
|
+
this.regatherRoot(tileset);
|
|
23299
|
+
}
|
|
23300
|
+
// A simulated root is where the tileset stands outside its history; recorded positions still win.
|
|
23301
|
+
if (change.entities && !((_a = this.historicAnimation) === null || _a === void 0 ? void 0 : _a.getDateRange().minDate)) {
|
|
23302
|
+
this.applyRootRest(tileset);
|
|
23303
|
+
}
|
|
23304
|
+
});
|
|
23305
|
+
}
|
|
23306
|
+
/*
|
|
23307
|
+
* Re-reads the root's history after its simulated records changed, following the clock if it was not.
|
|
23308
|
+
*/
|
|
23309
|
+
async regatherRoot(tileset) {
|
|
22469
23310
|
var _a;
|
|
23311
|
+
if (!this.historicGatherer) {
|
|
23312
|
+
this.viewerDateTimeSub(tileset);
|
|
23313
|
+
return;
|
|
23314
|
+
}
|
|
23315
|
+
await this.historicPossesLoadingProm;
|
|
23316
|
+
const { recordsByIds } = await this.historicGatherer.Gather([{ Bruce: { ID: this.rootId } }]);
|
|
23317
|
+
if (this.disposed) {
|
|
23318
|
+
return;
|
|
23319
|
+
}
|
|
23320
|
+
const posses = Manager.HistoricPossesFor({ viewer: this.viewer, item: this.item, records: recordsByIds[this.rootId] || [] });
|
|
23321
|
+
(_a = this.historicAnimation) === null || _a === void 0 ? void 0 : _a.replacePositions(posses);
|
|
23322
|
+
if (!posses.length) {
|
|
23323
|
+
this.applyRootRest(tileset);
|
|
23324
|
+
}
|
|
23325
|
+
}
|
|
23326
|
+
/*
|
|
23327
|
+
* The root as read at the scene's time, with any simulated record at that time laid over it.
|
|
23328
|
+
*/
|
|
23329
|
+
atClock(root) {
|
|
23330
|
+
return this.simulation && root
|
|
23331
|
+
? this.simulation.ApplyAt(root, Cesium.JulianDate.toDate(this.viewer.clock.currentTime).getTime())
|
|
23332
|
+
: root;
|
|
23333
|
+
}
|
|
23334
|
+
/*
|
|
23335
|
+
* Stands the tileset where its root is outside history: the simulated root, else the root's own record.
|
|
23336
|
+
*/
|
|
23337
|
+
applyRootRest(tileset) {
|
|
23338
|
+
var _a;
|
|
23339
|
+
this.applyCoords(tileset, (_a = this.simulatedRootCoords()) !== null && _a !== void 0 ? _a : this.restCoords);
|
|
23340
|
+
}
|
|
23341
|
+
/*
|
|
23342
|
+
* Where a simulated root puts the tileset, or null; a patch only places it when it names a whole location.
|
|
23343
|
+
*/
|
|
23344
|
+
simulatedRootCoords() {
|
|
23345
|
+
var _a, _b;
|
|
23346
|
+
const simulation = this.rootId ? this.simulation : null;
|
|
23347
|
+
const root = (_a = simulation === null || simulation === void 0 ? void 0 : simulation.GetEntity(this.rootId)) !== null && _a !== void 0 ? _a : ((simulation === null || simulation === void 0 ? void 0 : simulation.HasBlob(this.rootId)) ? simulation.Overlay({ Bruce: { ID: this.rootId } }) : null);
|
|
23348
|
+
const location = (_b = root === null || root === void 0 ? void 0 : root.Bruce) === null || _b === void 0 ? void 0 : _b.Location;
|
|
23349
|
+
if ((location === null || location === void 0 ? void 0 : location.latitude) == null || (location === null || location === void 0 ? void 0 : location.longitude) == null) {
|
|
23350
|
+
return null;
|
|
23351
|
+
}
|
|
23352
|
+
const pos3d = Cesium.Cartesian3.fromDegrees(+location.longitude, +location.latitude, EnsureNumber(location.altitude, 0));
|
|
23353
|
+
const transform = root.Bruce.Transform;
|
|
23354
|
+
return coordsFromHistoricPos3d(this.cTileset, this.rootId, pos3d, EnsureNumber(transform === null || transform === void 0 ? void 0 : transform.heading, 0), transform === null || transform === void 0 ? void 0 : transform.pitch, transform === null || transform === void 0 ? void 0 : transform.roll);
|
|
23355
|
+
}
|
|
23356
|
+
viewerDateTimeDispose() {
|
|
23357
|
+
var _a, _b;
|
|
22470
23358
|
(_a = this.viewerDateTimeChangeRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
22471
23359
|
this.viewerDateTimeChangeRemoval = null;
|
|
23360
|
+
// Made before the first positions land, so it can outlive a removal that was never set.
|
|
23361
|
+
(_b = this.historicGatherer) === null || _b === void 0 ? void 0 : _b.Dispose();
|
|
23362
|
+
this.historicGatherer = null;
|
|
22472
23363
|
}
|
|
22473
23364
|
/**
|
|
22474
23365
|
* Updates the Tileset with latest coordinates.
|
|
@@ -23609,11 +24500,11 @@
|
|
|
23609
24500
|
this.viewMonitorRemoval = null;
|
|
23610
24501
|
this.renderQueue = [];
|
|
23611
24502
|
this.renderQueueInterval = null;
|
|
23612
|
-
this.reRenderState = new EntityReRenderMaintainState();
|
|
23613
24503
|
this.zoomControl = [];
|
|
23614
24504
|
this.queryLoadId = 0;
|
|
23615
24505
|
this.renderDataLabEntitiesChain = Promise.resolve();
|
|
23616
24506
|
const { viewer, item, apiGetter, monitor, visualsRegister } = params;
|
|
24507
|
+
this.reRenderState = new EntityReRenderMaintainState(params.simulation);
|
|
23617
24508
|
this.viewer = viewer;
|
|
23618
24509
|
this.item = item;
|
|
23619
24510
|
this.apiGetter = apiGetter;
|
|
@@ -23710,7 +24601,8 @@
|
|
|
23710
24601
|
if (!((_a = data === null || data === void 0 ? void 0 : data.entities) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
23711
24602
|
return;
|
|
23712
24603
|
}
|
|
23713
|
-
|
|
24604
|
+
// The getter answers from the API, so a simulated Entity in its answer is swapped for its blob.
|
|
24605
|
+
this.distributeForRender(this.reRenderState.withSimulated(data.entities));
|
|
23714
24606
|
});
|
|
23715
24607
|
}
|
|
23716
24608
|
unsetGetter() {
|
|
@@ -25457,6 +26349,7 @@
|
|
|
25457
26349
|
this.featurePropCache = new Map();
|
|
25458
26350
|
this.featurePropsChecked = 0;
|
|
25459
26351
|
const { viewer, register: visualsManager, getters: apiGetter, item, initQueue, monitor } = params;
|
|
26352
|
+
this.simulation = params.simulation;
|
|
25460
26353
|
this.monitor = monitor;
|
|
25461
26354
|
this.viewer = viewer;
|
|
25462
26355
|
this.getters = apiGetter;
|
|
@@ -25557,6 +26450,7 @@
|
|
|
25557
26450
|
if (Manager.NeedsStyler(this.item)) {
|
|
25558
26451
|
const api = this.getters.GetBruceApi();
|
|
25559
26452
|
this.styler.Init({
|
|
26453
|
+
simulation: this.simulation,
|
|
25560
26454
|
viewer: this.viewer,
|
|
25561
26455
|
api: api,
|
|
25562
26456
|
cTileset: this.cTileset,
|
|
@@ -25914,6 +26808,7 @@
|
|
|
25914
26808
|
const needsStyler = Manager.NeedsStyler(this.item);
|
|
25915
26809
|
if (needsStyler) {
|
|
25916
26810
|
this.styler.Init({
|
|
26811
|
+
simulation: this.simulation,
|
|
25917
26812
|
viewer: this.viewer,
|
|
25918
26813
|
api: this.getters.GetBruceApi(),
|
|
25919
26814
|
cTileset: this.cTileset,
|
|
@@ -26598,6 +27493,7 @@
|
|
|
26598
27493
|
// LCC object if we're using XGrids to render LCC data.
|
|
26599
27494
|
this.lccObject = null;
|
|
26600
27495
|
this.viewer = params.viewer;
|
|
27496
|
+
this.simulation = params.simulation;
|
|
26601
27497
|
this.getters = params.getters;
|
|
26602
27498
|
this.visualsManager = params.register;
|
|
26603
27499
|
// We modify the object due to a lot of backwards compatibility.
|
|
@@ -26832,6 +27728,7 @@
|
|
|
26832
27728
|
this.item.Type == BModels.MenuItem.EType.IonTileset) {
|
|
26833
27729
|
const api = this.getters.GetBruceApi();
|
|
26834
27730
|
this.styler.Init({
|
|
27731
|
+
simulation: this.simulation,
|
|
26835
27732
|
viewer: this.viewer,
|
|
26836
27733
|
api: api,
|
|
26837
27734
|
cTileset: this.cTileset,
|
|
@@ -27172,14 +28069,32 @@
|
|
|
27172
28069
|
}
|
|
27173
28070
|
;
|
|
27174
28071
|
constructor(params) {
|
|
28072
|
+
var _a, _b;
|
|
27175
28073
|
// If true, this menu item is disposed and should not be used.
|
|
27176
28074
|
// Any rendering will be ignored.
|
|
27177
28075
|
this.disposed = false;
|
|
28076
|
+
this.simulationRemoval = null;
|
|
28077
|
+
this.FollowsSimulation = true;
|
|
27178
28078
|
const { apiGetter, item, register, viewer } = params;
|
|
27179
28079
|
this.apiGetter = apiGetter;
|
|
27180
28080
|
this.item = item;
|
|
27181
28081
|
this.register = register;
|
|
27182
28082
|
this.viewer = viewer;
|
|
28083
|
+
this.simulation = params.simulation;
|
|
28084
|
+
// Ends are not this item's own Entities, so it watches the store itself rather than wait to be asked.
|
|
28085
|
+
this.simulationRemoval = (_b = (_a = this.simulation) === null || _a === void 0 ? void 0 : _a.OnChanged.Subscribe((change) => {
|
|
28086
|
+
if (this.disposed) {
|
|
28087
|
+
return;
|
|
28088
|
+
}
|
|
28089
|
+
const relations = RelationRenderEngine.TakeRelationsTouching({
|
|
28090
|
+
visualRegister: this.register,
|
|
28091
|
+
menuItemId: this.item.id,
|
|
28092
|
+
entityIds: change.entityIds
|
|
28093
|
+
});
|
|
28094
|
+
if (relations.length) {
|
|
28095
|
+
this.onGetterUpdate(relations);
|
|
28096
|
+
}
|
|
28097
|
+
})) !== null && _b !== void 0 ? _b : null;
|
|
27183
28098
|
}
|
|
27184
28099
|
/**
|
|
27185
28100
|
* Starts rendering the menu item.
|
|
@@ -27248,10 +28163,13 @@
|
|
|
27248
28163
|
return items;
|
|
27249
28164
|
}
|
|
27250
28165
|
Dispose() {
|
|
28166
|
+
var _a;
|
|
27251
28167
|
if (this.disposed) {
|
|
27252
28168
|
return;
|
|
27253
28169
|
}
|
|
27254
28170
|
this.disposed = true;
|
|
28171
|
+
(_a = this.simulationRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
28172
|
+
this.simulationRemoval = null;
|
|
27255
28173
|
this.register.RemoveRegos({
|
|
27256
28174
|
menuItemId: this.item.id
|
|
27257
28175
|
});
|
|
@@ -27490,7 +28408,8 @@
|
|
|
27490
28408
|
menuItemId: this.item.id,
|
|
27491
28409
|
relations: relations,
|
|
27492
28410
|
viewer: this.viewer,
|
|
27493
|
-
visualRegister: this.register
|
|
28411
|
+
visualRegister: this.register,
|
|
28412
|
+
simulation: this.simulation
|
|
27494
28413
|
});
|
|
27495
28414
|
if (this.disposed) {
|
|
27496
28415
|
this.register.RemoveRegos({
|
|
@@ -27772,9 +28691,12 @@
|
|
|
27772
28691
|
}
|
|
27773
28692
|
;
|
|
27774
28693
|
constructor(params) {
|
|
28694
|
+
var _a, _b;
|
|
27775
28695
|
// If true, this menu item is disposed and should not be used.
|
|
27776
28696
|
// Any rendering will be ignored.
|
|
27777
28697
|
this.disposed = false;
|
|
28698
|
+
this.simulationRemoval = null;
|
|
28699
|
+
this.FollowsSimulation = true;
|
|
27778
28700
|
// Record of the currently drawn entity IDs.
|
|
27779
28701
|
// This is to avoid re-queueing already drawn entities when Init is called multiple times.
|
|
27780
28702
|
this.drawnIds = [];
|
|
@@ -27783,6 +28705,21 @@
|
|
|
27783
28705
|
this.item = item;
|
|
27784
28706
|
this.register = register;
|
|
27785
28707
|
this.viewer = viewer;
|
|
28708
|
+
this.simulation = params.simulation;
|
|
28709
|
+
// Ends are not this item's own Entities, so it watches the store itself rather than wait to be asked.
|
|
28710
|
+
this.simulationRemoval = (_b = (_a = this.simulation) === null || _a === void 0 ? void 0 : _a.OnChanged.Subscribe((change) => {
|
|
28711
|
+
if (this.disposed) {
|
|
28712
|
+
return;
|
|
28713
|
+
}
|
|
28714
|
+
const relations = RelationRenderEngine.TakeRelationsTouching({
|
|
28715
|
+
visualRegister: this.register,
|
|
28716
|
+
menuItemId: this.item.id,
|
|
28717
|
+
entityIds: change.entityIds
|
|
28718
|
+
});
|
|
28719
|
+
if (relations.length) {
|
|
28720
|
+
this.onGetterUpdate(relations);
|
|
28721
|
+
}
|
|
28722
|
+
})) !== null && _b !== void 0 ? _b : null;
|
|
27786
28723
|
}
|
|
27787
28724
|
/**
|
|
27788
28725
|
* Starts rendering the menu item.
|
|
@@ -27825,10 +28762,13 @@
|
|
|
27825
28762
|
this.drawnIds = [].concat(entityIds);
|
|
27826
28763
|
}
|
|
27827
28764
|
Dispose() {
|
|
28765
|
+
var _a;
|
|
27828
28766
|
if (this.disposed) {
|
|
27829
28767
|
return;
|
|
27830
28768
|
}
|
|
27831
28769
|
this.disposed = true;
|
|
28770
|
+
(_a = this.simulationRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
28771
|
+
this.simulationRemoval = null;
|
|
27832
28772
|
this.register.RemoveRegos({
|
|
27833
28773
|
menuItemId: this.item.id
|
|
27834
28774
|
});
|
|
@@ -27989,7 +28929,8 @@
|
|
|
27989
28929
|
menuItemId: this.item.id,
|
|
27990
28930
|
relations: relations,
|
|
27991
28931
|
viewer: this.viewer,
|
|
27992
|
-
visualRegister: this.register
|
|
28932
|
+
visualRegister: this.register,
|
|
28933
|
+
simulation: this.simulation
|
|
27993
28934
|
});
|
|
27994
28935
|
if (this.disposed) {
|
|
27995
28936
|
this.register.RemoveRegos({
|
|
@@ -29214,6 +30155,55 @@
|
|
|
29214
30155
|
get ModelSpace() {
|
|
29215
30156
|
return this.modelSpace;
|
|
29216
30157
|
}
|
|
30158
|
+
/**
|
|
30159
|
+
* Every node of the Assembly read so far, so a caller can work out motion in the Assembly's own terms.
|
|
30160
|
+
* @param timeMs the scene time for each node's current pose. Default: the clock's.
|
|
30161
|
+
* @returns per node its pose within its parent from the API (local) and as drawn at the time (current).
|
|
30162
|
+
*/
|
|
30163
|
+
GetNodes(timeMs) {
|
|
30164
|
+
const at = timeMs !== null && timeMs !== void 0 ? timeMs : Cesium.JulianDate.toDate(this.viewer.clock.currentTime).getTime();
|
|
30165
|
+
const rowsOf = (m) => m ? [0, 1, 2, 3].map(row => [0, 1, 2, 3].map(column => m[(column * 4) + row])) : null;
|
|
30166
|
+
return Object.values(this.hierarchyNodeByEntityId || {}).map(node => {
|
|
30167
|
+
var _a, _b, _c;
|
|
30168
|
+
const entity = (_a = this.apiEntities.get(node.entityId)) !== null && _a !== void 0 ? _a : null;
|
|
30169
|
+
const rows = (_b = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _b === void 0 ? void 0 : _b.AssemblyPosition;
|
|
30170
|
+
return {
|
|
30171
|
+
entityId: node.entityId,
|
|
30172
|
+
name: node.name,
|
|
30173
|
+
parentId: (_c = node.parentId) !== null && _c !== void 0 ? _c : null,
|
|
30174
|
+
local: Manager.MatrixFromRows(rows) ? rows : null,
|
|
30175
|
+
current: rowsOf(this.localAt(node.entityId, at)),
|
|
30176
|
+
entity
|
|
30177
|
+
};
|
|
30178
|
+
});
|
|
30179
|
+
}
|
|
30180
|
+
/**
|
|
30181
|
+
* The root's world frame at a time, history and simulation included, eg. to follow or frame the Assembly.
|
|
30182
|
+
* @param timeMs the scene time. Default: the clock's.
|
|
30183
|
+
* @returns null until the root has been read.
|
|
30184
|
+
*/
|
|
30185
|
+
GetRootFrame(timeMs) {
|
|
30186
|
+
const root = this.rootAt(timeMs !== null && timeMs !== void 0 ? timeMs : Cesium.JulianDate.toDate(this.viewer.clock.currentTime).getTime());
|
|
30187
|
+
return root ? Manager.rootFrame(root.pos3d, root.heading, root.pitch, root.roll) : null;
|
|
30188
|
+
}
|
|
30189
|
+
/**
|
|
30190
|
+
* A part's world frame at a time as its records and its parents place it, size included.
|
|
30191
|
+
* @param entityId the part.
|
|
30192
|
+
* @param timeMs the scene time. Default: the clock's.
|
|
30193
|
+
* @returns null until the root and the part have been read.
|
|
30194
|
+
*/
|
|
30195
|
+
GetPartFrame(entityId, timeMs) {
|
|
30196
|
+
const at = timeMs !== null && timeMs !== void 0 ? timeMs : Cesium.JulianDate.toDate(this.viewer.clock.currentTime).getTime();
|
|
30197
|
+
const rootFrame = this.GetRootFrame(at);
|
|
30198
|
+
const world = entityId === this.rootId ? Cesium.Matrix4.IDENTITY : this.composeWorld(entityId, at);
|
|
30199
|
+
if (!rootFrame || !world) {
|
|
30200
|
+
return null;
|
|
30201
|
+
}
|
|
30202
|
+
// Where the part is drawn, so its own ground offset is part of its frame.
|
|
30203
|
+
const ground = this.groundOffsets.get(entityId);
|
|
30204
|
+
const placed = ground ? Cesium.Matrix4.multiply(Cesium.Matrix4.fromTranslation(ground), world, new Cesium.Matrix4()) : world;
|
|
30205
|
+
return Cesium.Matrix4.multiply(rootFrame, placed, new Cesium.Matrix4());
|
|
30206
|
+
}
|
|
29217
30207
|
constructor(params) {
|
|
29218
30208
|
this.disposed = false;
|
|
29219
30209
|
// Cache of the hierarchy so that our scene-tree can reference it.
|
|
@@ -29223,19 +30213,41 @@
|
|
|
29223
30213
|
this.hierarchyNodeByEntityId = null;
|
|
29224
30214
|
this.modelSpace = false;
|
|
29225
30215
|
this.rootId = null;
|
|
29226
|
-
// Where the root sits
|
|
29227
|
-
// Every child is placed through this, so a root move is one value changing rather than a refetch of the whole Assembly.
|
|
30216
|
+
// Where the root sits outside its history, and which way it faces in degrees. Children are placed through it.
|
|
29228
30217
|
this.rootPos3d = null;
|
|
29229
30218
|
this.rootHeading = 0;
|
|
29230
|
-
|
|
30219
|
+
this.rootPitch = 0;
|
|
30220
|
+
this.rootRoll = 0;
|
|
30221
|
+
// The root's recorded positions and attitudes, oldest first, kept apart since a record may name only one.
|
|
30222
|
+
this.rootSeries = [];
|
|
30223
|
+
this.rootAttitude = [];
|
|
30224
|
+
// Every placement worked out for the scene time last asked about, shared by all parts drawn at it.
|
|
30225
|
+
this.pose = null;
|
|
30226
|
+
// Entity ID -> what was drawn for it.
|
|
29231
30227
|
this.children = new Map();
|
|
30228
|
+
// Entity ID -> its live position within its parent, for every node fetched including undrawn groups.
|
|
30229
|
+
this.localPositions = new Map();
|
|
30230
|
+
// Entity ID -> how much higher the ground under it is than under the root, along the root's up.
|
|
30231
|
+
this.groundOffsets = new Map();
|
|
30232
|
+
// Entity ID -> its recorded poses within its parent, oldest first.
|
|
30233
|
+
this.jointSeries = new Map();
|
|
30234
|
+
// Drawn children that can move with the clock, so a tick places only these and not the whole Assembly.
|
|
30235
|
+
this.animatedIds = new Set();
|
|
29232
30236
|
// The trail through the root's own movement, which describes the whole Assembly.
|
|
29233
|
-
this.
|
|
30237
|
+
this.trail = null;
|
|
30238
|
+
this.simulationRemoval = null;
|
|
30239
|
+
this.FollowsSimulation = true;
|
|
30240
|
+
// Entity ID -> the record the API gave, so a cleared simulation falls back to it without a refetch.
|
|
30241
|
+
this.apiEntities = new Map();
|
|
30242
|
+
// Re-reads history and re-applies it, eg. after simulated records change. Set while the clock is followed.
|
|
30243
|
+
this.regather = null;
|
|
30244
|
+
this.followingClock = false;
|
|
29234
30245
|
// Callback to dispose the link between scene time and the root's historic position.
|
|
29235
30246
|
this.viewerDateTimeChangeRemoval = null;
|
|
29236
30247
|
this.historicGatherer = null;
|
|
29237
30248
|
this.historicAnimation = null;
|
|
29238
30249
|
const { viewer, register: visualsManager, getters, item } = params;
|
|
30250
|
+
this.simulation = params.simulation;
|
|
29239
30251
|
this.viewer = viewer;
|
|
29240
30252
|
this.getters = getters;
|
|
29241
30253
|
this.item = item;
|
|
@@ -29259,10 +30271,10 @@
|
|
|
29259
30271
|
return exports.EntityRenderEngine.GetHeightRef((_b = (_a = item === null || item === void 0 ? void 0 : item.Style) === null || _a === void 0 ? void 0 : _a.Settings) === null || _b === void 0 ? void 0 : _b["modelStyle"]);
|
|
29260
30272
|
}
|
|
29261
30273
|
/*
|
|
29262
|
-
* The root's frame: east-north-up at its position, turned by its heading.
|
|
30274
|
+
* The root's frame: east-north-up at its position, turned by its heading, pitch and roll.
|
|
29263
30275
|
*/
|
|
29264
|
-
static rootFrame(rootPos3d, headingDegrees) {
|
|
29265
|
-
const hpr = new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(headingDegrees || 0), 0, 0);
|
|
30276
|
+
static rootFrame(rootPos3d, headingDegrees, pitchDegrees = 0, rollDegrees = 0) {
|
|
30277
|
+
const hpr = new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(headingDegrees || 0), Cesium.Math.toRadians(pitchDegrees || 0), Cesium.Math.toRadians(rollDegrees || 0));
|
|
29266
30278
|
return Cesium.Transforms.headingPitchRollToFixedFrame(rootPos3d, hpr);
|
|
29267
30279
|
}
|
|
29268
30280
|
/**
|
|
@@ -29285,28 +30297,553 @@
|
|
|
29285
30297
|
return Cesium.Matrix4.multiplyByPoint(Manager.rootFrame(rootPos3d, headingDegrees), offset, new Cesium.Cartesian3());
|
|
29286
30298
|
}
|
|
29287
30299
|
/**
|
|
29288
|
-
* Moves the
|
|
30300
|
+
* Moves where the root sits outside its history, without refetching children; recorded poses still win.
|
|
29289
30301
|
* @param params
|
|
29290
30302
|
*/
|
|
29291
30303
|
ApplyRootPosition(params) {
|
|
29292
|
-
var _a, _b;
|
|
30304
|
+
var _a, _b, _c, _d, _e, _f;
|
|
29293
30305
|
if (this.disposed || !(params === null || params === void 0 ? void 0 : params.pos3d)) {
|
|
29294
30306
|
return;
|
|
29295
30307
|
}
|
|
29296
30308
|
this.rootPos3d = params.pos3d;
|
|
29297
30309
|
this.rootHeading = (_b = (_a = params.heading) !== null && _a !== void 0 ? _a : this.rootHeading) !== null && _b !== void 0 ? _b : 0;
|
|
30310
|
+
this.rootPitch = (_d = (_c = params.pitch) !== null && _c !== void 0 ? _c : this.rootPitch) !== null && _d !== void 0 ? _d : 0;
|
|
30311
|
+
this.rootRoll = (_f = (_e = params.roll) !== null && _e !== void 0 ? _e : this.rootRoll) !== null && _f !== void 0 ? _f : 0;
|
|
30312
|
+
this.repose(true);
|
|
30313
|
+
}
|
|
30314
|
+
/*
|
|
30315
|
+
* Drops every placement worked out so far, after the root or a joint's records change.
|
|
30316
|
+
* @param everyChild re-place the parts that cannot move as well, because where they rest has changed.
|
|
30317
|
+
*/
|
|
30318
|
+
repose(everyChild = false) {
|
|
30319
|
+
this.pose = null;
|
|
29298
30320
|
for (const child of this.children.values()) {
|
|
29299
|
-
|
|
29300
|
-
|
|
30321
|
+
if (everyChild || Boolean(child.moving) != this.canMove(child.entityId)) {
|
|
30322
|
+
this.bindPlacement(child);
|
|
30323
|
+
}
|
|
30324
|
+
}
|
|
30325
|
+
this.viewer.scene.requestRender();
|
|
30326
|
+
}
|
|
30327
|
+
/*
|
|
30328
|
+
* Takes a node's pose outside its history from its record, and for the root where it stands and faces.
|
|
30329
|
+
*/
|
|
30330
|
+
absorbNode(entity) {
|
|
30331
|
+
var _a, _b, _c, _d, _e;
|
|
30332
|
+
const id = (_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID;
|
|
30333
|
+
const local = Manager.MatrixFromRows((_b = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _b === void 0 ? void 0 : _b.AssemblyPosition);
|
|
30334
|
+
if (local) {
|
|
30335
|
+
this.localPositions.set(id, local);
|
|
30336
|
+
}
|
|
30337
|
+
// Anchored on the root, which is usually never drawn; everything under it is placed from the hierarchy.
|
|
30338
|
+
if (id === this.rootId || (!this.rootId && !this.rootPos3d)) {
|
|
30339
|
+
const rootPos3d = exports.EntityUtils.GetPos({
|
|
30340
|
+
viewer: this.viewer,
|
|
30341
|
+
entity,
|
|
30342
|
+
recordHeightRef: Manager.GetRecordHeightRef(this.item),
|
|
30343
|
+
returnHeightRef: Cesium.HeightReference.NONE,
|
|
30344
|
+
allowRendered: false
|
|
30345
|
+
});
|
|
30346
|
+
if (rootPos3d) {
|
|
30347
|
+
this.rootPos3d = rootPos3d;
|
|
30348
|
+
this.rootHeading = EnsureNumber((_c = entity.Bruce.Transform) === null || _c === void 0 ? void 0 : _c.heading, 0);
|
|
30349
|
+
this.rootPitch = EnsureNumber((_d = entity.Bruce.Transform) === null || _d === void 0 ? void 0 : _d.pitch, 0);
|
|
30350
|
+
this.rootRoll = EnsureNumber((_e = entity.Bruce.Transform) === null || _e === void 0 ? void 0 : _e.roll, 0);
|
|
30351
|
+
}
|
|
30352
|
+
}
|
|
30353
|
+
}
|
|
30354
|
+
/*
|
|
30355
|
+
* A node's record with its simulation laid over it, keeping what a stand-in leaves out, eg. its model.
|
|
30356
|
+
*/
|
|
30357
|
+
withSimulation(entity) {
|
|
30358
|
+
var _a;
|
|
30359
|
+
const id = (_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID;
|
|
30360
|
+
if (!this.simulation || !id) {
|
|
30361
|
+
return entity;
|
|
30362
|
+
}
|
|
30363
|
+
const standIn = this.simulation.GetEntity(id);
|
|
30364
|
+
if (!standIn) {
|
|
30365
|
+
return this.simulation.Overlay(entity);
|
|
30366
|
+
}
|
|
30367
|
+
return { ...entity, ...standIn, Bruce: { ...entity.Bruce, ...standIn.Bruce } };
|
|
30368
|
+
}
|
|
30369
|
+
/*
|
|
30370
|
+
* Brings the Assembly in line with a change to what is simulated.
|
|
30371
|
+
*/
|
|
30372
|
+
onSimulationChanged(change) {
|
|
30373
|
+
var _a;
|
|
30374
|
+
if (this.disposed || !this.hierarchyNodeByEntityId) {
|
|
30375
|
+
return;
|
|
30376
|
+
}
|
|
30377
|
+
const ids = change.entityIds.filter(id => this.hierarchyNodeByEntityId[id]);
|
|
30378
|
+
if (!ids.length) {
|
|
30379
|
+
return;
|
|
30380
|
+
}
|
|
30381
|
+
if (change.entities) {
|
|
30382
|
+
for (const id of ids) {
|
|
30383
|
+
const api = this.apiEntities.get(id);
|
|
30384
|
+
if (!api) {
|
|
30385
|
+
// Not loaded yet, and it will be drawn from its simulation when it is.
|
|
30386
|
+
continue;
|
|
30387
|
+
}
|
|
30388
|
+
const entity = this.withSimulation(api);
|
|
30389
|
+
// A cleared blob leaves the node's own matrix where the API has none, so it is reset.
|
|
30390
|
+
if (!((_a = entity.Bruce) === null || _a === void 0 ? void 0 : _a.AssemblyPosition)) {
|
|
30391
|
+
this.localPositions.delete(id);
|
|
30392
|
+
}
|
|
30393
|
+
this.absorbNode(entity);
|
|
30394
|
+
const child = this.children.get(id);
|
|
30395
|
+
if (child) {
|
|
30396
|
+
child.entity = entity;
|
|
30397
|
+
}
|
|
30398
|
+
}
|
|
30399
|
+
this.repose(true);
|
|
30400
|
+
}
|
|
30401
|
+
if (change.records) {
|
|
30402
|
+
if (this.regather) {
|
|
30403
|
+
this.regather(true);
|
|
30404
|
+
}
|
|
30405
|
+
else {
|
|
30406
|
+
this.viewerDateTimeSub();
|
|
30407
|
+
}
|
|
30408
|
+
}
|
|
30409
|
+
}
|
|
30410
|
+
/*
|
|
30411
|
+
* Nodes of this Assembly with simulated history.
|
|
30412
|
+
*/
|
|
30413
|
+
simulatedNodeIds() {
|
|
30414
|
+
if (!this.simulation || !this.hierarchyNodeByEntityId) {
|
|
30415
|
+
return [];
|
|
30416
|
+
}
|
|
30417
|
+
return this.simulation.GetRecordEntityIds().filter(id => this.hierarchyNodeByEntityId[id]);
|
|
30418
|
+
}
|
|
30419
|
+
/*
|
|
30420
|
+
* Whether a node can move with time: the root has history, or it or an ancestor does.
|
|
30421
|
+
*/
|
|
30422
|
+
canMove(entityId) {
|
|
30423
|
+
return this.rootSeries.length > 0 || this.rootAttitude.length > 0 || this.animatedIds.has(entityId);
|
|
30424
|
+
}
|
|
30425
|
+
/*
|
|
30426
|
+
* Gives a child its position and orientation, as functions of scene time only when it can move.
|
|
30427
|
+
*/
|
|
30428
|
+
bindPlacement(child) {
|
|
30429
|
+
const id = child.entityId;
|
|
30430
|
+
const moving = this.canMove(id);
|
|
30431
|
+
if (moving) {
|
|
30432
|
+
if (!child.moving) {
|
|
30433
|
+
child.cEntity.position = new Cesium.CallbackProperty((time, result) => { var _a; return Manager.cloneOrUndefined((_a = this.placementAt(id, time)) === null || _a === void 0 ? void 0 : _a.pos3d, result); }, false);
|
|
30434
|
+
child.cEntity.orientation = new Cesium.CallbackProperty((time, result) => {
|
|
30435
|
+
var _a;
|
|
30436
|
+
const orient = (_a = this.placementAt(id, time)) === null || _a === void 0 ? void 0 : _a.orient;
|
|
30437
|
+
return orient ? Cesium.Quaternion.clone(orient, result) : undefined;
|
|
30438
|
+
}, false);
|
|
30439
|
+
child.cEntity.model.scale = new Cesium.CallbackProperty((time) => { var _a, _b; return (_b = (_a = this.placementAt(id, time)) === null || _a === void 0 ? void 0 : _a.scale) !== null && _b !== void 0 ? _b : 1; }, false);
|
|
30440
|
+
}
|
|
30441
|
+
}
|
|
30442
|
+
else {
|
|
30443
|
+
const placed = this.placementAt(id, this.viewer.clock.currentTime);
|
|
30444
|
+
if (placed) {
|
|
30445
|
+
child.cEntity.position = new Cesium.ConstantPositionProperty(placed.pos3d);
|
|
30446
|
+
if (placed.orient) {
|
|
30447
|
+
child.cEntity.orientation = new Cesium.ConstantProperty(placed.orient);
|
|
30448
|
+
}
|
|
30449
|
+
child.cEntity.model.scale = new Cesium.ConstantProperty(placed.scale);
|
|
30450
|
+
}
|
|
30451
|
+
}
|
|
30452
|
+
if (Boolean(child.moving) != moving) {
|
|
30453
|
+
child.moving = moving;
|
|
30454
|
+
this.visualsManager.SetMoving({ moving, entityIds: [id], menuItemId: this.item.id });
|
|
30455
|
+
}
|
|
30456
|
+
}
|
|
30457
|
+
/*
|
|
30458
|
+
* The one scale a model takes from its part's composed matrix, the largest when they are uneven.
|
|
30459
|
+
*/
|
|
30460
|
+
static uniformScale(world) {
|
|
30461
|
+
return Cesium.Cartesian3.maximumComponent(Cesium.Matrix4.getScale(world, new Cesium.Cartesian3()));
|
|
30462
|
+
}
|
|
30463
|
+
static cloneOrUndefined(pos3d, result) {
|
|
30464
|
+
return pos3d ? Cesium.Cartesian3.clone(pos3d, result) : undefined;
|
|
30465
|
+
}
|
|
30466
|
+
/*
|
|
30467
|
+
* Where a node draws at a scene time, from one composition shared by every part asked about at that time.
|
|
30468
|
+
*/
|
|
30469
|
+
placementAt(entityId, time) {
|
|
30470
|
+
if (this.disposed || !time) {
|
|
30471
|
+
return null;
|
|
30472
|
+
}
|
|
30473
|
+
const timeMs = Cesium.JulianDate.toDate(time).getTime();
|
|
30474
|
+
if (!this.pose || this.pose.timeMs !== timeMs) {
|
|
30475
|
+
const root = this.rootAt(timeMs);
|
|
30476
|
+
this.pose = {
|
|
30477
|
+
timeMs,
|
|
30478
|
+
rootFrame: root ? Manager.rootFrame(root.pos3d, root.heading, root.pitch, root.roll) : null,
|
|
30479
|
+
worlds: new Map(),
|
|
30480
|
+
placements: new Map()
|
|
30481
|
+
};
|
|
30482
|
+
}
|
|
30483
|
+
const pose = this.pose;
|
|
30484
|
+
if (!pose.rootFrame) {
|
|
30485
|
+
return null;
|
|
30486
|
+
}
|
|
30487
|
+
let placed = pose.placements.get(entityId);
|
|
30488
|
+
if (!placed) {
|
|
30489
|
+
const world = this.composeWorld(entityId, timeMs, pose.worlds);
|
|
30490
|
+
const offset = world
|
|
30491
|
+
? Cesium.Matrix4.getTranslation(world, new Cesium.Cartesian3())
|
|
30492
|
+
: new Cesium.Cartesian3(0, 0, 0);
|
|
30493
|
+
const ground = this.groundOffsets.get(entityId);
|
|
30494
|
+
if (ground) {
|
|
30495
|
+
Cesium.Cartesian3.add(offset, ground, offset);
|
|
30496
|
+
}
|
|
30497
|
+
placed = {
|
|
30498
|
+
pos3d: Cesium.Matrix4.multiplyByPoint(pose.rootFrame, offset, new Cesium.Cartesian3()),
|
|
30499
|
+
// Without a matrix, eg. the root itself, there is no orientation of its own to apply.
|
|
30500
|
+
orient: world ? exports.EntityUtils.GetAssemblyOrientationInFrame(pose.rootFrame, world) : null,
|
|
30501
|
+
scale: world ? Manager.uniformScale(world) : 1
|
|
30502
|
+
};
|
|
30503
|
+
pose.placements.set(entityId, placed);
|
|
30504
|
+
}
|
|
30505
|
+
return placed;
|
|
30506
|
+
}
|
|
30507
|
+
/*
|
|
30508
|
+
* The root's pose at a time, position and attitude each following their own records as a joint's do.
|
|
30509
|
+
*/
|
|
30510
|
+
rootAt(timeMs) {
|
|
30511
|
+
const pos3d = this.rootPositionAt(timeMs);
|
|
30512
|
+
if (!pos3d) {
|
|
30513
|
+
return null;
|
|
30514
|
+
}
|
|
30515
|
+
const live = { heading: this.rootHeading, pitch: this.rootPitch, roll: this.rootRoll };
|
|
30516
|
+
const series = this.rootAttitude;
|
|
30517
|
+
if (!series.length || timeMs < series[0].timeMs) {
|
|
30518
|
+
return { pos3d, ...live };
|
|
30519
|
+
}
|
|
30520
|
+
let i = 0;
|
|
30521
|
+
while (i < series.length - 1 && series[i + 1].timeMs <= timeMs) {
|
|
30522
|
+
i++;
|
|
30523
|
+
}
|
|
30524
|
+
const a = series[i];
|
|
30525
|
+
const b = series[i + 1];
|
|
30526
|
+
const t = b ? (timeMs - a.timeMs) / (b.timeMs - a.timeMs) : 0;
|
|
30527
|
+
const turn = (from, to, fallback) => {
|
|
30528
|
+
var _a, _b, _c;
|
|
30529
|
+
const x = (_a = from !== null && from !== void 0 ? from : fallback) !== null && _a !== void 0 ? _a : 0;
|
|
30530
|
+
const y = (_c = (_b = (b ? to : from)) !== null && _b !== void 0 ? _b : fallback) !== null && _c !== void 0 ? _c : 0;
|
|
30531
|
+
// The short way round, so 350 to 10 degrees turns through north rather than all the way back.
|
|
30532
|
+
return x + (Cesium.Math.negativePiToPi(Cesium.Math.toRadians(y - x)) * 180 / Math.PI) * t;
|
|
30533
|
+
};
|
|
30534
|
+
return {
|
|
30535
|
+
pos3d,
|
|
30536
|
+
heading: turn(a.heading, b === null || b === void 0 ? void 0 : b.heading, live.heading),
|
|
30537
|
+
pitch: turn(a.pitch, b === null || b === void 0 ? void 0 : b.pitch, live.pitch),
|
|
30538
|
+
roll: turn(a.roll, b === null || b === void 0 ? void 0 : b.roll, live.roll)
|
|
30539
|
+
};
|
|
30540
|
+
}
|
|
30541
|
+
/*
|
|
30542
|
+
* Where the root is at a time, from its recorded positions.
|
|
30543
|
+
*/
|
|
30544
|
+
rootPositionAt(timeMs) {
|
|
30545
|
+
const series = this.rootSeries;
|
|
30546
|
+
if (!series.length || timeMs < series[0].timeMs) {
|
|
30547
|
+
return this.rootPos3d;
|
|
30548
|
+
}
|
|
30549
|
+
let i = 0;
|
|
30550
|
+
while (i < series.length - 1 && series[i + 1].timeMs <= timeMs) {
|
|
30551
|
+
i++;
|
|
30552
|
+
}
|
|
30553
|
+
const a = series[i];
|
|
30554
|
+
const b = series[i + 1];
|
|
30555
|
+
if (!b) {
|
|
30556
|
+
return a.pos3d;
|
|
30557
|
+
}
|
|
30558
|
+
const t = (timeMs - a.timeMs) / (b.timeMs - a.timeMs);
|
|
30559
|
+
// A straight line between orbit samples cuts up to a kilometre inside the path, so it is lifted back out.
|
|
30560
|
+
const chord = Cesium.Cartesian3.lerp(a.pos3d, b.pos3d, t, new Cesium.Cartesian3());
|
|
30561
|
+
const carto = Cesium.Cartographic.fromCartesian(chord);
|
|
30562
|
+
return carto
|
|
30563
|
+
? Cesium.Cartesian3.fromRadians(carto.longitude, carto.latitude, Cesium.Math.lerp(a.height, b.height, t))
|
|
30564
|
+
: chord;
|
|
30565
|
+
}
|
|
30566
|
+
/*
|
|
30567
|
+
* Takes the root's records into its series.
|
|
30568
|
+
* @param replace the records are everything held for the root, so the series becomes exactly them.
|
|
30569
|
+
* @returns whether the series changed.
|
|
30570
|
+
*/
|
|
30571
|
+
absorbRootRecords(posses, replace) {
|
|
30572
|
+
if (!(posses === null || posses === void 0 ? void 0 : posses.length) && !(replace && this.rootSeries.length)) {
|
|
30573
|
+
return false;
|
|
30574
|
+
}
|
|
30575
|
+
const byTime = new Map(replace ? [] : this.rootSeries.map(k => [k.timeMs, k]));
|
|
30576
|
+
for (const pos of posses) {
|
|
30577
|
+
const carto = Cesium.Cartographic.fromCartesian(pos.pos3d);
|
|
30578
|
+
if (!carto) {
|
|
30579
|
+
continue;
|
|
30580
|
+
}
|
|
30581
|
+
byTime.set(pos.dateTime.getTime(), {
|
|
30582
|
+
timeMs: pos.dateTime.getTime(),
|
|
30583
|
+
pos3d: pos.pos3d,
|
|
30584
|
+
height: carto.height
|
|
30585
|
+
});
|
|
30586
|
+
}
|
|
30587
|
+
const series = Array.from(byTime.values()).sort((x, y) => x.timeMs - y.timeMs);
|
|
30588
|
+
const changed = (series.length !== this.rootSeries.length ||
|
|
30589
|
+
series.some((k, i) => !Cesium.Cartesian3.equals(k.pos3d, this.rootSeries[i].pos3d)));
|
|
30590
|
+
this.rootSeries = series;
|
|
30591
|
+
return changed;
|
|
30592
|
+
}
|
|
30593
|
+
/*
|
|
30594
|
+
* Takes the angles the root's records name into its attitude series, apart from where it is.
|
|
30595
|
+
* @param replace the records are everything held for the root, as for absorbRootRecords.
|
|
30596
|
+
* @returns whether the series changed.
|
|
30597
|
+
*/
|
|
30598
|
+
absorbRootAttitude(records, replace) {
|
|
30599
|
+
const angle = (transform, key) => {
|
|
30600
|
+
var _a;
|
|
30601
|
+
const value = (_a = transform === null || transform === void 0 ? void 0 : transform[key]) !== null && _a !== void 0 ? _a : transform === null || transform === void 0 ? void 0 : transform[key[0].toUpperCase() + key.slice(1)];
|
|
30602
|
+
return value == null || isNaN(+value) ? null : +value;
|
|
30603
|
+
};
|
|
30604
|
+
const byTime = new Map(replace ? [] : this.rootAttitude.map(k => [k.timeMs, k]));
|
|
30605
|
+
for (const record of records || []) {
|
|
30606
|
+
const timeMs = (record === null || record === void 0 ? void 0 : record.dateTime) ? new Date(record.dateTime).getTime() : NaN;
|
|
30607
|
+
const key = {
|
|
30608
|
+
timeMs,
|
|
30609
|
+
heading: angle(record === null || record === void 0 ? void 0 : record.transform, "heading"),
|
|
30610
|
+
pitch: angle(record === null || record === void 0 ? void 0 : record.transform, "pitch"),
|
|
30611
|
+
roll: angle(record === null || record === void 0 ? void 0 : record.transform, "roll")
|
|
30612
|
+
};
|
|
30613
|
+
if (isNaN(timeMs) || (key.heading == null && key.pitch == null && key.roll == null)) {
|
|
29301
30614
|
continue;
|
|
29302
30615
|
}
|
|
29303
|
-
|
|
29304
|
-
|
|
29305
|
-
|
|
29306
|
-
|
|
30616
|
+
byTime.set(timeMs, key);
|
|
30617
|
+
}
|
|
30618
|
+
const series = Array.from(byTime.values()).sort((x, y) => x.timeMs - y.timeMs);
|
|
30619
|
+
const held = this.rootAttitude;
|
|
30620
|
+
const differs = (k, i) => (k.timeMs !== held[i].timeMs ||
|
|
30621
|
+
k.heading !== held[i].heading ||
|
|
30622
|
+
k.pitch !== held[i].pitch ||
|
|
30623
|
+
k.roll !== held[i].roll);
|
|
30624
|
+
const changed = series.length !== held.length || series.some(differs);
|
|
30625
|
+
this.rootAttitude = series;
|
|
30626
|
+
return changed;
|
|
30627
|
+
}
|
|
30628
|
+
/*
|
|
30629
|
+
* A node's matrix in the root's frame at a time, from each ancestor's pose within its parent.
|
|
30630
|
+
*/
|
|
30631
|
+
composeWorld(entityId, timeMs, memo, localOf = (id) => this.localAt(id, timeMs)) {
|
|
30632
|
+
var _a, _b, _c, _d;
|
|
30633
|
+
if (memo === null || memo === void 0 ? void 0 : memo.has(entityId)) {
|
|
30634
|
+
return memo.get(entityId);
|
|
30635
|
+
}
|
|
30636
|
+
let world = null;
|
|
30637
|
+
const local = localOf(entityId);
|
|
30638
|
+
if (local) {
|
|
30639
|
+
const parentId = (_b = (_a = this.hierarchyNodeByEntityId) === null || _a === void 0 ? void 0 : _a[entityId]) === null || _b === void 0 ? void 0 : _b.parentId;
|
|
30640
|
+
const parent = parentId ? this.composeWorld(parentId, timeMs, memo, localOf) : null;
|
|
30641
|
+
world = parent ? Cesium.Matrix4.multiply(parent, local, new Cesium.Matrix4()) : Cesium.Matrix4.clone(local);
|
|
30642
|
+
}
|
|
30643
|
+
else if (entityId !== this.rootId) {
|
|
30644
|
+
// A node whose own position was not fetched falls back to the API's composed matrix.
|
|
30645
|
+
const child = this.children.get(entityId);
|
|
30646
|
+
world = Manager.MatrixFromRows((_d = (_c = child === null || child === void 0 ? void 0 : child.entity) === null || _c === void 0 ? void 0 : _c.Bruce) === null || _d === void 0 ? void 0 : _d.AssemblyWorldPosition);
|
|
30647
|
+
}
|
|
30648
|
+
memo === null || memo === void 0 ? void 0 : memo.set(entityId, world);
|
|
30649
|
+
return world;
|
|
30650
|
+
}
|
|
30651
|
+
/*
|
|
30652
|
+
* Under a clamped or relative style, lifts a part by how much higher its ground is than the root's.
|
|
30653
|
+
*/
|
|
30654
|
+
measureGroundOffset(entity) {
|
|
30655
|
+
var _a;
|
|
30656
|
+
const entityId = (_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID;
|
|
30657
|
+
this.groundOffsets.delete(entityId);
|
|
30658
|
+
const root = this.apiEntities.get(this.rootId);
|
|
30659
|
+
if (!entityId ||
|
|
30660
|
+
entityId === this.rootId ||
|
|
30661
|
+
!root ||
|
|
30662
|
+
!this.rootPos3d ||
|
|
30663
|
+
Manager.GetRecordHeightRef(this.item) === Cesium.HeightReference.NONE) {
|
|
30664
|
+
return;
|
|
30665
|
+
}
|
|
30666
|
+
const rootFrame = Manager.rootFrame(this.rootPos3d, this.rootHeading, this.rootPitch, this.rootRoll);
|
|
30667
|
+
const toRoot = Cesium.Matrix4.inverse(rootFrame, new Cesium.Matrix4());
|
|
30668
|
+
const groundUp = (record) => {
|
|
30669
|
+
const pos3d = exports.EntityUtils.GetPos({
|
|
30670
|
+
viewer: this.viewer,
|
|
30671
|
+
entity: record,
|
|
30672
|
+
recordHeightRef: Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
30673
|
+
returnHeightRef: Cesium.HeightReference.NONE,
|
|
30674
|
+
allowRendered: false
|
|
30675
|
+
});
|
|
30676
|
+
return pos3d ? Cesium.Matrix4.multiplyByPoint(toRoot, pos3d, new Cesium.Cartesian3()).z : null;
|
|
30677
|
+
};
|
|
30678
|
+
const part = groundUp(entity);
|
|
30679
|
+
const under = groundUp(root);
|
|
30680
|
+
if (part == null || under == null || Math.abs(part - under) < 1e-6) {
|
|
30681
|
+
return;
|
|
30682
|
+
}
|
|
30683
|
+
this.groundOffsets.set(entityId, new Cesium.Cartesian3(0, 0, part - under));
|
|
30684
|
+
}
|
|
30685
|
+
/*
|
|
30686
|
+
* The rigid Earth-fixed motion from where a part's record places it to where it is drawn at a time.
|
|
30687
|
+
*/
|
|
30688
|
+
motionAt(entityId, time) {
|
|
30689
|
+
if (this.disposed || !time || !this.rootPos3d) {
|
|
30690
|
+
return null;
|
|
30691
|
+
}
|
|
30692
|
+
const timeMs = Cesium.JulianDate.toDate(time).getTime();
|
|
30693
|
+
const root = this.rootAt(timeMs);
|
|
30694
|
+
const drawn = this.composeWorld(entityId, timeMs, new Map());
|
|
30695
|
+
const stored = this.composeWorld(entityId, timeMs, new Map(), (id) => {
|
|
30696
|
+
var _a, _b, _c, _d;
|
|
30697
|
+
return ((_d = (_c = Manager.MatrixFromRows((_b = (_a = this.apiEntities.get(id)) === null || _a === void 0 ? void 0 : _a.Bruce) === null || _b === void 0 ? void 0 : _b.AssemblyPosition)) !== null && _c !== void 0 ? _c : this.localPositions.get(id)) !== null && _d !== void 0 ? _d : null);
|
|
30698
|
+
});
|
|
30699
|
+
if (!root || !drawn || !stored) {
|
|
30700
|
+
return null;
|
|
30701
|
+
}
|
|
30702
|
+
const drawnFixed = Cesium.Matrix4.multiply(Manager.rootFrame(root.pos3d, root.heading, root.pitch, root.roll), drawn, new Cesium.Matrix4());
|
|
30703
|
+
const storedFixed = Cesium.Matrix4.multiply(Manager.rootFrame(this.rootPos3d, this.rootHeading, this.rootPitch, this.rootRoll), stored, new Cesium.Matrix4());
|
|
30704
|
+
return Cesium.Matrix4.multiply(drawnFixed, Cesium.Matrix4.inverse(storedFixed, new Cesium.Matrix4()), new Cesium.Matrix4());
|
|
30705
|
+
}
|
|
30706
|
+
/*
|
|
30707
|
+
* A node's pose within its parent at a time, interpolated between the records either side.
|
|
30708
|
+
*/
|
|
30709
|
+
localAt(entityId, timeMs) {
|
|
30710
|
+
var _a;
|
|
30711
|
+
// The live pose stands before the first record and the last record holds after it, as in an API read.
|
|
30712
|
+
const live = (_a = this.localPositions.get(entityId)) !== null && _a !== void 0 ? _a : null;
|
|
30713
|
+
const series = this.jointSeries.get(entityId);
|
|
30714
|
+
if (!(series === null || series === void 0 ? void 0 : series.length) || timeMs == null || timeMs < series[0].timeMs) {
|
|
30715
|
+
return live;
|
|
30716
|
+
}
|
|
30717
|
+
let i = 0;
|
|
30718
|
+
while (i < series.length - 1 && series[i + 1].timeMs <= timeMs) {
|
|
30719
|
+
i++;
|
|
30720
|
+
}
|
|
30721
|
+
const a = series[i];
|
|
30722
|
+
const b = series[i + 1];
|
|
30723
|
+
if (!b) {
|
|
30724
|
+
return Cesium.Matrix4.fromTranslationQuaternionRotationScale(a.translation, a.rotation, a.scale);
|
|
30725
|
+
}
|
|
30726
|
+
const t = (timeMs - a.timeMs) / (b.timeMs - a.timeMs);
|
|
30727
|
+
return Cesium.Matrix4.fromTranslationQuaternionRotationScale(Cesium.Cartesian3.lerp(a.translation, b.translation, t, new Cesium.Cartesian3()), Cesium.Quaternion.slerp(a.rotation, b.rotation, t, new Cesium.Quaternion()), Cesium.Cartesian3.lerp(a.scale, b.scale, t, new Cesium.Cartesian3()));
|
|
30728
|
+
}
|
|
30729
|
+
/*
|
|
30730
|
+
* Rebuilds each node's keyframes from the historic records held so far.
|
|
30731
|
+
* @returns whether any node has keyframes.
|
|
30732
|
+
*/
|
|
30733
|
+
absorbJointRecords(recordsByIds) {
|
|
30734
|
+
var _a;
|
|
30735
|
+
// Everything held is given, so a node with nothing left, eg. its simulated records cleared, stops moving.
|
|
30736
|
+
for (const entityId of Array.from(this.jointSeries.keys())) {
|
|
30737
|
+
if (!((_a = recordsByIds === null || recordsByIds === void 0 ? void 0 : recordsByIds[entityId]) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
30738
|
+
this.jointSeries.delete(entityId);
|
|
30739
|
+
}
|
|
30740
|
+
}
|
|
30741
|
+
for (const entityId of Object.keys(recordsByIds || {})) {
|
|
30742
|
+
const keyframes = [];
|
|
30743
|
+
for (const record of recordsByIds[entityId] || []) {
|
|
30744
|
+
const matrix = Manager.MatrixFromRows(record.assemblyPosition);
|
|
30745
|
+
const timeMs = new Date(record.dateTime).getTime();
|
|
30746
|
+
if (!matrix || isNaN(timeMs)) {
|
|
30747
|
+
continue;
|
|
30748
|
+
}
|
|
30749
|
+
const scale = Cesium.Matrix4.getScale(matrix, new Cesium.Cartesian3());
|
|
30750
|
+
const rotation = Cesium.Matrix4.getRotation(matrix, new Cesium.Matrix3());
|
|
30751
|
+
keyframes.push({
|
|
30752
|
+
timeMs,
|
|
30753
|
+
translation: Cesium.Matrix4.getTranslation(matrix, new Cesium.Cartesian3()),
|
|
30754
|
+
rotation: Cesium.Quaternion.fromRotationMatrix(rotation),
|
|
30755
|
+
scale
|
|
30756
|
+
});
|
|
30757
|
+
}
|
|
30758
|
+
if (keyframes.length) {
|
|
30759
|
+
keyframes.sort((x, y) => x.timeMs - y.timeMs);
|
|
30760
|
+
this.jointSeries.set(entityId, keyframes);
|
|
29307
30761
|
}
|
|
29308
30762
|
}
|
|
29309
|
-
this.
|
|
30763
|
+
this.refreshAnimated();
|
|
30764
|
+
return this.jointSeries.size > 0;
|
|
30765
|
+
}
|
|
30766
|
+
/*
|
|
30767
|
+
* Rebuilds which drawn children can move with the clock, by walking each one up to the root.
|
|
30768
|
+
*/
|
|
30769
|
+
refreshAnimated() {
|
|
30770
|
+
this.animatedIds.clear();
|
|
30771
|
+
if (!this.jointSeries.size) {
|
|
30772
|
+
return;
|
|
30773
|
+
}
|
|
30774
|
+
for (const entityId of this.children.keys()) {
|
|
30775
|
+
if (this.hasAnimatedAncestry(entityId)) {
|
|
30776
|
+
this.animatedIds.add(entityId);
|
|
30777
|
+
}
|
|
30778
|
+
}
|
|
30779
|
+
}
|
|
30780
|
+
/*
|
|
30781
|
+
* Whether a node or anything above it has keyframes, so the clock can move it.
|
|
30782
|
+
*/
|
|
30783
|
+
hasAnimatedAncestry(entityId) {
|
|
30784
|
+
var _a, _b;
|
|
30785
|
+
let at = entityId;
|
|
30786
|
+
while (at && this.jointSeries.size) {
|
|
30787
|
+
if (this.jointSeries.has(at)) {
|
|
30788
|
+
return true;
|
|
30789
|
+
}
|
|
30790
|
+
at = (_b = (_a = this.hierarchyNodeByEntityId) === null || _a === void 0 ? void 0 : _a[at]) === null || _b === void 0 ? void 0 : _b.parentId;
|
|
30791
|
+
}
|
|
30792
|
+
return false;
|
|
30793
|
+
}
|
|
30794
|
+
/*
|
|
30795
|
+
* Which nodes to ask history for.
|
|
30796
|
+
*/
|
|
30797
|
+
async historicEntityIds(api) {
|
|
30798
|
+
var _a, _b;
|
|
30799
|
+
// Through the Assembly when there is one, so the hierarchy follows the relation type on its record.
|
|
30800
|
+
const assemblyId = this.item["Assembly.ID"];
|
|
30801
|
+
const root = assemblyId ? { key: "root", AssemblyID: assemblyId } : { key: "root", RootID: this.rootId };
|
|
30802
|
+
const historic = {
|
|
30803
|
+
key: "historic",
|
|
30804
|
+
Historic: { HistoricKey: ((_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historicAttrKey) || undefined }
|
|
30805
|
+
};
|
|
30806
|
+
const query = {
|
|
30807
|
+
[BModels.DataLab.EReqKey.Primary]: { Items: [root, historic], Select: { Type: BModels.DataLab.ESelectType.Id } }
|
|
30808
|
+
};
|
|
30809
|
+
const load = 1000;
|
|
30810
|
+
try {
|
|
30811
|
+
const ids = [];
|
|
30812
|
+
for (let skip = 0;; skip += load) {
|
|
30813
|
+
const { items } = await BModels.DataLab.Run({ api, query, skip, load });
|
|
30814
|
+
ids.push(...(items !== null && items !== void 0 ? items : []));
|
|
30815
|
+
if (!items || items.length < load) {
|
|
30816
|
+
return ids;
|
|
30817
|
+
}
|
|
30818
|
+
}
|
|
30819
|
+
}
|
|
30820
|
+
catch (e) {
|
|
30821
|
+
// An API without the historic criterion gets asked about every node, as before it existed.
|
|
30822
|
+
console.warn("AssemblyRenderManager.historicEntityIds()", e);
|
|
30823
|
+
}
|
|
30824
|
+
return Object.keys((_b = this.hierarchyNodeByEntityId) !== null && _b !== void 0 ? _b : { [this.rootId]: null });
|
|
30825
|
+
}
|
|
30826
|
+
/**
|
|
30827
|
+
* Builds a matrix from the row-major rows that Bruce/AssemblyPosition and AssemblyWorldPosition use.
|
|
30828
|
+
* @param rows
|
|
30829
|
+
*/
|
|
30830
|
+
static MatrixFromRows(rows) {
|
|
30831
|
+
if (!Array.isArray(rows) || rows.length !== 4 || rows.some(r => !Array.isArray(r) || r.length !== 4)) {
|
|
30832
|
+
return null;
|
|
30833
|
+
}
|
|
30834
|
+
return new Cesium.Matrix4(+rows[0][0], +rows[0][1], +rows[0][2], +rows[0][3], +rows[1][0], +rows[1][1], +rows[1][2], +rows[1][3], +rows[2][0], +rows[2][1], +rows[2][2], +rows[2][3], +rows[3][0], +rows[3][1], +rows[3][2], +rows[3][3]);
|
|
30835
|
+
}
|
|
30836
|
+
/**
|
|
30837
|
+
* The inverse of MatrixFromRows.
|
|
30838
|
+
* @param matrix
|
|
30839
|
+
*/
|
|
30840
|
+
static RowsFromMatrix(matrix) {
|
|
30841
|
+
const rows = [];
|
|
30842
|
+
for (let r = 0; r < 4; r++) {
|
|
30843
|
+
const row = Cesium.Matrix4.getRow(matrix, r, new Cesium.Cartesian4());
|
|
30844
|
+
rows.push([row.x, row.y, row.z, row.w]);
|
|
30845
|
+
}
|
|
30846
|
+
return rows;
|
|
29310
30847
|
}
|
|
29311
30848
|
Init() {
|
|
29312
30849
|
this.renderPriority = this.item.renderPriority;
|
|
@@ -29314,6 +30851,7 @@
|
|
|
29314
30851
|
this.renderPriority = 1;
|
|
29315
30852
|
}
|
|
29316
30853
|
(async () => {
|
|
30854
|
+
var _a, _b;
|
|
29317
30855
|
if (this.disposed) {
|
|
29318
30856
|
return;
|
|
29319
30857
|
}
|
|
@@ -29329,6 +30867,8 @@
|
|
|
29329
30867
|
this.hierarchyUpdate.Trigger(hierarchy);
|
|
29330
30868
|
// Build hierarchy node cache for quick lookups
|
|
29331
30869
|
this.buildHierarchyNodes(hierarchy);
|
|
30870
|
+
// A set for membership, since searching the list took minutes at 200k nodes.
|
|
30871
|
+
const seenIds = new Set();
|
|
29332
30872
|
const traverseHierarchy = (node, leavesOnly) => {
|
|
29333
30873
|
var _a;
|
|
29334
30874
|
if (node.Children) {
|
|
@@ -29337,7 +30877,8 @@
|
|
|
29337
30877
|
}
|
|
29338
30878
|
}
|
|
29339
30879
|
const addNode = !leavesOnly || !((_a = node.Children) === null || _a === void 0 ? void 0 : _a.length);
|
|
29340
|
-
if (addNode && node.ID && !
|
|
30880
|
+
if (addNode && node.ID && !seenIds.has(node.ID)) {
|
|
30881
|
+
seenIds.add(node.ID);
|
|
29341
30882
|
flatEntityIds.push(node.ID);
|
|
29342
30883
|
}
|
|
29343
30884
|
};
|
|
@@ -29356,17 +30897,17 @@
|
|
|
29356
30897
|
}
|
|
29357
30898
|
}
|
|
29358
30899
|
this.rootId = rootId;
|
|
30900
|
+
this.simulationRemoval = (_b = (_a = this.simulation) === null || _a === void 0 ? void 0 : _a.OnChanged.Subscribe((change) => this.onSimulationChanged(change))) !== null && _b !== void 0 ? _b : null;
|
|
29359
30901
|
if (this.disposed) {
|
|
29360
30902
|
return;
|
|
29361
30903
|
}
|
|
29362
30904
|
else if (flatEntityIds.length === 0) {
|
|
29363
30905
|
return;
|
|
29364
30906
|
}
|
|
30907
|
+
// Followed while the parts load: in a large Assembly loading takes far longer than anyone waits.
|
|
30908
|
+
const following = this.viewerDateTimeSub();
|
|
29365
30909
|
await this.processEntityIds(flatEntityIds);
|
|
29366
|
-
|
|
29367
|
-
return;
|
|
29368
|
-
}
|
|
29369
|
-
this.viewerDateTimeSub();
|
|
30910
|
+
await following;
|
|
29370
30911
|
}
|
|
29371
30912
|
catch (e) {
|
|
29372
30913
|
console.error("AssemblyRenderManager.Manager.Init()", e);
|
|
@@ -29393,7 +30934,7 @@
|
|
|
29393
30934
|
if (!batch.length) {
|
|
29394
30935
|
continue;
|
|
29395
30936
|
}
|
|
29396
|
-
const { entities:
|
|
30937
|
+
const { entities: apiEntities } = await BModels.Entity.GetListByIds({
|
|
29397
30938
|
entityIds: batch,
|
|
29398
30939
|
api: this.getters.GetBruceApi(),
|
|
29399
30940
|
expandLODs: true,
|
|
@@ -29405,6 +30946,16 @@
|
|
|
29405
30946
|
if (this.disposed) {
|
|
29406
30947
|
return;
|
|
29407
30948
|
}
|
|
30949
|
+
const fetched = apiEntities.map((entity) => {
|
|
30950
|
+
var _a;
|
|
30951
|
+
this.apiEntities.set((_a = entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID, entity);
|
|
30952
|
+
return this.withSimulation(entity);
|
|
30953
|
+
});
|
|
30954
|
+
for (const entity of fetched) {
|
|
30955
|
+
this.absorbNode(entity);
|
|
30956
|
+
}
|
|
30957
|
+
// Placements already worked out did not know about this batch's positions.
|
|
30958
|
+
this.pose = null;
|
|
29408
30959
|
for (const entity of fetched) {
|
|
29409
30960
|
// Filter for valid LODs then take the highest quality one (lowest level).
|
|
29410
30961
|
let lods = entity.Bruce.LOD || [];
|
|
@@ -29427,57 +30978,46 @@
|
|
|
29427
30978
|
// Won't bother with vector data for this experiment.
|
|
29428
30979
|
continue;
|
|
29429
30980
|
}
|
|
29430
|
-
const
|
|
29431
|
-
|
|
29432
|
-
|
|
29433
|
-
|
|
29434
|
-
|
|
29435
|
-
returnHeightRef: Cesium.HeightReference.NONE,
|
|
29436
|
-
allowRendered: false
|
|
29437
|
-
});
|
|
29438
|
-
if (!recordPos3d) {
|
|
30981
|
+
const entityId = entity.Bruce.ID;
|
|
30982
|
+
this.measureGroundOffset(entity);
|
|
30983
|
+
// A placement cached before the offset was known would leave the part off its ground.
|
|
30984
|
+
(_a = this.pose) === null || _a === void 0 ? void 0 : _a.placements.delete(entityId);
|
|
30985
|
+
if (!this.canMove(entityId) && !this.placementAt(entityId, this.viewer.clock.currentTime)) {
|
|
29439
30986
|
continue;
|
|
29440
30987
|
}
|
|
29441
|
-
const entityId = entity.Bruce.ID;
|
|
29442
|
-
if (entityId === this.rootId || !this.rootPos3d) {
|
|
29443
|
-
// Nothing has a frame to be relative to until the root has one.
|
|
29444
|
-
this.rootPos3d = recordPos3d;
|
|
29445
|
-
this.rootHeading = EnsureNumber((_a = entity.Bruce.Transform) === null || _a === void 0 ? void 0 : _a.heading, 0);
|
|
29446
|
-
}
|
|
29447
|
-
// Held as an offset in the root's frame, so a root move places it again without
|
|
29448
|
-
// anything being fetched. Where the root already moved, that is where it goes.
|
|
29449
|
-
const offset = Manager.ChildOffsetIn(this.rootPos3d, this.rootHeading, recordPos3d);
|
|
29450
|
-
const pos3d = Manager.ChildPositionFrom(this.rootPos3d, this.rootHeading, offset);
|
|
29451
|
-
// Optional: a child with no world matrix is still worth drawing, it just has no
|
|
29452
|
-
// orientation of its own to apply.
|
|
29453
|
-
const orient = exports.EntityUtils.GetAssemblyOrientation(entity, pos3d);
|
|
29454
30988
|
const cEntity = this.viewer.entities.add({
|
|
29455
30989
|
model: {
|
|
29456
30990
|
uri: lod["ClientFile"].URL,
|
|
29457
30991
|
...exports.ViewUtils.DefaultLightingProps(),
|
|
29458
30992
|
heightReference: Cesium.HeightReference.NONE
|
|
29459
|
-
}
|
|
29460
|
-
position: pos3d,
|
|
29461
|
-
orientation: orient ? new Cesium.ConstantProperty(orient) : undefined
|
|
30993
|
+
}
|
|
29462
30994
|
});
|
|
29463
30995
|
const prior = this.children.get(entityId);
|
|
29464
30996
|
if (prior && prior.cEntity !== cEntity) {
|
|
29465
30997
|
RemoveCEntityFromScene(this.viewer, prior.cEntity);
|
|
29466
30998
|
}
|
|
29467
|
-
|
|
30999
|
+
const child = {
|
|
29468
31000
|
entityId: entityId,
|
|
29469
31001
|
entity: entity,
|
|
29470
|
-
cEntity: cEntity
|
|
29471
|
-
|
|
29472
|
-
|
|
31002
|
+
cEntity: cEntity
|
|
31003
|
+
};
|
|
31004
|
+
this.children.set(entityId, child);
|
|
31005
|
+
// Joins the animated set by its own ancestry, so a late part never re-walks the rest.
|
|
31006
|
+
if (this.hasAnimatedAncestry(entityId)) {
|
|
31007
|
+
this.animatedIds.add(entityId);
|
|
31008
|
+
}
|
|
29473
31009
|
const geometryRadius = BModels.Entity.GetValue({
|
|
29474
31010
|
entity: entity,
|
|
29475
31011
|
path: ["Bruce", "GeometryRadius"]
|
|
29476
31012
|
});
|
|
29477
31013
|
cEntity.model["_radius"] = geometryRadius;
|
|
29478
31014
|
cEntity.model["_radiusLoaded"] = true;
|
|
31015
|
+
// Placed before it is registered, so the register never measures a visual with no position.
|
|
31016
|
+
this.bindPlacement(child);
|
|
29479
31017
|
this.visualsManager.AddRego({
|
|
29480
31018
|
rego: {
|
|
31019
|
+
moving: child.moving ? true : null,
|
|
31020
|
+
motion: (time) => this.motionAt(entity.Bruce.ID, time),
|
|
29481
31021
|
canEdit: true,
|
|
29482
31022
|
entityId: entity.Bruce.ID,
|
|
29483
31023
|
menuItemId: this.item.id,
|
|
@@ -29497,47 +31037,89 @@
|
|
|
29497
31037
|
}
|
|
29498
31038
|
}
|
|
29499
31039
|
/*
|
|
29500
|
-
* Follows the scene clock
|
|
29501
|
-
* Only the root is fetched: everything under it is placed through ApplyRootPosition.
|
|
31040
|
+
* Follows the scene clock, moving the Assembly along the root's history and each joint along its own.
|
|
29502
31041
|
*/
|
|
29503
|
-
viewerDateTimeSub() {
|
|
29504
|
-
|
|
31042
|
+
async viewerDateTimeSub() {
|
|
31043
|
+
var _a;
|
|
31044
|
+
const historic = Manager.IsHistoric(this.item);
|
|
31045
|
+
if (this.followingClock || !this.rootId || (!historic && !this.simulatedNodeIds().length)) {
|
|
29505
31046
|
return;
|
|
29506
31047
|
}
|
|
31048
|
+
this.followingClock = true;
|
|
29507
31049
|
const api = this.getters.GetBruceApi();
|
|
29508
|
-
const
|
|
31050
|
+
const historicIds = historic ? await this.historicEntityIds(api) : [];
|
|
31051
|
+
if (this.disposed) {
|
|
31052
|
+
return;
|
|
31053
|
+
}
|
|
31054
|
+
const attrKey = (_a = this.item.BruceEntity) === null || _a === void 0 ? void 0 : _a.historicAttrKey;
|
|
29509
31055
|
const recordHeightRef = Manager.GetRecordHeightRef(this.item);
|
|
29510
31056
|
const toPosses = (records) => {
|
|
29511
31057
|
return exports.CesiumAnimatedProperty.GetSeriesPossesForHistoricEntity(this.viewer, recordHeightRef, Cesium.HeightReference.NONE, records);
|
|
29512
31058
|
};
|
|
29513
|
-
|
|
29514
|
-
|
|
29515
|
-
|
|
29516
|
-
|
|
29517
|
-
|
|
29518
|
-
|
|
29519
|
-
|
|
29520
|
-
|
|
29521
|
-
|
|
29522
|
-
|
|
31059
|
+
if (historicIds.length) {
|
|
31060
|
+
this.historicGatherer = new HistoricGatherer({
|
|
31061
|
+
viewer: this.viewer,
|
|
31062
|
+
getApi: () => api,
|
|
31063
|
+
getAttrKey: () => attrKey,
|
|
31064
|
+
// For clock desync between whatever wrote the record and this client.
|
|
31065
|
+
paddingMs: 15000,
|
|
31066
|
+
simulation: this.simulation,
|
|
31067
|
+
onRecords: (recordsByIds) => {
|
|
31068
|
+
var _a;
|
|
31069
|
+
const records = recordsByIds[this.rootId];
|
|
31070
|
+
if (records === null || records === void 0 ? void 0 : records.length) {
|
|
31071
|
+
const posses = toPosses(records);
|
|
31072
|
+
(_a = this.historicAnimation) === null || _a === void 0 ? void 0 : _a.SupplementSeries(posses);
|
|
31073
|
+
const moved = this.absorbRootRecords(posses, false);
|
|
31074
|
+
if (this.absorbRootAttitude(records, false) || moved) {
|
|
31075
|
+
this.repose();
|
|
31076
|
+
}
|
|
31077
|
+
}
|
|
31078
|
+
// Joint records go in together once the gather lands, so parents never draw apart from children.
|
|
29523
31079
|
}
|
|
29524
|
-
}
|
|
29525
|
-
}
|
|
31080
|
+
});
|
|
31081
|
+
}
|
|
31082
|
+
// The nodes with history: what the API has records for, and whatever has been simulated since.
|
|
31083
|
+
const nodes = () => Array.from(new Set([...historicIds, ...this.simulatedNodeIds()]));
|
|
29526
31084
|
let gathering = false;
|
|
29527
|
-
|
|
31085
|
+
// Set when what is held changed without a fetch, eg. a simulated record arriving.
|
|
31086
|
+
let stale = true;
|
|
31087
|
+
const gather = async (force) => {
|
|
31088
|
+
var _a, _b;
|
|
31089
|
+
stale = stale || Boolean(force);
|
|
29528
31090
|
if (this.disposed || gathering) {
|
|
29529
31091
|
return;
|
|
29530
31092
|
}
|
|
29531
31093
|
gathering = true;
|
|
29532
31094
|
try {
|
|
29533
|
-
const
|
|
31095
|
+
const ids = nodes();
|
|
31096
|
+
let fetched = false;
|
|
31097
|
+
let recordsByIds;
|
|
31098
|
+
if (this.historicGatherer) {
|
|
31099
|
+
({ fetched, recordsByIds } = await this.historicGatherer.Gather(ids.map(id => ({ Bruce: { ID: id } }))));
|
|
31100
|
+
}
|
|
31101
|
+
else {
|
|
31102
|
+
recordsByIds = (_b = (_a = this.simulation) === null || _a === void 0 ? void 0 : _a.MergeRecords({}, attrKey, ids)) !== null && _b !== void 0 ? _b : {};
|
|
31103
|
+
}
|
|
29534
31104
|
if (this.disposed) {
|
|
29535
31105
|
return;
|
|
29536
31106
|
}
|
|
31107
|
+
// A gather served from what is held changes nothing and runs every tick, so it stops here.
|
|
31108
|
+
if (!fetched && !stale && this.jointSeries.size) {
|
|
31109
|
+
return;
|
|
31110
|
+
}
|
|
31111
|
+
stale = false;
|
|
31112
|
+
let changed = this.absorbJointRecords(recordsByIds);
|
|
29537
31113
|
const posses = toPosses(recordsByIds[this.rootId] || []);
|
|
31114
|
+
changed = this.absorbRootRecords(posses, true) || changed;
|
|
31115
|
+
changed = this.absorbRootAttitude(recordsByIds[this.rootId] || [], true) || changed;
|
|
31116
|
+
if (changed) {
|
|
31117
|
+
this.repose();
|
|
31118
|
+
}
|
|
29538
31119
|
if (!posses.length) {
|
|
29539
31120
|
return;
|
|
29540
31121
|
}
|
|
31122
|
+
// Held for the trail alone; where the root is drawn comes from rootAt(), never from its smoothing.
|
|
29541
31123
|
if (!this.historicAnimation) {
|
|
29542
31124
|
this.historicAnimation = new exports.CesiumAnimatedProperty.AnimatePositionSeries({
|
|
29543
31125
|
posses: posses,
|
|
@@ -29551,15 +31133,21 @@
|
|
|
29551
31133
|
}
|
|
29552
31134
|
catch (e) {
|
|
29553
31135
|
console.error("AssemblyRenderManager.viewerDateTimeSub()", e);
|
|
31136
|
+
// A gather that throws would throw again straight away, so the retry waits for the next tick.
|
|
31137
|
+
stale = false;
|
|
29554
31138
|
}
|
|
29555
31139
|
finally {
|
|
29556
31140
|
gathering = false;
|
|
31141
|
+
// A change that landed mid-gather is not left for a tick that may never come on a paused clock.
|
|
31142
|
+
if (stale && !this.disposed) {
|
|
31143
|
+
gather();
|
|
31144
|
+
}
|
|
29557
31145
|
}
|
|
29558
31146
|
};
|
|
31147
|
+
this.regather = gather;
|
|
29559
31148
|
gather();
|
|
29560
31149
|
let lastTime = null;
|
|
29561
31150
|
const tickRemoval = this.viewer.clock.onTick.addEventListener(() => {
|
|
29562
|
-
var _a;
|
|
29563
31151
|
if (this.disposed) {
|
|
29564
31152
|
return;
|
|
29565
31153
|
}
|
|
@@ -29569,15 +31157,9 @@
|
|
|
29569
31157
|
}
|
|
29570
31158
|
lastTime = now;
|
|
29571
31159
|
gather();
|
|
29572
|
-
|
|
29573
|
-
|
|
29574
|
-
|
|
29575
|
-
const pos3d = this.historicAnimation.GetValue();
|
|
29576
|
-
if (pos3d) {
|
|
29577
|
-
this.ApplyRootPosition({
|
|
29578
|
-
pos3d: pos3d,
|
|
29579
|
-
heading: (_a = this.historicAnimation.GetHeadingDegrees()) !== null && _a !== void 0 ? _a : this.rootHeading
|
|
29580
|
-
});
|
|
31160
|
+
// Moving parts read the clock themselves when drawn, so the scene only needs a new frame.
|
|
31161
|
+
if (this.rootSeries.length || this.rootAttitude.length || this.animatedIds.size) {
|
|
31162
|
+
this.viewer.scene.requestRender();
|
|
29581
31163
|
}
|
|
29582
31164
|
});
|
|
29583
31165
|
this.viewerDateTimeChangeRemoval = () => {
|
|
@@ -29586,6 +31168,10 @@
|
|
|
29586
31168
|
(_a = this.historicGatherer) === null || _a === void 0 ? void 0 : _a.Dispose();
|
|
29587
31169
|
this.historicGatherer = null;
|
|
29588
31170
|
this.historicAnimation = null;
|
|
31171
|
+
this.regather = null;
|
|
31172
|
+
this.rootSeries = [];
|
|
31173
|
+
this.rootAttitude = [];
|
|
31174
|
+
this.pose = null;
|
|
29589
31175
|
};
|
|
29590
31176
|
}
|
|
29591
31177
|
/*
|
|
@@ -29593,7 +31179,7 @@
|
|
|
29593
31179
|
*/
|
|
29594
31180
|
applyRootTrack() {
|
|
29595
31181
|
var _a;
|
|
29596
|
-
if (!this.item.historicDrawTrack || !this.historicAnimation || this.
|
|
31182
|
+
if (!this.item.historicDrawTrack || !this.historicAnimation || this.trail) {
|
|
29597
31183
|
return;
|
|
29598
31184
|
}
|
|
29599
31185
|
const trackStyle = exports.EntityRenderEngine.GetTrackStyle({
|
|
@@ -29603,41 +31189,35 @@
|
|
|
29603
31189
|
if (!trackStyle) {
|
|
29604
31190
|
return;
|
|
29605
31191
|
}
|
|
29606
|
-
this.
|
|
29607
|
-
id: BModels.ObjectUtils.UId(15),
|
|
29608
|
-
polyline: {
|
|
29609
|
-
positions: new Cesium.CallbackProperty(() => { var _a, _b; return (_b = (_a = this.historicAnimation) === null || _a === void 0 ? void 0 : _a.GetSeries()) !== null && _b !== void 0 ? _b : []; }, false),
|
|
29610
|
-
width: trackStyle.width,
|
|
29611
|
-
material: trackStyle.color,
|
|
29612
|
-
clampToGround: exports.EntityRenderEngine.GetHeightRefPolyline(Manager.GetRecordHeightRef(this.item))
|
|
29613
|
-
== Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
29614
|
-
zIndex: 0,
|
|
29615
|
-
classificationType: Cesium.ClassificationType.BOTH
|
|
29616
|
-
}
|
|
29617
|
-
});
|
|
29618
|
-
this.trackEntity._noEdit = true;
|
|
29619
|
-
this.trackEntity._noSelect = true;
|
|
29620
|
-
exports.CesiumEntityStyler.SetDefaultColor({
|
|
29621
|
-
color: trackStyle.color,
|
|
29622
|
-
entity: this.trackEntity,
|
|
31192
|
+
this.trail = new HistoricTrail.Trail({
|
|
29623
31193
|
viewer: this.viewer,
|
|
29624
|
-
|
|
29625
|
-
|
|
31194
|
+
color: trackStyle.color,
|
|
31195
|
+
width: trackStyle.width,
|
|
31196
|
+
clampToGround: exports.EntityRenderEngine.GetHeightRefPolyline(Manager.GetRecordHeightRef(this.item))
|
|
31197
|
+
== Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
31198
|
+
getSeries: () => { var _a, _b; return (_b = (_a = this.historicAnimation) === null || _a === void 0 ? void 0 : _a.GetSeries()) !== null && _b !== void 0 ? _b : []; },
|
|
31199
|
+
trailSeconds: HistoricTrail.GetTrailSeconds(this.item),
|
|
31200
|
+
// Where the root is drawn, so the trail ends on the model; nothing before the first record held.
|
|
31201
|
+
positionAt: (timeMs) => (this.rootSeries.length && timeMs >= this.rootSeries[0].timeMs ? this.rootPositionAt(timeMs) : null)
|
|
29626
31202
|
});
|
|
29627
31203
|
}
|
|
29628
31204
|
Dispose() {
|
|
29629
|
-
var _a;
|
|
31205
|
+
var _a, _b, _c;
|
|
29630
31206
|
if (this.disposed) {
|
|
29631
31207
|
return;
|
|
29632
31208
|
}
|
|
29633
31209
|
this.disposed = true;
|
|
29634
31210
|
(_a = this.viewerDateTimeChangeRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
29635
31211
|
this.viewerDateTimeChangeRemoval = null;
|
|
29636
|
-
|
|
29637
|
-
|
|
29638
|
-
|
|
29639
|
-
|
|
31212
|
+
(_b = this.simulationRemoval) === null || _b === void 0 ? void 0 : _b.call(this);
|
|
31213
|
+
this.simulationRemoval = null;
|
|
31214
|
+
this.apiEntities.clear();
|
|
31215
|
+
(_c = this.trail) === null || _c === void 0 ? void 0 : _c.Dispose();
|
|
31216
|
+
this.trail = null;
|
|
29640
31217
|
this.children.clear();
|
|
31218
|
+
this.localPositions.clear();
|
|
31219
|
+
this.jointSeries.clear();
|
|
31220
|
+
this.animatedIds.clear();
|
|
29641
31221
|
this.visualsManager.RemoveRegos({
|
|
29642
31222
|
menuItemId: this.item.id
|
|
29643
31223
|
});
|
|
@@ -30091,6 +31671,9 @@
|
|
|
30091
31671
|
this.OnSeriesUpdated.Trigger({ itemId, segments: [] });
|
|
30092
31672
|
}
|
|
30093
31673
|
}
|
|
31674
|
+
get Simulation() {
|
|
31675
|
+
return this.simulation;
|
|
31676
|
+
}
|
|
30094
31677
|
get Monitor() {
|
|
30095
31678
|
return this.sharedMonitor;
|
|
30096
31679
|
}
|
|
@@ -30116,6 +31699,7 @@
|
|
|
30116
31699
|
this.visualsRegister = visualsRegister;
|
|
30117
31700
|
this.sharedGetters = new exports.SharedGetters.Cache();
|
|
30118
31701
|
this.sharedHistoric = new SharedHistoricGetters.Cache();
|
|
31702
|
+
this.simulation = exports.EntitySimulation.Get(this.viewer);
|
|
30119
31703
|
this.sharedMonitor = new exports.CesiumViewMonitor.Monitor(this.viewer);
|
|
30120
31704
|
this.tilesetInitQueue = new InitQueueTileset(this.getters);
|
|
30121
31705
|
}
|
|
@@ -30329,7 +31913,8 @@
|
|
|
30329
31913
|
monitor: this.sharedMonitor,
|
|
30330
31914
|
item: params.item,
|
|
30331
31915
|
sharedGetters: this.sharedGetters,
|
|
30332
|
-
onSeriesDiscovered: (segment) => this.RecordSeriesSegment(rItem.id, segment)
|
|
31916
|
+
onSeriesDiscovered: (segment) => this.RecordSeriesSegment(rItem.id, segment),
|
|
31917
|
+
simulation: this.simulation
|
|
30333
31918
|
});
|
|
30334
31919
|
break;
|
|
30335
31920
|
case BModels.MenuItem.EType.EntitiesLoaded:
|
|
@@ -30338,7 +31923,8 @@
|
|
|
30338
31923
|
register: this.visualsRegister,
|
|
30339
31924
|
apiGetter: params.apiGetter,
|
|
30340
31925
|
monitor: this.sharedMonitor,
|
|
30341
|
-
item: params.item
|
|
31926
|
+
item: params.item,
|
|
31927
|
+
simulation: this.simulation
|
|
30342
31928
|
});
|
|
30343
31929
|
break;
|
|
30344
31930
|
case BModels.MenuItem.EType.EntitiesIds:
|
|
@@ -30348,7 +31934,8 @@
|
|
|
30348
31934
|
apiGetter: params.apiGetter,
|
|
30349
31935
|
monitor: this.sharedMonitor,
|
|
30350
31936
|
item: params.item,
|
|
30351
|
-
sharedHistoric: this.sharedHistoric
|
|
31937
|
+
sharedHistoric: this.sharedHistoric,
|
|
31938
|
+
simulation: this.simulation
|
|
30352
31939
|
});
|
|
30353
31940
|
break;
|
|
30354
31941
|
case BModels.MenuItem.EType.Entity:
|
|
@@ -30357,7 +31944,8 @@
|
|
|
30357
31944
|
register: this.visualsRegister,
|
|
30358
31945
|
apiGetter: params.apiGetter,
|
|
30359
31946
|
monitor: this.sharedMonitor,
|
|
30360
|
-
item: params.item
|
|
31947
|
+
item: params.item,
|
|
31948
|
+
simulation: this.simulation
|
|
30361
31949
|
});
|
|
30362
31950
|
break;
|
|
30363
31951
|
case BModels.MenuItem.EType.CadTileset:
|
|
@@ -30367,7 +31955,8 @@
|
|
|
30367
31955
|
getters: this.getters,
|
|
30368
31956
|
item: params.item,
|
|
30369
31957
|
modelSpace: params.modelSpace,
|
|
30370
|
-
initQueue: this.tilesetInitQueue
|
|
31958
|
+
initQueue: this.tilesetInitQueue,
|
|
31959
|
+
simulation: this.simulation
|
|
30371
31960
|
});
|
|
30372
31961
|
break;
|
|
30373
31962
|
case BModels.MenuItem.EType.Assembly:
|
|
@@ -30377,6 +31966,7 @@
|
|
|
30377
31966
|
getters: params.getters,
|
|
30378
31967
|
item: params.item,
|
|
30379
31968
|
modelSpace: params.modelSpace,
|
|
31969
|
+
simulation: this.simulation
|
|
30380
31970
|
});
|
|
30381
31971
|
break;
|
|
30382
31972
|
case BModels.MenuItem.EType.Osm:
|
|
@@ -30404,7 +31994,8 @@
|
|
|
30404
31994
|
getters: this.getters,
|
|
30405
31995
|
monitor: this.sharedMonitor,
|
|
30406
31996
|
item: params.item,
|
|
30407
|
-
initQueue: this.tilesetInitQueue
|
|
31997
|
+
initQueue: this.tilesetInitQueue,
|
|
31998
|
+
simulation: this.simulation
|
|
30408
31999
|
});
|
|
30409
32000
|
break;
|
|
30410
32001
|
case BModels.MenuItem.EType.Kml:
|
|
@@ -30428,7 +32019,8 @@
|
|
|
30428
32019
|
getters: params.getters,
|
|
30429
32020
|
item: params.item,
|
|
30430
32021
|
viewer: this.viewer,
|
|
30431
|
-
register: this.visualsRegister
|
|
32022
|
+
register: this.visualsRegister,
|
|
32023
|
+
simulation: this.simulation
|
|
30432
32024
|
});
|
|
30433
32025
|
break;
|
|
30434
32026
|
// Deprecated to support a few demos until new one is phased in.
|
|
@@ -30438,7 +32030,8 @@
|
|
|
30438
32030
|
apiGetter: params.apiGetter,
|
|
30439
32031
|
item: params.item,
|
|
30440
32032
|
register: this.visualsRegister,
|
|
30441
|
-
viewer: this.viewer
|
|
32033
|
+
viewer: this.viewer,
|
|
32034
|
+
simulation: this.simulation
|
|
30442
32035
|
});
|
|
30443
32036
|
break;
|
|
30444
32037
|
case BModels.MenuItem.EType.Relationships:
|
|
@@ -30446,7 +32039,8 @@
|
|
|
30446
32039
|
apiGetter: params.apiGetter,
|
|
30447
32040
|
item: params.item,
|
|
30448
32041
|
register: this.visualsRegister,
|
|
30449
|
-
viewer: this.viewer
|
|
32042
|
+
viewer: this.viewer,
|
|
32043
|
+
simulation: this.simulation
|
|
30450
32044
|
});
|
|
30451
32045
|
break;
|
|
30452
32046
|
case BModels.MenuItem.EType.GooglePhotoTileset:
|
|
@@ -30478,7 +32072,8 @@
|
|
|
30478
32072
|
item: params.item,
|
|
30479
32073
|
apiGetter: params.apiGetter,
|
|
30480
32074
|
monitor: this.sharedMonitor,
|
|
30481
|
-
visualsRegister: this.visualsRegister
|
|
32075
|
+
visualsRegister: this.visualsRegister,
|
|
32076
|
+
simulation: this.simulation
|
|
30482
32077
|
});
|
|
30483
32078
|
break;
|
|
30484
32079
|
// Any "none" type can be ignored. These are just containers for other items.
|
|
@@ -30691,6 +32286,63 @@
|
|
|
30691
32286
|
}
|
|
30692
32287
|
}
|
|
30693
32288
|
}
|
|
32289
|
+
/**
|
|
32290
|
+
* Draws Entities from data given here as well as, or instead of, the API's, until ClearSimulation is called.
|
|
32291
|
+
* @param params.set the simulation this belongs to, so it can be cleared apart from others. Default "default".
|
|
32292
|
+
* @param params.entities stand-ins that replace each Entity's record; API data for them is ignored.
|
|
32293
|
+
* @param params.patches partial Entities laid over each Entity; live API data shows where a patch is silent.
|
|
32294
|
+
* @param params.records historic records naming entityId, dateTime and only the data they change.
|
|
32295
|
+
* @param params.menuItemIds the items to redraw. By default every item already drawing one of the Entities.
|
|
32296
|
+
*/
|
|
32297
|
+
async Simulate(params) {
|
|
32298
|
+
const { set, entities, patches, records, menuItemIds } = params;
|
|
32299
|
+
this.simulation.SetEntities(entities, set);
|
|
32300
|
+
this.simulation.SetPatches(patches, set);
|
|
32301
|
+
this.simulation.SetRecords(records, set);
|
|
32302
|
+
const entityIds = [
|
|
32303
|
+
...(entities || []).map(x => { var _a; return (_a = x === null || x === void 0 ? void 0 : x.Bruce) === null || _a === void 0 ? void 0 : _a.ID; }),
|
|
32304
|
+
...(patches || []).map(x => { var _a; return (_a = x === null || x === void 0 ? void 0 : x.Bruce) === null || _a === void 0 ? void 0 : _a.ID; }),
|
|
32305
|
+
...(records || []).map(x => x === null || x === void 0 ? void 0 : x.entityId)
|
|
32306
|
+
];
|
|
32307
|
+
await this.reRenderSimulated(entityIds, menuItemIds);
|
|
32308
|
+
}
|
|
32309
|
+
/**
|
|
32310
|
+
* Stops simulating these Entities, or every Entity when none are named, and redraws them.
|
|
32311
|
+
* @param params.set the simulation to clear. By default every one.
|
|
32312
|
+
* @param params.entities clear the stand-ins and patches. Default true.
|
|
32313
|
+
* @param params.records clear the historic records. Default true.
|
|
32314
|
+
*/
|
|
32315
|
+
async ClearSimulation(params) {
|
|
32316
|
+
var _a;
|
|
32317
|
+
const entityIds = (_a = params === null || params === void 0 ? void 0 : params.entityIds) !== null && _a !== void 0 ? _a : [
|
|
32318
|
+
...this.simulation.GetEntityIds(),
|
|
32319
|
+
...this.simulation.GetRecordEntityIds()
|
|
32320
|
+
];
|
|
32321
|
+
this.simulation.Clear({ set: params === null || params === void 0 ? void 0 : params.set, entityIds, entities: params === null || params === void 0 ? void 0 : params.entities, records: params === null || params === void 0 ? void 0 : params.records });
|
|
32322
|
+
await this.reRenderSimulated(entityIds, params === null || params === void 0 ? void 0 : params.menuItemIds);
|
|
32323
|
+
}
|
|
32324
|
+
/*
|
|
32325
|
+
* Redraws simulated Entities only in the items holding a visual for them, unless the caller names the items.
|
|
32326
|
+
*/
|
|
32327
|
+
async reRenderSimulated(entityIds, menuItemIds) {
|
|
32328
|
+
const ids = Array.from(new Set(entityIds.filter(x => !!x)));
|
|
32329
|
+
if (!ids.length) {
|
|
32330
|
+
return;
|
|
32331
|
+
}
|
|
32332
|
+
const items = (menuItemIds === null || menuItemIds === void 0 ? void 0 : menuItemIds.length) ? this.items.filter(x => menuItemIds.includes(x.id)) : this.items;
|
|
32333
|
+
for (const item of items) {
|
|
32334
|
+
// Some managers follow the store themselves, and redrawing them here would only repeat that.
|
|
32335
|
+
if (!item.renderManager || item.renderManager.FollowsSimulation) {
|
|
32336
|
+
continue;
|
|
32337
|
+
}
|
|
32338
|
+
const drawn = (menuItemIds === null || menuItemIds === void 0 ? void 0 : menuItemIds.length)
|
|
32339
|
+
? ids
|
|
32340
|
+
: ids.filter(id => this.visualsRegister.GetRego({ entityId: id, menuItemId: item.id }));
|
|
32341
|
+
if (drawn.length) {
|
|
32342
|
+
await item.renderManager.ReRender({ entityIds: drawn, force: true });
|
|
32343
|
+
}
|
|
32344
|
+
}
|
|
32345
|
+
}
|
|
30694
32346
|
}
|
|
30695
32347
|
MenuItemManager.Manager = Manager;
|
|
30696
32348
|
})(exports.MenuItemManager || (exports.MenuItemManager = {}));
|
|
@@ -39227,6 +40879,7 @@
|
|
|
39227
40879
|
}
|
|
39228
40880
|
class WidgetInfoView extends exports.Widget.AWidget {
|
|
39229
40881
|
constructor(params) {
|
|
40882
|
+
var _a;
|
|
39230
40883
|
super(params);
|
|
39231
40884
|
this.STYLESHEET_ID = "nextspace-info-view-stylesheet";
|
|
39232
40885
|
this._generateStyles();
|
|
@@ -39236,10 +40889,19 @@
|
|
|
39236
40889
|
this._updateInfoView();
|
|
39237
40890
|
}
|
|
39238
40891
|
});
|
|
40892
|
+
// The selected Entity is read once per selection, so a change to its simulation reads it again.
|
|
40893
|
+
this._simulationRemoval = (_a = this._manager.Simulation) === null || _a === void 0 ? void 0 : _a.OnChanged.Subscribe((change) => {
|
|
40894
|
+
if (this._lastSelectedId && change.entityIds.includes(this._lastSelectedId)) {
|
|
40895
|
+
this._lastSelectedId = null;
|
|
40896
|
+
this._updateInfoView();
|
|
40897
|
+
}
|
|
40898
|
+
});
|
|
39239
40899
|
}
|
|
39240
40900
|
Dispose() {
|
|
40901
|
+
var _a;
|
|
39241
40902
|
super.Dispose();
|
|
39242
40903
|
this._updateRemoval();
|
|
40904
|
+
(_a = this._simulationRemoval) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
39243
40905
|
}
|
|
39244
40906
|
_generateStyles() {
|
|
39245
40907
|
if (document.getElementById(this.STYLESHEET_ID)) {
|
|
@@ -39701,7 +41363,7 @@
|
|
|
39701
41363
|
}
|
|
39702
41364
|
}
|
|
39703
41365
|
async _updateInfoView() {
|
|
39704
|
-
var
|
|
41366
|
+
var _a, _c, _d, _e, _f;
|
|
39705
41367
|
const selectedIds = this._manager.VisualsRegister.GetSelected();
|
|
39706
41368
|
if (selectedIds.length <= 0) {
|
|
39707
41369
|
this._element.style.display = "none";
|
|
@@ -39723,17 +41385,20 @@
|
|
|
39723
41385
|
this._summary.innerHTML = "";
|
|
39724
41386
|
this._imageContainer.style.display = "none";
|
|
39725
41387
|
const api = this._apiGetters.GetBruceApi();
|
|
39726
|
-
|
|
41388
|
+
// A simulated Entity is shown as simulated: a stand-in as is, a patch over what the API has.
|
|
41389
|
+
const simulation = this._manager.Simulation;
|
|
41390
|
+
let entity = (_a = simulation === null || simulation === void 0 ? void 0 : simulation.GetEntity(firstId)) !== null && _a !== void 0 ? _a : (await BModels.Entity.Get({
|
|
39727
41391
|
entityId: firstId,
|
|
39728
41392
|
api: api,
|
|
39729
41393
|
migrated: true,
|
|
39730
41394
|
expandLocation: DISPLAY_ASSEMBLY_INFO
|
|
39731
|
-
});
|
|
41395
|
+
})).entity;
|
|
41396
|
+
entity = entity && simulation ? simulation.Resolve(firstId, entity) : entity;
|
|
39732
41397
|
if (!entity) {
|
|
39733
41398
|
throw (new Error("Could not get entity."));
|
|
39734
41399
|
}
|
|
39735
41400
|
const { entityType } = await BModels.EntityType.Get({
|
|
39736
|
-
entityTypeId: (
|
|
41401
|
+
entityTypeId: (_c = entity.Bruce) === null || _c === void 0 ? void 0 : _c["EntityType.ID"],
|
|
39737
41402
|
api: api
|
|
39738
41403
|
});
|
|
39739
41404
|
let name = BModels.Entity.CalculateName({
|
|
@@ -39756,7 +41421,7 @@
|
|
|
39756
41421
|
const defaultImage = images.length ? images[0] : null;
|
|
39757
41422
|
if (defaultImage) {
|
|
39758
41423
|
this._imageContainer.style.display = "flex";
|
|
39759
|
-
const url = (
|
|
41424
|
+
const url = (_d = defaultImage.URL) !== null && _d !== void 0 ? _d : BModels.ClientFile.GetUrl({
|
|
39760
41425
|
fileId: defaultImage["ClientFile.ID"],
|
|
39761
41426
|
api: api,
|
|
39762
41427
|
viaCdn: true
|
|
@@ -39769,7 +41434,7 @@
|
|
|
39769
41434
|
this._summary.appendChild(detailsTitle);
|
|
39770
41435
|
// TODO: Selection for what mode instead of always doing "all".
|
|
39771
41436
|
const attributes = gatherAttributes(EMode.ShowAll, entity, entityType);
|
|
39772
|
-
const groups = groupAttributes(attributes, (
|
|
41437
|
+
const groups = groupAttributes(attributes, (_f = (_e = entityType.DataSchema) === null || _e === void 0 ? void 0 : _e.Structure) !== null && _f !== void 0 ? _f : []);
|
|
39773
41438
|
for (const group of groups) {
|
|
39774
41439
|
this._generateAttrGroup(entityType, entity, group);
|
|
39775
41440
|
}
|
|
@@ -42363,7 +44028,7 @@
|
|
|
42363
44028
|
* @param params
|
|
42364
44029
|
*/
|
|
42365
44030
|
async function GetLocation(params) {
|
|
42366
|
-
var _a, _b, _c, _d;
|
|
44031
|
+
var _a, _b, _c, _d, _e;
|
|
42367
44032
|
let { api, viewer, samples, visualRegister, minimumAlt, paddingAlt } = params;
|
|
42368
44033
|
if (!paddingAlt) {
|
|
42369
44034
|
paddingAlt = 0;
|
|
@@ -42911,6 +44576,13 @@
|
|
|
42911
44576
|
recordSamples.push(sample);
|
|
42912
44577
|
}
|
|
42913
44578
|
}
|
|
44579
|
+
// A simulated Entity is where its blob says, never where the API has it.
|
|
44580
|
+
const simulation = exports.EntitySimulation.Get(viewer);
|
|
44581
|
+
for (const sample of recordSamples) {
|
|
44582
|
+
if (!sample.entity && sample.entityId) {
|
|
44583
|
+
sample.entity = (_e = simulation === null || simulation === void 0 ? void 0 : simulation.GetEntity(sample.entityId)) !== null && _e !== void 0 ? _e : sample.entity;
|
|
44584
|
+
}
|
|
44585
|
+
}
|
|
42914
44586
|
// Bulk requesting Entity records.
|
|
42915
44587
|
try {
|
|
42916
44588
|
const toSearchIds = recordSamples.filter(x => !x.entity && x.entityId).map(x => x.entityId);
|
|
@@ -42931,7 +44603,8 @@
|
|
|
42931
44603
|
for (let i = 0; i < recordSamples.length; i++) {
|
|
42932
44604
|
const sample = recordSamples[i];
|
|
42933
44605
|
if (!sample.entity && sample.entityId) {
|
|
42934
|
-
|
|
44606
|
+
const read = entitiesMap.get(sample.entityId);
|
|
44607
|
+
sample.entity = read && simulation ? simulation.Overlay(read) : read;
|
|
42935
44608
|
}
|
|
42936
44609
|
}
|
|
42937
44610
|
}
|
|
@@ -42945,6 +44618,11 @@
|
|
|
42945
44618
|
// We have to detect whatever is an Assembly Entity and expand it.
|
|
42946
44619
|
try {
|
|
42947
44620
|
const needsExpansion = recordSamples.filter(x => {
|
|
44621
|
+
var _a;
|
|
44622
|
+
// Expanding reads the API's record, which would replace the stand-in or undo the patch.
|
|
44623
|
+
if (simulation === null || simulation === void 0 ? void 0 : simulation.HasBlob((_a = x.entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID)) {
|
|
44624
|
+
return false;
|
|
44625
|
+
}
|
|
42948
44626
|
const assemblyPos = BModels.Entity.GetValue({
|
|
42949
44627
|
entity: x.entity,
|
|
42950
44628
|
path: ["Bruce", "AssemblyPosition"]
|
|
@@ -43303,6 +44981,21 @@
|
|
|
43303
44981
|
return Cesium.Quaternion.multiply(combined, _assemblyAxisShift, new Cesium.Quaternion());
|
|
43304
44982
|
}
|
|
43305
44983
|
EntityUtils.GetAssemblyOrientation = GetAssemblyOrientation;
|
|
44984
|
+
/**
|
|
44985
|
+
* Returns the orientation a part's model is drawn with, for its world matrix within the root's frame.
|
|
44986
|
+
* @param rootFrame the root's frame in the world, eg. from Transforms.headingPitchRollToFixedFrame.
|
|
44987
|
+
* @param world the part's matrix within the root's frame.
|
|
44988
|
+
*/
|
|
44989
|
+
function GetAssemblyOrientationInFrame(rootFrame, world) {
|
|
44990
|
+
if (!rootFrame || !world) {
|
|
44991
|
+
return null;
|
|
44992
|
+
}
|
|
44993
|
+
const combined = Cesium.Matrix4.multiply(rootFrame, world, new Cesium.Matrix4());
|
|
44994
|
+
const rotation = Cesium.Matrix4.getRotation(combined, new Cesium.Matrix3());
|
|
44995
|
+
const q = Cesium.Quaternion.fromRotationMatrix(rotation);
|
|
44996
|
+
return Cesium.Quaternion.multiply(q, _assemblyAxisShift, new Cesium.Quaternion());
|
|
44997
|
+
}
|
|
44998
|
+
EntityUtils.GetAssemblyOrientationInFrame = GetAssemblyOrientationInFrame;
|
|
43306
44999
|
/**
|
|
43307
45000
|
* Returns the position of the entity.
|
|
43308
45001
|
* This is an async variant that checks for related assemblies.
|
|
@@ -43312,6 +45005,18 @@
|
|
|
43312
45005
|
*/
|
|
43313
45006
|
async function GetPosAsync(params) {
|
|
43314
45007
|
let { viewer, entityId, entity, visualRegister, returnHeightRef, recordHeightRef, api } = params;
|
|
45008
|
+
// A simulated Entity is where its stand-in says, or where its patch puts the API's record.
|
|
45009
|
+
const simulation = exports.EntitySimulation.Get(viewer);
|
|
45010
|
+
entity = entity !== null && entity !== void 0 ? entity : simulation === null || simulation === void 0 ? void 0 : simulation.GetEntity(entityId);
|
|
45011
|
+
if (!entity && entityId && (simulation === null || simulation === void 0 ? void 0 : simulation.HasBlob(entityId))) {
|
|
45012
|
+
try {
|
|
45013
|
+
const read = (await BModels.Entity.Get({ api, entityId, expandLocation: false, migrated: true })).entity;
|
|
45014
|
+
entity = read ? simulation.Overlay(read) : null;
|
|
45015
|
+
}
|
|
45016
|
+
catch (e) {
|
|
45017
|
+
console.error(e);
|
|
45018
|
+
}
|
|
45019
|
+
}
|
|
43315
45020
|
// We have issues where imported BRZ files have improper expandLocation=true info.
|
|
43316
45021
|
// As a result we'll double-request the data.
|
|
43317
45022
|
// If there is a location then we won't bother using expanded information.
|
|
@@ -53273,7 +54978,7 @@ void main() {
|
|
|
53273
54978
|
StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
|
|
53274
54979
|
})(exports.StyleUtils || (exports.StyleUtils = {}));
|
|
53275
54980
|
|
|
53276
|
-
const VERSION = "7.6.
|
|
54981
|
+
const VERSION = "7.6.2";
|
|
53277
54982
|
/**
|
|
53278
54983
|
* Updates the environment instance used by bruce-cesium to one specified.
|
|
53279
54984
|
* This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
|