123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759 |
- import AssociativeArray from "../Core/AssociativeArray.js";
- import Cartesian3 from "../Core/Cartesian3.js";
- import defined from "../Core/defined.js";
- import destroyObject from "../Core/destroyObject.js";
- import DeveloperError from "../Core/DeveloperError.js";
- import JulianDate from "../Core/JulianDate.js";
- import Matrix3 from "../Core/Matrix3.js";
- import Matrix4 from "../Core/Matrix4.js";
- import ReferenceFrame from "../Core/ReferenceFrame.js";
- import TimeInterval from "../Core/TimeInterval.js";
- import Transforms from "../Core/Transforms.js";
- import PolylineCollection from "../Scene/PolylineCollection.js";
- import SceneMode from "../Scene/SceneMode.js";
- import CompositePositionProperty from "./CompositePositionProperty.js";
- import ConstantPositionProperty from "./ConstantPositionProperty.js";
- import MaterialProperty from "./MaterialProperty.js";
- import Property from "./Property.js";
- import ReferenceProperty from "./ReferenceProperty.js";
- import SampledPositionProperty from "./SampledPositionProperty.js";
- import ScaledPositionProperty from "./ScaledPositionProperty.js";
- import TimeIntervalCollectionPositionProperty from "./TimeIntervalCollectionPositionProperty.js";
- const defaultResolution = 60.0;
- const defaultWidth = 1.0;
- const scratchTimeInterval = new TimeInterval();
- const subSampleCompositePropertyScratch = new TimeInterval();
- const subSampleIntervalPropertyScratch = new TimeInterval();
- function EntityData(entity) {
- this.entity = entity;
- this.polyline = undefined;
- this.index = undefined;
- this.updater = undefined;
- }
- function subSampleSampledProperty(
- property,
- start,
- stop,
- times,
- updateTime,
- referenceFrame,
- maximumStep,
- startingIndex,
- result
- ) {
- let r = startingIndex;
- //Always step exactly on start (but only use it if it exists.)
- let tmp;
- tmp = property.getValueInReferenceFrame(start, referenceFrame, result[r]);
- if (defined(tmp)) {
- result[r++] = tmp;
- }
- let steppedOnNow =
- !defined(updateTime) ||
- JulianDate.lessThanOrEquals(updateTime, start) ||
- JulianDate.greaterThanOrEquals(updateTime, stop);
- //Iterate over all interval times and add the ones that fall in our
- //time range. Note that times can contain data outside of
- //the intervals range. This is by design for use with interpolation.
- let t = 0;
- const len = times.length;
- let current = times[t];
- const loopStop = stop;
- let sampling = false;
- let sampleStepsToTake;
- let sampleStepsTaken;
- let sampleStepSize;
- while (t < len) {
- if (!steppedOnNow && JulianDate.greaterThanOrEquals(current, updateTime)) {
- tmp = property.getValueInReferenceFrame(
- updateTime,
- referenceFrame,
- result[r]
- );
- if (defined(tmp)) {
- result[r++] = tmp;
- }
- steppedOnNow = true;
- }
- if (
- JulianDate.greaterThan(current, start) &&
- JulianDate.lessThan(current, loopStop) &&
- !current.equals(updateTime)
- ) {
- tmp = property.getValueInReferenceFrame(
- current,
- referenceFrame,
- result[r]
- );
- if (defined(tmp)) {
- result[r++] = tmp;
- }
- }
- if (t < len - 1) {
- if (maximumStep > 0 && !sampling) {
- const next = times[t + 1];
- const secondsUntilNext = JulianDate.secondsDifference(next, current);
- sampling = secondsUntilNext > maximumStep;
- if (sampling) {
- sampleStepsToTake = Math.ceil(secondsUntilNext / maximumStep);
- sampleStepsTaken = 0;
- sampleStepSize = secondsUntilNext / Math.max(sampleStepsToTake, 2);
- sampleStepsToTake = Math.max(sampleStepsToTake - 1, 1);
- }
- }
- if (sampling && sampleStepsTaken < sampleStepsToTake) {
- current = JulianDate.addSeconds(
- current,
- sampleStepSize,
- new JulianDate()
- );
- sampleStepsTaken++;
- continue;
- }
- }
- sampling = false;
- t++;
- current = times[t];
- }
- //Always step exactly on stop (but only use it if it exists.)
- tmp = property.getValueInReferenceFrame(stop, referenceFrame, result[r]);
- if (defined(tmp)) {
- result[r++] = tmp;
- }
- return r;
- }
- function subSampleGenericProperty(
- property,
- start,
- stop,
- updateTime,
- referenceFrame,
- maximumStep,
- startingIndex,
- result
- ) {
- let tmp;
- let i = 0;
- let index = startingIndex;
- let time = start;
- const stepSize = Math.max(maximumStep, 60);
- let steppedOnNow =
- !defined(updateTime) ||
- JulianDate.lessThanOrEquals(updateTime, start) ||
- JulianDate.greaterThanOrEquals(updateTime, stop);
- while (JulianDate.lessThan(time, stop)) {
- if (!steppedOnNow && JulianDate.greaterThanOrEquals(time, updateTime)) {
- steppedOnNow = true;
- tmp = property.getValueInReferenceFrame(
- updateTime,
- referenceFrame,
- result[index]
- );
- if (defined(tmp)) {
- result[index] = tmp;
- index++;
- }
- }
- tmp = property.getValueInReferenceFrame(
- time,
- referenceFrame,
- result[index]
- );
- if (defined(tmp)) {
- result[index] = tmp;
- index++;
- }
- i++;
- time = JulianDate.addSeconds(start, stepSize * i, new JulianDate());
- }
- //Always sample stop.
- tmp = property.getValueInReferenceFrame(stop, referenceFrame, result[index]);
- if (defined(tmp)) {
- result[index] = tmp;
- index++;
- }
- return index;
- }
- function subSampleIntervalProperty(
- property,
- start,
- stop,
- updateTime,
- referenceFrame,
- maximumStep,
- startingIndex,
- result
- ) {
- subSampleIntervalPropertyScratch.start = start;
- subSampleIntervalPropertyScratch.stop = stop;
- let index = startingIndex;
- const intervals = property.intervals;
- for (let i = 0; i < intervals.length; i++) {
- const interval = intervals.get(i);
- if (
- !TimeInterval.intersect(
- interval,
- subSampleIntervalPropertyScratch,
- scratchTimeInterval
- ).isEmpty
- ) {
- let time = interval.start;
- if (!interval.isStartIncluded) {
- if (interval.isStopIncluded) {
- time = interval.stop;
- } else {
- time = JulianDate.addSeconds(
- interval.start,
- JulianDate.secondsDifference(interval.stop, interval.start) / 2,
- new JulianDate()
- );
- }
- }
- const tmp = property.getValueInReferenceFrame(
- time,
- referenceFrame,
- result[index]
- );
- if (defined(tmp)) {
- result[index] = tmp;
- index++;
- }
- }
- }
- return index;
- }
- function subSampleConstantProperty(
- property,
- start,
- stop,
- updateTime,
- referenceFrame,
- maximumStep,
- startingIndex,
- result
- ) {
- const tmp = property.getValueInReferenceFrame(
- start,
- referenceFrame,
- result[startingIndex]
- );
- if (defined(tmp)) {
- result[startingIndex++] = tmp;
- }
- return startingIndex;
- }
- function subSampleCompositeProperty(
- property,
- start,
- stop,
- updateTime,
- referenceFrame,
- maximumStep,
- startingIndex,
- result
- ) {
- subSampleCompositePropertyScratch.start = start;
- subSampleCompositePropertyScratch.stop = stop;
- let index = startingIndex;
- const intervals = property.intervals;
- for (let i = 0; i < intervals.length; i++) {
- const interval = intervals.get(i);
- if (
- !TimeInterval.intersect(
- interval,
- subSampleCompositePropertyScratch,
- scratchTimeInterval
- ).isEmpty
- ) {
- const intervalStart = interval.start;
- const intervalStop = interval.stop;
- let sampleStart = start;
- if (JulianDate.greaterThan(intervalStart, sampleStart)) {
- sampleStart = intervalStart;
- }
- let sampleStop = stop;
- if (JulianDate.lessThan(intervalStop, sampleStop)) {
- sampleStop = intervalStop;
- }
- index = reallySubSample(
- interval.data,
- sampleStart,
- sampleStop,
- updateTime,
- referenceFrame,
- maximumStep,
- index,
- result
- );
- }
- }
- return index;
- }
- function reallySubSample(
- property,
- start,
- stop,
- updateTime,
- referenceFrame,
- maximumStep,
- index,
- result
- ) {
- //Unwrap any references until we have the actual property.
- while (property instanceof ReferenceProperty) {
- property = property.resolvedProperty;
- }
- if (property instanceof SampledPositionProperty) {
- const times = property._property._times;
- index = subSampleSampledProperty(
- property,
- start,
- stop,
- times,
- updateTime,
- referenceFrame,
- maximumStep,
- index,
- result
- );
- } else if (property instanceof CompositePositionProperty) {
- index = subSampleCompositeProperty(
- property,
- start,
- stop,
- updateTime,
- referenceFrame,
- maximumStep,
- index,
- result
- );
- } else if (property instanceof TimeIntervalCollectionPositionProperty) {
- index = subSampleIntervalProperty(
- property,
- start,
- stop,
- updateTime,
- referenceFrame,
- maximumStep,
- index,
- result
- );
- } else if (
- property instanceof ConstantPositionProperty ||
- (property instanceof ScaledPositionProperty &&
- Property.isConstant(property))
- ) {
- index = subSampleConstantProperty(
- property,
- start,
- stop,
- updateTime,
- referenceFrame,
- maximumStep,
- index,
- result
- );
- } else {
- //Fallback to generic sampling.
- index = subSampleGenericProperty(
- property,
- start,
- stop,
- updateTime,
- referenceFrame,
- maximumStep,
- index,
- result
- );
- }
- return index;
- }
- function subSample(
- property,
- start,
- stop,
- updateTime,
- referenceFrame,
- maximumStep,
- result
- ) {
- if (!defined(result)) {
- result = [];
- }
- const length = reallySubSample(
- property,
- start,
- stop,
- updateTime,
- referenceFrame,
- maximumStep,
- 0,
- result
- );
- result.length = length;
- return result;
- }
- const toFixedScratch = new Matrix3();
- function PolylineUpdater(scene, referenceFrame) {
- this._unusedIndexes = [];
- this._polylineCollection = new PolylineCollection();
- this._scene = scene;
- this._referenceFrame = referenceFrame;
- scene.primitives.add(this._polylineCollection);
- }
- PolylineUpdater.prototype.update = function (time) {
- if (this._referenceFrame === ReferenceFrame.INERTIAL) {
- let toFixed = Transforms.computeIcrfToFixedMatrix(time, toFixedScratch);
- if (!defined(toFixed)) {
- toFixed = Transforms.computeTemeToPseudoFixedMatrix(time, toFixedScratch);
- }
- Matrix4.fromRotationTranslation(
- toFixed,
- Cartesian3.ZERO,
- this._polylineCollection.modelMatrix
- );
- }
- };
- PolylineUpdater.prototype.updateObject = function (time, item) {
- const entity = item.entity;
- const pathGraphics = entity._path;
- const positionProperty = entity._position;
- let sampleStart;
- let sampleStop;
- const showProperty = pathGraphics._show;
- let polyline = item.polyline;
- let show =
- entity.isShowing &&
- entity.isAvailable(time) &&
- (!defined(showProperty) || showProperty.getValue(time));
- //While we want to show the path, there may not actually be anything to show
- //depending on lead/trail settings. Compute the interval of the path to
- //show and check against actual availability.
- if (show) {
- const leadTime = Property.getValueOrUndefined(pathGraphics._leadTime, time);
- const trailTime = Property.getValueOrUndefined(
- pathGraphics._trailTime,
- time
- );
- const availability = entity._availability;
- const hasAvailability = defined(availability);
- const hasLeadTime = defined(leadTime);
- const hasTrailTime = defined(trailTime);
- //Objects need to have either defined availability or both a lead and trail time in order to
- //draw a path (since we can't draw "infinite" paths.
- show = hasAvailability || (hasLeadTime && hasTrailTime);
- //The final step is to compute the actual start/stop times of the path to show.
- //If current time is outside of the availability interval, there's a chance that
- //we won't have to draw anything anyway.
- if (show) {
- if (hasTrailTime) {
- sampleStart = JulianDate.addSeconds(time, -trailTime, new JulianDate());
- }
- if (hasLeadTime) {
- sampleStop = JulianDate.addSeconds(time, leadTime, new JulianDate());
- }
- if (hasAvailability) {
- const start = availability.start;
- const stop = availability.stop;
- if (!hasTrailTime || JulianDate.greaterThan(start, sampleStart)) {
- sampleStart = start;
- }
- if (!hasLeadTime || JulianDate.lessThan(stop, sampleStop)) {
- sampleStop = stop;
- }
- }
- show = JulianDate.lessThan(sampleStart, sampleStop);
- }
- }
- if (!show) {
- //don't bother creating or updating anything else
- if (defined(polyline)) {
- this._unusedIndexes.push(item.index);
- item.polyline = undefined;
- polyline.show = false;
- item.index = undefined;
- }
- return;
- }
- if (!defined(polyline)) {
- const unusedIndexes = this._unusedIndexes;
- const length = unusedIndexes.length;
- if (length > 0) {
- const index = unusedIndexes.pop();
- polyline = this._polylineCollection.get(index);
- item.index = index;
- } else {
- item.index = this._polylineCollection.length;
- polyline = this._polylineCollection.add();
- }
- polyline.id = entity;
- item.polyline = polyline;
- }
- const resolution = Property.getValueOrDefault(
- pathGraphics._resolution,
- time,
- defaultResolution
- );
- polyline.show = true;
- polyline.positions = subSample(
- positionProperty,
- sampleStart,
- sampleStop,
- time,
- this._referenceFrame,
- resolution,
- polyline.positions.slice()
- );
- polyline.material = MaterialProperty.getValue(
- time,
- pathGraphics._material,
- polyline.material
- );
- polyline.width = Property.getValueOrDefault(
- pathGraphics._width,
- time,
- defaultWidth
- );
- polyline.distanceDisplayCondition = Property.getValueOrUndefined(
- pathGraphics._distanceDisplayCondition,
- time,
- polyline.distanceDisplayCondition
- );
- };
- PolylineUpdater.prototype.removeObject = function (item) {
- const polyline = item.polyline;
- if (defined(polyline)) {
- this._unusedIndexes.push(item.index);
- item.polyline = undefined;
- polyline.show = false;
- polyline.id = undefined;
- item.index = undefined;
- }
- };
- PolylineUpdater.prototype.destroy = function () {
- this._scene.primitives.remove(this._polylineCollection);
- return destroyObject(this);
- };
- /**
- * A {@link Visualizer} which maps {@link Entity#path} to a {@link Polyline}.
- * @alias PathVisualizer
- * @constructor
- *
- * @param {Scene} scene The scene the primitives will be rendered in.
- * @param {EntityCollection} entityCollection The entityCollection to visualize.
- */
- function PathVisualizer(scene, entityCollection) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(scene)) {
- throw new DeveloperError("scene is required.");
- }
- if (!defined(entityCollection)) {
- throw new DeveloperError("entityCollection is required.");
- }
- //>>includeEnd('debug');
- entityCollection.collectionChanged.addEventListener(
- PathVisualizer.prototype._onCollectionChanged,
- this
- );
- this._scene = scene;
- this._updaters = {};
- this._entityCollection = entityCollection;
- this._items = new AssociativeArray();
- this._onCollectionChanged(entityCollection, entityCollection.values, [], []);
- }
- /**
- * Updates all of the primitives created by this visualizer to match their
- * Entity counterpart at the given time.
- *
- * @param {JulianDate} time The time to update to.
- * @returns {boolean} This function always returns true.
- */
- PathVisualizer.prototype.update = function (time) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(time)) {
- throw new DeveloperError("time is required.");
- }
- //>>includeEnd('debug');
- const updaters = this._updaters;
- for (const key in updaters) {
- if (updaters.hasOwnProperty(key)) {
- updaters[key].update(time);
- }
- }
- const items = this._items.values;
- if (
- items.length === 0 &&
- defined(this._updaters) &&
- Object.keys(this._updaters).length > 0
- ) {
- for (const u in updaters) {
- if (updaters.hasOwnProperty(u)) {
- updaters[u].destroy();
- }
- }
- this._updaters = {};
- }
- for (let i = 0, len = items.length; i < len; i++) {
- const item = items[i];
- const entity = item.entity;
- const positionProperty = entity._position;
- const lastUpdater = item.updater;
- let frameToVisualize = ReferenceFrame.FIXED;
- if (this._scene.mode === SceneMode.SCENE3D) {
- frameToVisualize = positionProperty.referenceFrame;
- }
- let currentUpdater = this._updaters[frameToVisualize];
- if (lastUpdater === currentUpdater && defined(currentUpdater)) {
- currentUpdater.updateObject(time, item);
- continue;
- }
- if (defined(lastUpdater)) {
- lastUpdater.removeObject(item);
- }
- if (!defined(currentUpdater)) {
- currentUpdater = new PolylineUpdater(this._scene, frameToVisualize);
- currentUpdater.update(time);
- this._updaters[frameToVisualize] = currentUpdater;
- }
- item.updater = currentUpdater;
- if (defined(currentUpdater)) {
- currentUpdater.updateObject(time, item);
- }
- }
- return true;
- };
- /**
- * Returns true if this object was destroyed; otherwise, false.
- *
- * @returns {boolean} True if this object was destroyed; otherwise, false.
- */
- PathVisualizer.prototype.isDestroyed = function () {
- return false;
- };
- /**
- * Removes and destroys all primitives created by this instance.
- */
- PathVisualizer.prototype.destroy = function () {
- this._entityCollection.collectionChanged.removeEventListener(
- PathVisualizer.prototype._onCollectionChanged,
- this
- );
- const updaters = this._updaters;
- for (const key in updaters) {
- if (updaters.hasOwnProperty(key)) {
- updaters[key].destroy();
- }
- }
- return destroyObject(this);
- };
- PathVisualizer.prototype._onCollectionChanged = function (
- entityCollection,
- added,
- removed,
- changed
- ) {
- let i;
- let entity;
- let item;
- const items = this._items;
- for (i = added.length - 1; i > -1; i--) {
- entity = added[i];
- if (defined(entity._path) && defined(entity._position)) {
- items.set(entity.id, new EntityData(entity));
- }
- }
- for (i = changed.length - 1; i > -1; i--) {
- entity = changed[i];
- if (defined(entity._path) && defined(entity._position)) {
- if (!items.contains(entity.id)) {
- items.set(entity.id, new EntityData(entity));
- }
- } else {
- item = items.get(entity.id);
- if (defined(item)) {
- if (defined(item.updater)) {
- item.updater.removeObject(item);
- }
- items.remove(entity.id);
- }
- }
- }
- for (i = removed.length - 1; i > -1; i--) {
- entity = removed[i];
- item = items.get(entity.id);
- if (defined(item)) {
- if (defined(item.updater)) {
- item.updater.removeObject(item);
- }
- items.remove(entity.id);
- }
- }
- };
- //for testing
- PathVisualizer._subSample = subSample;
- export default PathVisualizer;
|