/*global define*/ define(['Core/Cartesian3', 'Core/Color', 'Core/defined', 'Core/destroyObject', 'Core/DeveloperError', 'Core/Quaternion', 'Core/Math', 'Core/Matrix3', 'Core/Matrix4', 'Core/Spherical', 'Scene/CustomSensorVolume', 'Scene/Material', 'DynamicScene/MaterialProperty'], function( Cartesian3, Color, defined, destroyObject, DeveloperError, Quaternion, CesiumMath, Matrix3, Matrix4, Spherical, CustomSensorVolume, Material, MaterialProperty) { "use strict"; //CZML_TODO DynamicConeVisualizerUsingCustomSensor is a temporary workaround //because ComplexConicSensor has major performance issues. As soon as //ComplexConicSensor is working, this class can be deleted and //DynamicConeVisualizer is a drop in replacement that already does things //"the right way". var matrix3Scratch = new Matrix3(); function assignSpherical(index, array, clock, cone) { var spherical = array[index]; if (!defined(spherical)) { array[index] = spherical = new Spherical(); } spherical.clock = clock; spherical.cone = cone; spherical.magnitude = 1.0; } function computeDirections(minimumClockAngle, maximumClockAngle, innerHalfAngle, outerHalfAngle, result) { var angle; var i = 0; var angleStep = CesiumMath.toRadians(2.0); if (minimumClockAngle === 0.0 && maximumClockAngle === CesiumMath.TWO_PI) { // No clock angle limits, so this is just a circle. // There might be a hole but we're ignoring it for now. for (angle = 0.0; angle < CesiumMath.TWO_PI; angle += angleStep) { assignSpherical(i++, result, angle, outerHalfAngle); } } else { // There are clock angle limits. for (angle = minimumClockAngle; angle < maximumClockAngle; angle += angleStep) { assignSpherical(i++, result, angle, outerHalfAngle); } assignSpherical(i++, result, maximumClockAngle, outerHalfAngle); if (innerHalfAngle) { for (angle = maximumClockAngle; angle > minimumClockAngle; angle -= angleStep) { assignSpherical(i++, result, angle, innerHalfAngle); } assignSpherical(i++, result, minimumClockAngle, innerHalfAngle); } else { assignSpherical(i++, result, maximumClockAngle, 0.0); } } result.length = i; return result; } /** * A DynamicObject visualizer which maps the DynamicCone instance * in DynamicObject.cone to a CustomSensor primitive. * @alias DynamicConeVisualizerUsingCustomSensor * @constructor * * @param {Scene} scene The scene the primitives will be rendered in. * @param {DynamicObjectCollection} [dynamicObjectCollection] The dynamicObjectCollection to visualize. * * @exception {DeveloperError} scene is required. * * @see DynamicCone * @see Scene * @see DynamicObject * @see DynamicObjectCollection * @see CompositeDynamicObjectCollection * @see VisualizerCollection * @see DynamicBillboardVisualizer * @see DynamicConeVisualizer * @see DynamicLabelVisualizer * @see DynamicPointVisualizer * @see DynamicPolygonVisualizer * @see DynamicPolylineVisualizer * @see DynamicPyramidVisualizer * */ var DynamicConeVisualizerUsingCustomSensor = function(scene, dynamicObjectCollection) { //>>includeStart('debug', pragmas.debug); if (!defined(scene)) { throw new DeveloperError('scene is required.'); } //>>includeEnd('debug'); this._scene = scene; this._unusedIndexes = []; this._primitives = scene.getPrimitives(); this._coneCollection = []; this._dynamicObjectCollection = undefined; this.setDynamicObjectCollection(dynamicObjectCollection); }; /** * Returns the scene being used by this visualizer. * * @returns {Scene} The scene being used by this visualizer. */ DynamicConeVisualizerUsingCustomSensor.prototype.getScene = function() { return this._scene; }; /** * Gets the DynamicObjectCollection being visualized. * * @returns {DynamicObjectCollection} The DynamicObjectCollection being visualized. */ DynamicConeVisualizerUsingCustomSensor.prototype.getDynamicObjectCollection = function() { return this._dynamicObjectCollection; }; /** * Sets the DynamicObjectCollection to visualize. * * @param dynamicObjectCollection The DynamicObjectCollection to visualizer. */ DynamicConeVisualizerUsingCustomSensor.prototype.setDynamicObjectCollection = function(dynamicObjectCollection) { var oldCollection = this._dynamicObjectCollection; if (oldCollection !== dynamicObjectCollection) { if (defined(oldCollection)) { oldCollection.collectionChanged.removeEventListener(DynamicConeVisualizerUsingCustomSensor.prototype._onObjectsRemoved, this); this.removeAllPrimitives(); } this._dynamicObjectCollection = dynamicObjectCollection; if (defined(dynamicObjectCollection)) { dynamicObjectCollection.collectionChanged.addEventListener(DynamicConeVisualizerUsingCustomSensor.prototype._onObjectsRemoved, this); } } }; /** * Updates all of the primitives created by this visualizer to match their * DynamicObject counterpart at the given time. * * @param {JulianDate} time The time to update to. * * @exception {DeveloperError} time is required. */ DynamicConeVisualizerUsingCustomSensor.prototype.update = function(time) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError('time is requied.'); } //>>includeEnd('debug'); if (defined(this._dynamicObjectCollection)) { var dynamicObjects = this._dynamicObjectCollection.getObjects(); for ( var i = 0, len = dynamicObjects.length; i < len; i++) { updateObject(this, time, dynamicObjects[i]); } } }; /** * Removes all primitives from the scene. */ DynamicConeVisualizerUsingCustomSensor.prototype.removeAllPrimitives = function() { var i, len; for (i = 0, len = this._coneCollection.length; i < len; i++) { this._primitives.remove(this._coneCollection[i]); } if (defined(this._dynamicObjectCollection)) { var dynamicObjects = this._dynamicObjectCollection.getObjects(); for (i = dynamicObjects.length - 1; i > -1; i--) { dynamicObjects[i]._coneVisualizerIndex = undefined; } } this._unusedIndexes = []; this._coneCollection = []; }; /** * Returns true if this object was destroyed; otherwise, false. * <br /><br /> * If this object was destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. * * @memberof DynamicConeVisualizerUsingCustomSensor * * @returns {Boolean} True if this object was destroyed; otherwise, false. * * @see DynamicConeVisualizerUsingCustomSensor#destroy */ DynamicConeVisualizerUsingCustomSensor.prototype.isDestroyed = function() { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. * <br /><br /> * Once an object is destroyed, it should not be used; calling any function other than * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore, * assign the return value (<code>undefined</code>) to the object as done in the example. * * @memberof DynamicConeVisualizerUsingCustomSensor * * @returns {undefined} * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @see DynamicConeVisualizerUsingCustomSensor#isDestroyed * * @example * visualizer = visualizer && visualizer.destroy(); */ DynamicConeVisualizerUsingCustomSensor.prototype.destroy = function() { this.setDynamicObjectCollection(undefined); return destroyObject(this); }; var cachedPosition = new Cartesian3(); var cachedOrientation = new Quaternion(); function updateObject(dynamicConeVisualizerUsingCustomSensor, time, dynamicObject) { var dynamicCone = dynamicObject._cone; if (!defined(dynamicCone)) { return; } var positionProperty = dynamicObject._position; if (!defined(positionProperty)) { return; } var orientationProperty = dynamicObject._orientation; if (!defined(orientationProperty)) { return; } var cone; var showProperty = dynamicCone._show; var coneVisualizerIndex = dynamicObject._coneVisualizerIndex; var show = dynamicObject.isAvailable(time) && (!defined(showProperty) || showProperty.getValue(time)); if (!show) { //don't bother creating or updating anything else if (defined(coneVisualizerIndex)) { cone = dynamicConeVisualizerUsingCustomSensor._coneCollection[coneVisualizerIndex]; cone.show = false; dynamicObject._coneVisualizerIndex = undefined; dynamicConeVisualizerUsingCustomSensor._unusedIndexes.push(coneVisualizerIndex); } return; } if (!defined(coneVisualizerIndex)) { var unusedIndexes = dynamicConeVisualizerUsingCustomSensor._unusedIndexes; var length = unusedIndexes.length; if (length > 0) { coneVisualizerIndex = unusedIndexes.pop(); cone = dynamicConeVisualizerUsingCustomSensor._coneCollection[coneVisualizerIndex]; } else { coneVisualizerIndex = dynamicConeVisualizerUsingCustomSensor._coneCollection.length; cone = new CustomSensorVolume(); cone._directionsScratch = []; dynamicConeVisualizerUsingCustomSensor._coneCollection.push(cone); dynamicConeVisualizerUsingCustomSensor._primitives.add(cone); } dynamicObject._coneVisualizerIndex = coneVisualizerIndex; cone.dynamicObject = dynamicObject; // CZML_TODO Determine official defaults cone.material = Material.fromType(Material.ColorType); cone.intersectionColor = Color.clone(Color.YELLOW); cone.intersectionWidth = 5.0; cone.radius = Number.POSITIVE_INFINITY; cone.showIntersection = true; } else { cone = dynamicConeVisualizerUsingCustomSensor._coneCollection[coneVisualizerIndex]; } cone.show = true; var minimumClockAngle; var property = dynamicCone._minimumClockAngle; if (defined(property)) { minimumClockAngle = property.getValue(time); } if (!defined(minimumClockAngle)) { minimumClockAngle = 0; } var maximumClockAngle; property = dynamicCone._maximumClockAngle; if (defined(property)) { maximumClockAngle = property.getValue(time); } if (!defined(maximumClockAngle)) { maximumClockAngle = CesiumMath.TWO_PI; } var innerHalfAngle; property = dynamicCone._innerHalfAngle; if (defined(property)) { innerHalfAngle = property.getValue(time); } if (!defined(innerHalfAngle)) { innerHalfAngle = 0; } var outerHalfAngle; property = dynamicCone._outerHalfAngle; if (defined(property)) { outerHalfAngle = property.getValue(time); } if (!defined(outerHalfAngle)) { outerHalfAngle = Math.PI; } if (minimumClockAngle !== cone.minimumClockAngle || maximumClockAngle !== cone.maximumClockAngle || innerHalfAngle !== cone.innerHalfAngle || outerHalfAngle !== cone.outerHalfAngle) { cone.setDirections(computeDirections(minimumClockAngle, maximumClockAngle, innerHalfAngle, outerHalfAngle, cone._directionsScratch)); cone.innerHalfAngle = innerHalfAngle; cone.maximumClockAngle = maximumClockAngle; cone.outerHalfAngle = outerHalfAngle; cone.minimumClockAngle = minimumClockAngle; } property = dynamicCone._radius; if (defined(property)) { var radius = property.getValue(time); if (defined(radius)) { cone.radius = radius; } } var position = positionProperty.getValue(time, cachedPosition); var orientation = orientationProperty.getValue(time, cachedOrientation); if (defined(position) && defined(orientation) && (!Cartesian3.equals(position, cone._visualizerPosition) || !Quaternion.equals(orientation, cone._visualizerOrientation))) { Matrix4.fromRotationTranslation(Matrix3.fromQuaternion(orientation, matrix3Scratch), position, cone.modelMatrix); cone._visualizerPosition = Cartesian3.clone(position, cone._visualizerPosition); cone._visualizerOrientation = Quaternion.clone(orientation, cone._visualizerOrientation); } cone.material = MaterialProperty.getValue(time, dynamicCone._outerMaterial, cone.material); property = dynamicCone._intersectionColor; if (defined(property)) { property.getValue(time, cone.intersectionColor); } property = dynamicCone._intersectionWidth; if (defined(property)) { var intersectionWidth = property.getValue(time); if (defined(intersectionWidth)) { cone.intersectionWidth = intersectionWidth; } } } DynamicConeVisualizerUsingCustomSensor.prototype._onObjectsRemoved = function(dynamicObjectCollection, added, dynamicObjects) { var thisConeCollection = this._coneCollection; var thisUnusedIndexes = this._unusedIndexes; for ( var i = dynamicObjects.length - 1; i > -1; i--) { var dynamicObject = dynamicObjects[i]; var coneVisualizerIndex = dynamicObject._coneVisualizerIndex; if (defined(coneVisualizerIndex)) { var cone = thisConeCollection[coneVisualizerIndex]; cone.show = false; thisUnusedIndexes.push(coneVisualizerIndex); dynamicObject._coneVisualizerIndex = undefined; } } }; return DynamicConeVisualizerUsingCustomSensor; });