import AssociativeArray from '../Core/AssociativeArray.js'; import BoundingSphere from '../Core/BoundingSphere.js'; import Check from '../Core/Check.js'; import defaultValue from '../Core/defaultValue.js'; import defined from '../Core/defined.js'; import destroyObject from '../Core/destroyObject.js'; import ClassificationType from '../Scene/ClassificationType.js'; import PolylineColorAppearance from '../Scene/PolylineColorAppearance.js'; import PolylineMaterialAppearance from '../Scene/PolylineMaterialAppearance.js'; import ShadowMode from '../Scene/ShadowMode.js'; import BoundingSphereState from './BoundingSphereState.js'; import ColorMaterialProperty from './ColorMaterialProperty.js'; import DynamicGeometryBatch from './DynamicGeometryBatch.js'; import PolylineGeometryUpdater from './PolylineGeometryUpdater.js'; import StaticGeometryColorBatch from './StaticGeometryColorBatch.js'; import StaticGeometryPerMaterialBatch from './StaticGeometryPerMaterialBatch.js'; import StaticGroundPolylinePerMaterialBatch from './StaticGroundPolylinePerMaterialBatch.js'; var emptyArray = []; function removeUpdater(that, updater) { //We don't keep track of which batch an updater is in, so just remove it from all of them. var batches = that._batches; var length = batches.length; for (var i = 0; i < length; i++) { batches[i].remove(updater); } } function insertUpdaterIntoBatch(that, time, updater) { if (updater.isDynamic) { that._dynamicBatch.add(time, updater); return; } if (updater.clampToGround && updater.fillEnabled) { // Also checks for support var classificationType = updater.classificationTypeProperty.getValue(time); that._groundBatches[classificationType].add(time, updater); return; } var shadows; if (updater.fillEnabled) { shadows = updater.shadowsProperty.getValue(time); } var multiplier = 0; if (defined(updater.depthFailMaterialProperty)) { multiplier = updater.depthFailMaterialProperty instanceof ColorMaterialProperty ? 1 : 2; } var index; if (defined(shadows)) { index = shadows + multiplier * ShadowMode.NUMBER_OF_SHADOW_MODES; } if (updater.fillEnabled) { if (updater.fillMaterialProperty instanceof ColorMaterialProperty) { that._colorBatches[index].add(time, updater); } else { that._materialBatches[index].add(time, updater); } } } /** * A visualizer for polylines represented by {@link Primitive} instances. * @alias PolylineVisualizer * @constructor * * @param {Scene} scene The scene the primitives will be rendered in. * @param {EntityCollection} entityCollection The entityCollection to visualize. * @param {PrimitiveCollection} [primitives=scene.primitives] A collection to add primitives related to the entities * @param {PrimitiveCollection} [groundPrimitives=scene.groundPrimitives] A collection to add ground primitives related to the entities */ function PolylineVisualizer(scene, entityCollection, primitives, groundPrimitives) { //>>includeStart('debug', pragmas.debug); Check.defined('scene', scene); Check.defined('entityCollection', entityCollection); //>>includeEnd('debug'); groundPrimitives = defaultValue(groundPrimitives, scene.groundPrimitives); primitives = defaultValue(primitives, scene.primitives); this._scene = scene; this._primitives = primitives; this._entityCollection = undefined; this._addedObjects = new AssociativeArray(); this._removedObjects = new AssociativeArray(); this._changedObjects = new AssociativeArray(); var i; var numberOfShadowModes = ShadowMode.NUMBER_OF_SHADOW_MODES; this._colorBatches = new Array(numberOfShadowModes * 3); this._materialBatches = new Array(numberOfShadowModes * 3); for (i = 0; i < numberOfShadowModes; ++i) { this._colorBatches[i] = new StaticGeometryColorBatch(primitives, PolylineColorAppearance, undefined, false, i); // no depth fail appearance this._materialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, PolylineMaterialAppearance, undefined, false, i); this._colorBatches[i + numberOfShadowModes] = new StaticGeometryColorBatch(primitives, PolylineColorAppearance, PolylineColorAppearance, false, i); //depth fail appearance variations this._materialBatches[i + numberOfShadowModes] = new StaticGeometryPerMaterialBatch(primitives, PolylineMaterialAppearance, PolylineColorAppearance, false, i); this._colorBatches[i + numberOfShadowModes * 2] = new StaticGeometryColorBatch(primitives, PolylineColorAppearance, PolylineMaterialAppearance, false, i); this._materialBatches[i + numberOfShadowModes * 2] = new StaticGeometryPerMaterialBatch(primitives, PolylineMaterialAppearance, PolylineMaterialAppearance, false, i); } this._dynamicBatch = new DynamicGeometryBatch(primitives, groundPrimitives); var numberOfClassificationTypes = ClassificationType.NUMBER_OF_CLASSIFICATION_TYPES; this._groundBatches = new Array(numberOfClassificationTypes); for (i = 0; i < numberOfClassificationTypes; ++i) { this._groundBatches[i] = new StaticGroundPolylinePerMaterialBatch(groundPrimitives, i); } this._batches = this._colorBatches.concat(this._materialBatches, this._dynamicBatch, this._groundBatches); this._subscriptions = new AssociativeArray(); this._updaters = new AssociativeArray(); this._entityCollection = entityCollection; entityCollection.collectionChanged.addEventListener(PolylineVisualizer.prototype._onCollectionChanged, this); this._onCollectionChanged(entityCollection, entityCollection.values, emptyArray); } /** * 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} True if the visualizer successfully updated to the provided time, * false if the visualizer is waiting for asynchronous primitives to be created. */ PolylineVisualizer.prototype.update = function(time) { //>>includeStart('debug', pragmas.debug); Check.defined('time', time); //>>includeEnd('debug'); var addedObjects = this._addedObjects; var added = addedObjects.values; var removedObjects = this._removedObjects; var removed = removedObjects.values; var changedObjects = this._changedObjects; var changed = changedObjects.values; var i; var entity; var id; var updater; for (i = changed.length - 1; i > -1; i--) { entity = changed[i]; id = entity.id; updater = this._updaters.get(id); //If in a single update, an entity gets removed and a new instance //re-added with the same id, the updater no longer tracks the //correct entity, we need to both remove the old one and //add the new one, which is done by pushing the entity //onto the removed/added lists. if (updater.entity === entity) { removeUpdater(this, updater); insertUpdaterIntoBatch(this, time, updater); } else { removed.push(entity); added.push(entity); } } for (i = removed.length - 1; i > -1; i--) { entity = removed[i]; id = entity.id; updater = this._updaters.get(id); removeUpdater(this, updater); updater.destroy(); this._updaters.remove(id); this._subscriptions.get(id)(); this._subscriptions.remove(id); } for (i = added.length - 1; i > -1; i--) { entity = added[i]; id = entity.id; updater = new PolylineGeometryUpdater(entity, this._scene); this._updaters.set(id, updater); insertUpdaterIntoBatch(this, time, updater); this._subscriptions.set(id, updater.geometryChanged.addEventListener(PolylineVisualizer._onGeometryChanged, this)); } addedObjects.removeAll(); removedObjects.removeAll(); changedObjects.removeAll(); var isUpdated = true; var batches = this._batches; var length = batches.length; for (i = 0; i < length; i++) { isUpdated = batches[i].update(time) && isUpdated; } return isUpdated; }; var getBoundingSphereArrayScratch = []; var getBoundingSphereBoundingSphereScratch = new BoundingSphere(); /** * Computes a bounding sphere which encloses the visualization produced for the specified entity. * The bounding sphere is in the fixed frame of the scene's globe. * * @param {Entity} entity The entity whose bounding sphere to compute. * @param {BoundingSphere} result The bounding sphere onto which to store the result. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere, * BoundingSphereState.PENDING if the result is still being computed, or * BoundingSphereState.FAILED if the entity has no visualization in the current scene. * @private */ PolylineVisualizer.prototype.getBoundingSphere = function(entity, result) { //>>includeStart('debug', pragmas.debug); Check.defined('entity', entity); Check.defined('result', result); //>>includeEnd('debug'); var boundingSpheres = getBoundingSphereArrayScratch; var tmp = getBoundingSphereBoundingSphereScratch; var count = 0; var state = BoundingSphereState.DONE; var batches = this._batches; var batchesLength = batches.length; var updater = this._updaters.get(entity.id); for (var i = 0; i < batchesLength; i++) { state = batches[i].getBoundingSphere(updater, tmp); if (state === BoundingSphereState.PENDING) { return BoundingSphereState.PENDING; } else if (state === BoundingSphereState.DONE) { boundingSpheres[count] = BoundingSphere.clone(tmp, boundingSpheres[count]); count++; } } if (count === 0) { return BoundingSphereState.FAILED; } boundingSpheres.length = count; BoundingSphere.fromBoundingSpheres(boundingSpheres, result); return BoundingSphereState.DONE; }; /** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ PolylineVisualizer.prototype.isDestroyed = function() { return false; }; /** * Removes and destroys all primitives created by this instance. */ PolylineVisualizer.prototype.destroy = function() { this._entityCollection.collectionChanged.removeEventListener(PolylineVisualizer.prototype._onCollectionChanged, this); this._addedObjects.removeAll(); this._removedObjects.removeAll(); var i; var batches = this._batches; var length = batches.length; for (i = 0; i < length; i++) { batches[i].removeAllPrimitives(); } var subscriptions = this._subscriptions.values; length = subscriptions.length; for (i = 0; i < length; i++) { subscriptions[i](); } this._subscriptions.removeAll(); return destroyObject(this); }; /** * @private */ PolylineVisualizer._onGeometryChanged = function(updater) { var removedObjects = this._removedObjects; var changedObjects = this._changedObjects; var entity = updater.entity; var id = entity.id; if (!defined(removedObjects.get(id)) && !defined(changedObjects.get(id))) { changedObjects.set(id, entity); } }; /** * @private */ PolylineVisualizer.prototype._onCollectionChanged = function(entityCollection, added, removed) { var addedObjects = this._addedObjects; var removedObjects = this._removedObjects; var changedObjects = this._changedObjects; var i; var id; var entity; for (i = removed.length - 1; i > -1; i--) { entity = removed[i]; id = entity.id; if (!addedObjects.remove(id)) { removedObjects.set(id, entity); changedObjects.remove(id); } } for (i = added.length - 1; i > -1; i--) { entity = added[i]; id = entity.id; if (removedObjects.remove(id)) { changedObjects.set(id, entity); } else { addedObjects.set(id, entity); } } }; export default PolylineVisualizer;