diff --git a/Apps/Sandcastle/gallery/Geometry Height Reference.html b/Apps/Sandcastle/gallery/Geometry Height Reference.html new file mode 100644 index 000000000000..6a31256c48bf --- /dev/null +++ b/Apps/Sandcastle/gallery/Geometry Height Reference.html @@ -0,0 +1,86 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/Geometry Height Reference.jpg b/Apps/Sandcastle/gallery/Geometry Height Reference.jpg new file mode 100644 index 000000000000..054d09cbc2e4 Binary files /dev/null and b/Apps/Sandcastle/gallery/Geometry Height Reference.jpg differ diff --git a/Source/DataSources/CorridorGeometryUpdater.js b/Source/DataSources/CorridorGeometryUpdater.js index ab1eef75de9b..4fbac9960203 100644 --- a/Source/DataSources/CorridorGeometryUpdater.js +++ b/Source/DataSources/CorridorGeometryUpdater.js @@ -1,4 +1,5 @@ define([ + '../Core/Cartesian3', '../Core/Check', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', @@ -8,17 +9,22 @@ define([ '../Core/DeveloperError', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', '../Core/GeometryInstance', + '../Core/GeometryOffsetAttribute', '../Core/Iso8601', + '../Core/OffsetGeometryInstanceAttribute', '../Core/ShowGeometryInstanceAttribute', '../Scene/GroundPrimitive', + '../Scene/HeightReference', '../Scene/MaterialAppearance', '../Scene/PerInstanceColorAppearance', './ColorMaterialProperty', './DynamicGeometryUpdater', + './GeometryHeightProperty', './GeometryUpdater', './GroundGeometryUpdater', './Property' ], function( + Cartesian3, Check, Color, ColorGeometryInstanceAttribute, @@ -28,19 +34,26 @@ define([ DeveloperError, DistanceDisplayConditionGeometryInstanceAttribute, GeometryInstance, + GeometryOffsetAttribute, Iso8601, + OffsetGeometryInstanceAttribute, ShowGeometryInstanceAttribute, GroundPrimitive, + HeightReference, MaterialAppearance, PerInstanceColorAppearance, ColorMaterialProperty, DynamicGeometryUpdater, + GeometryHeightProperty, GeometryUpdater, GroundGeometryUpdater, Property) { 'use strict'; var scratchColor = new Color(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); + var scratchCorridorGeometry = new CorridorGeometry({positions: [], width: 1}); function CorridorGeometryOptions(entity) { this.id = entity; @@ -51,6 +64,7 @@ define([ this.height = undefined; this.extrudedHeight = undefined; this.granularity = undefined; + this.offsetAttribute = undefined; } /** @@ -99,11 +113,11 @@ define([ var entity = this._entity; var isAvailable = entity.isAvailable(time); - var attributes; + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)) + }; - var color; - var show = new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)); - var distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)); if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if (defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable)) { @@ -112,17 +126,11 @@ define([ if (!defined(currentColor)) { currentColor = Color.WHITE; } - color = ColorGeometryInstanceAttribute.fromColor(currentColor); - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayCondition, - color : color - }; - } else { - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayCondition - }; + attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); + } + + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); } return new GeometryInstance({ @@ -153,17 +161,31 @@ define([ var isAvailable = entity.isAvailable(time); var outlineColor = Property.getValueOrDefault(this._outlineColorProperty, time, Color.BLACK, scratchColor); + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), + color : ColorGeometryInstanceAttribute.fromColor(outlineColor), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)) + }; + + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); + } + return new GeometryInstance({ id : entity, geometry : new CorridorOutlineGeometry(this._options), - attributes : { - show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), - color : ColorGeometryInstanceAttribute.fromColor(outlineColor), - distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)) - } + attributes : attributes }); }; + CorridorGeometryUpdater.prototype._computeCenter = function(time, result) { + var positions = Property.getValueOrUndefined(this._entity.corridor.positions, time); + if (!defined(positions) || positions.length === 0) { + return; + } + return Cartesian3.clone(positions[Math.floor(positions.length / 2.0)], result); + }; + CorridorGeometryUpdater.prototype._isHidden = function(entity, corridor) { return !defined(corridor.positions) || GeometryUpdater.prototype._isHidden.call(this, entity, corridor); }; @@ -207,6 +229,12 @@ define([ options.granularity = defined(granularity) ? granularity.getValue(Iso8601.MINIMUM_VALUE) : undefined; options.width = defined(width) ? width.getValue(Iso8601.MINIMUM_VALUE) : undefined; options.cornerType = defined(cornerType) ? cornerType.getValue(Iso8601.MINIMUM_VALUE) : undefined; + options.offsetAttribute = GeometryHeightProperty.computeGeometryOffsetAttribute(height, extrudedHeight, Iso8601.MINIMUM_VALUE); + + if (extrudedHeight instanceof GeometryHeightProperty && Property.getValueOrDefault(extrudedHeight.height, Iso8601.MINIMUM_VALUE, HeightReference.NONE) === HeightReference.CLAMP_TO_GROUND) { + scratchCorridorGeometry.setOptions(options); + options.extrudedHeight = GeometryHeightProperty.getMinimumTerrainValue(scratchCorridorGeometry.rectangle); + } }; CorridorGeometryUpdater.DynamicGeometryUpdater = DynamicCorridorGeometryUpdater; @@ -230,12 +258,20 @@ define([ DynamicCorridorGeometryUpdater.prototype._setOptions = function(entity, corridor, time) { var options = this._options; + var height = corridor.height; + var extrudedHeight = corridor.extrudedHeight; options.positions = Property.getValueOrUndefined(corridor.positions, time); options.width = Property.getValueOrUndefined(corridor.width, time); - options.height = Property.getValueOrUndefined(corridor.height, time); - options.extrudedHeight = Property.getValueOrUndefined(corridor.extrudedHeight, time); + options.height = Property.getValueOrUndefined(height, time); + options.extrudedHeight = Property.getValueOrUndefined(extrudedHeight, time); options.granularity = Property.getValueOrUndefined(corridor.granularity, time); options.cornerType = Property.getValueOrUndefined(corridor.cornerType, time); + options.offsetAttribute = GeometryHeightProperty.computeGeometryOffsetAttribute(height, extrudedHeight, time); + + if (extrudedHeight instanceof GeometryHeightProperty && Property.getValueOrDefault(extrudedHeight.height, time, HeightReference.NONE) === HeightReference.CLAMP_TO_GROUND) { + scratchCorridorGeometry.setOptions(options); + options.extrudedHeight = GeometryHeightProperty.getMinimumTerrainValue(scratchCorridorGeometry.rectangle); + } }; return CorridorGeometryUpdater; diff --git a/Source/DataSources/EllipseGeometryUpdater.js b/Source/DataSources/EllipseGeometryUpdater.js index b577e28981ba..b76ca9f7195b 100644 --- a/Source/DataSources/EllipseGeometryUpdater.js +++ b/Source/DataSources/EllipseGeometryUpdater.js @@ -1,4 +1,5 @@ define([ + '../Core/Cartesian3', '../Core/Check', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', @@ -8,17 +9,22 @@ define([ '../Core/EllipseGeometry', '../Core/EllipseOutlineGeometry', '../Core/GeometryInstance', + '../Core/GeometryOffsetAttribute', '../Core/Iso8601', + '../Core/OffsetGeometryInstanceAttribute', '../Core/ShowGeometryInstanceAttribute', '../Scene/GroundPrimitive', + '../Scene/HeightReference', '../Scene/MaterialAppearance', '../Scene/PerInstanceColorAppearance', './ColorMaterialProperty', './DynamicGeometryUpdater', + './GeometryHeightProperty', './GeometryUpdater', './GroundGeometryUpdater', './Property' ], function( + Cartesian3, Check, Color, ColorGeometryInstanceAttribute, @@ -28,19 +34,30 @@ define([ EllipseGeometry, EllipseOutlineGeometry, GeometryInstance, + GeometryOffsetAttribute, Iso8601, + OffsetGeometryInstanceAttribute, ShowGeometryInstanceAttribute, GroundPrimitive, + HeightReference, MaterialAppearance, PerInstanceColorAppearance, ColorMaterialProperty, DynamicGeometryUpdater, + GeometryHeightProperty, GeometryUpdater, GroundGeometryUpdater, Property) { 'use strict'; var scratchColor = new Color(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); + var scratchEllipseGeometry = new EllipseGeometry({ + center: new Cartesian3(), + semiMajorAxis: 2, + semiMinorAxis: 1 + }); function EllipseGeometryOptions(entity) { this.id = entity; @@ -54,6 +71,7 @@ define([ this.granularity = undefined; this.stRotation = undefined; this.numberOfVerticalLines = undefined; + this.offsetAttribute = undefined; } /** @@ -102,12 +120,11 @@ define([ var entity = this._entity; var isAvailable = entity.isAvailable(time); - var attributes; + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)) + }; - var color; - var show = new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)); - var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue(time); - var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition); if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if (defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable)) { @@ -116,17 +133,11 @@ define([ if (!defined(currentColor)) { currentColor = Color.WHITE; } - color = ColorGeometryInstanceAttribute.fromColor(currentColor); - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayConditionAttribute, - color : color - }; - } else { - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayConditionAttribute - }; + attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); + } + + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); } return new GeometryInstance({ @@ -158,17 +169,27 @@ define([ var outlineColor = Property.getValueOrDefault(this._outlineColorProperty, time, Color.BLACK, scratchColor); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue(time); + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), + color : ColorGeometryInstanceAttribute.fromColor(outlineColor), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition) + }; + + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); + } + return new GeometryInstance({ id : entity, geometry : new EllipseOutlineGeometry(this._options), - attributes : { - show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), - color : ColorGeometryInstanceAttribute.fromColor(outlineColor), - distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition) - } + attributes : attributes }); }; + EllipseGeometryUpdater.prototype._computeCenter = function(time, result) { + return Property.getValueOrUndefined(this._entity.position, time, result); + }; + EllipseGeometryUpdater.prototype._isHidden = function(entity, ellipse) { var position = entity.position; @@ -220,6 +241,12 @@ define([ options.granularity = defined(granularity) ? granularity.getValue(Iso8601.MINIMUM_VALUE) : undefined; options.stRotation = defined(stRotation) ? stRotation.getValue(Iso8601.MINIMUM_VALUE) : undefined; options.numberOfVerticalLines = defined(numberOfVerticalLines) ? numberOfVerticalLines.getValue(Iso8601.MINIMUM_VALUE) : undefined; + options.offsetAttribute = GeometryHeightProperty.computeGeometryOffsetAttribute(height, extrudedHeight, Iso8601.MINIMUM_VALUE); + + if (extrudedHeight instanceof GeometryHeightProperty && Property.getValueOrDefault(extrudedHeight.height, Iso8601.MINIMUM_VALUE, HeightReference.NONE) === HeightReference.CLAMP_TO_GROUND) { + scratchEllipseGeometry.setOptions(options); + options.extrudedHeight = GeometryHeightProperty.getMinimumTerrainValue(scratchEllipseGeometry.rectangle); + } }; EllipseGeometryUpdater.DynamicGeometryUpdater = DynamicEllipseGeometryUpdater; @@ -243,15 +270,23 @@ define([ DynamicEllipseGeometryUpdater.prototype._setOptions = function(entity, ellipse, time) { var options = this._options; + var height = ellipse.height; + var extrudedHeight = ellipse.extrudedHeight; options.center = Property.getValueOrUndefined(entity.position, time, options.center); options.semiMajorAxis = Property.getValueOrUndefined(ellipse.semiMajorAxis, time); options.semiMinorAxis = Property.getValueOrUndefined(ellipse.semiMinorAxis, time); options.rotation = Property.getValueOrUndefined(ellipse.rotation, time); - options.height = Property.getValueOrUndefined(ellipse.height, time); - options.extrudedHeight = Property.getValueOrUndefined(ellipse.extrudedHeight, time); + options.height = Property.getValueOrUndefined(height, time); + options.extrudedHeight = Property.getValueOrUndefined(extrudedHeight, time); options.granularity = Property.getValueOrUndefined(ellipse.granularity, time); options.stRotation = Property.getValueOrUndefined(ellipse.stRotation, time); options.numberOfVerticalLines = Property.getValueOrUndefined(ellipse.numberOfVerticalLines, time); + options.offsetAttribute = GeometryHeightProperty.computeGeometryOffsetAttribute(height, extrudedHeight, time); + + if (extrudedHeight instanceof GeometryHeightProperty && Property.getValueOrDefault(extrudedHeight.height, time, HeightReference.NONE) === HeightReference.CLAMP_TO_GROUND) { + scratchEllipseGeometry.setOptions(options); + options.extrudedHeight = GeometryHeightProperty.getMinimumTerrainValue(scratchEllipseGeometry.rectangle); + } }; return EllipseGeometryUpdater; diff --git a/Source/DataSources/GeometryHeightProperty.js b/Source/DataSources/GeometryHeightProperty.js new file mode 100644 index 000000000000..dd7438ef9084 --- /dev/null +++ b/Source/DataSources/GeometryHeightProperty.js @@ -0,0 +1,183 @@ +define([ + '../Core/ApproximateTerrainHeights', + '../Core/defined', + '../Core/defineProperties', + '../Core/Check', + '../Core/Event', + '../Core/GeometryOffsetAttribute', + '../Scene/HeightReference', + './createPropertyDescriptor', + './Property' +], function( + ApproximateTerrainHeights, + defined, + defineProperties, + Check, + Event, + GeometryOffsetAttribute, + HeightReference, + createPropertyDescriptor, + Property) { + 'use strict'; + + /** + * A {@link Property} which computes height or extrudedHeight for a {@link CorridorGraphics}, {@link EllipseGraphics}, {@link PolygonGraphics} or {@link RectangleGraphics}. + * + * @alias GeometryHeightProperty + * @constructor + * + * @param {Property} [height] A Property specifying the height in meters + * @param {Property} [heightReference=HeightReference.NONE] A Property specifying what the height is relative to. + * + * @example + * // A 40 meter tall extruded polygon clamped to terrain + * var redPolygon = viewer.entities.add({ + * polygon : { + * hierarchy : polygonPositions, + * material : Cesium.Color.RED, + * height : Cesium.GeometryHeightProperty(40.0, Cesium.HeightReference.RELATIVE_TO_GROUND), + * extrudedHeight : Cesium.GeometryHeightProperty(0.0, Cesium.HeightReference.CLAMP_TO_GROUND) + * } + * }); + * + * // An flat ellipse at 20 meters above terrain (does not contour to terrain) + * var blueEllipse = viewer.entities.add({ + * position: Cesium.Cartesian3.fromDegrees(-110.0, 43.0), + * ellipse : { + * semiMinorAxis : 300.0, + * semiMajorAxis : 200.0, + * material : Cesium.Color.BLUE, + * height : new Cesium.GeometryHeightProperty(20.0, Cesium.HeightReference.RELATIVE_TO_GROUND) + * } + * }); + */ + function GeometryHeightProperty(height, heightReference) { + this._height = undefined; + this._heightSubscription = undefined; + this._heightReference = undefined; + this._heightReferenceSubscription = undefined; + this._definitionChanged = new Event(); + + this.height = height; + this.heightReference = heightReference; + } + + defineProperties(GeometryHeightProperty.prototype, { + /** + * Gets a value indicating if this property is constant. + * @memberof GeometryHeightProperty.prototype + * + * @type {Boolean} + * @readonly + */ + isConstant : { + get : function() { + return Property.isConstant(this._height) && Property.isConstant(this._heightReference); + } + }, + /** + * Gets the event that is raised whenever the definition of this property changes. + * @memberof GeometryHeightProperty.prototype + * + * @type {Event} + * @readonly + */ + definitionChanged : { + get : function() { + return this._definitionChanged; + } + }, + /** + * Gets or sets the property used to compute the height. + * @memberof GeometryHeightProperty.prototype + * + * @type {Property} + */ + height : createPropertyDescriptor('height'), + /** + * Gets or sets the property used to define the height reference. + * @memberof GeometryHeightProperty.prototype + * + * @type {Property} + */ + heightReference : createPropertyDescriptor('heightReference') + }); + + /** + * Gets the minimum terrain height based on the positions at the provided time. + * + * @param {JulianDate} time The time for which to retrieve the value. + * @returns {Number} The minimum terrain height + */ + GeometryHeightProperty.prototype.getValue = function(time) { + //>>includeStart('debug', pragmas.debug); + Check.defined('time', time); + //>>includeEnd('debug'); + + var heightReference = Property.getValueOrDefault(this._heightReference, time, HeightReference.NONE); + + if (heightReference !== HeightReference.CLAMP_TO_GROUND) { + return Property.getValueOrDefault(this._height, time, 0); + } + return 0; + }; + + /** + * Used to get the minimum terrain value for when extrudedHeight is using CLAMP_TO_GROUND; + * @private + */ + GeometryHeightProperty.getMinimumTerrainValue = function(rectangle) { + //>>includeStart('debug', pragmas.debug); + Check.defined('rectangle', rectangle); + //>>includeEnd('debug'); + return ApproximateTerrainHeights.getApproximateTerrainHeights(rectangle).minimumTerrainHeight; + }; + + /** + * Gets the GeometryOffsetAttribute based on the values of height and extrudedHeight + * @param {Property} height + * @param {Property} extrudedHeight + * @param {JulianDate} time + * @returns {GeometryOffsetAttribute|undefined} + * @private + */ + GeometryHeightProperty.computeGeometryOffsetAttribute = function(height, extrudedHeight, time) { + if (!(extrudedHeight instanceof GeometryHeightProperty) && !(height instanceof GeometryHeightProperty)) { + return undefined; + } + + var heightReference = defined(height) ? Property.getValueOrDefault(height.heightReference, time, HeightReference.NONE) : HeightReference.NONE; + var extrudedHeightReference = defined(extrudedHeight) ? Property.getValueOrDefault(extrudedHeight.heightReference, time, HeightReference.NONE) : HeightReference.NONE; + + var n = 0; + if (heightReference !== HeightReference.NONE) { + n++; + } + if (extrudedHeightReference === HeightReference.RELATIVE_TO_GROUND) { + n++; + } + if (n === 2) { + return GeometryOffsetAttribute.ALL; + } + if (n === 1) { + return GeometryOffsetAttribute.TOP; + } + return undefined; + }; + + /** + * Compares this property to the provided property and returns + * true if they are equal, false otherwise. + * + * @param {Property} [other] The other property. + * @returns {Boolean} true if left and right are equal, false otherwise. + */ + GeometryHeightProperty.prototype.equals = function(other) { + return this === other ||// + (other instanceof GeometryHeightProperty && + Property.equals(this._height, other._height) && + Property.equals(this._heightReference, other._heightReference)); + }; + + return GeometryHeightProperty; +}); diff --git a/Source/DataSources/GeometryVisualizer.js b/Source/DataSources/GeometryVisualizer.js index 9ea93859248f..41f4ee0a792c 100644 --- a/Source/DataSources/GeometryVisualizer.js +++ b/Source/DataSources/GeometryVisualizer.js @@ -142,23 +142,31 @@ define([ this._changedObjects = new AssociativeArray(); var numberOfShadowModes = ShadowMode.NUMBER_OF_SHADOW_MODES; - this._outlineBatches = new Array(numberOfShadowModes); - this._closedColorBatches = new Array(numberOfShadowModes); - this._closedMaterialBatches = new Array(numberOfShadowModes); - this._openColorBatches = new Array(numberOfShadowModes); - this._openMaterialBatches = new Array(numberOfShadowModes); + this._outlineBatches = new Array(numberOfShadowModes*2); + this._closedColorBatches = new Array(numberOfShadowModes*2); + this._closedMaterialBatches = new Array(numberOfShadowModes*2); + this._openColorBatches = new Array(numberOfShadowModes*2); + this._openMaterialBatches = new Array(numberOfShadowModes*2); var supportsMaterialsforEntitiesOnTerrain = Entity.supportsMaterialsforEntitiesOnTerrain(scene); this._supportsMaterialsforEntitiesOnTerrain = supportsMaterialsforEntitiesOnTerrain; var i; for (i = 0; i < numberOfShadowModes; ++i) { - this._outlineBatches[i] = new StaticOutlineGeometryBatch(primitives, scene, i); + this._outlineBatches[i] = new StaticOutlineGeometryBatch(primitives, scene, i, false); + this._outlineBatches[numberOfShadowModes + i] = new StaticOutlineGeometryBatch(primitives, scene, i, true); - this._closedColorBatches[i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, true, i); - this._closedMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, true, i); - this._openColorBatches[i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, false, i); - this._openMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, false, i); + this._closedColorBatches[i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, true, i, true); + this._closedColorBatches[numberOfShadowModes + i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, true, i, false); + + this._closedMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, true, i, true); + this._closedMaterialBatches[numberOfShadowModes + i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, true, i, false); + + this._openColorBatches[i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, false, i, true); + this._openColorBatches[numberOfShadowModes + i] = new StaticGeometryColorBatch(primitives, PerInstanceColorAppearance, undefined, false, i, false); + + this._openMaterialBatches[i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, false, i, true); + this._openMaterialBatches[numberOfShadowModes + i] = new StaticGeometryPerMaterialBatch(primitives, MaterialAppearance, undefined, false, i, false); } var numberOfClassificationTypes = ClassificationType.NUMBER_OF_CLASSIFICATION_TYPES; @@ -390,8 +398,13 @@ define([ shadows = updater.shadowsProperty.getValue(time); } + var numberOfShadowModes = ShadowMode.NUMBER_OF_SHADOW_MODES; if (updater.outlineEnabled) { - this._outlineBatches[shadows].add(time, updater); + if (defined(updater.terrainOffsetProperty)) { + this._outlineBatches[numberOfShadowModes + shadows].add(time, updater); + } else { + this._outlineBatches[shadows].add(time, updater); + } } if (updater.fillEnabled) { @@ -408,14 +421,32 @@ define([ } } else if (updater.isClosed) { if (updater.fillMaterialProperty instanceof ColorMaterialProperty) { - this._closedColorBatches[shadows].add(time, updater); + if (defined(updater.terrainOffsetProperty)) { + this._closedColorBatches[numberOfShadowModes + shadows].add(time, updater); + } else { + this._closedColorBatches[shadows].add(time, updater); + } } else { - this._closedMaterialBatches[shadows].add(time, updater); + // eslint-disable-next-line no-lonely-if + if (defined(updater.terrainOffsetProperty)) { + this._closedMaterialBatches[numberOfShadowModes + shadows].add(time, updater); + } else { + this._closedMaterialBatches[shadows].add(time, updater); + } } } else if (updater.fillMaterialProperty instanceof ColorMaterialProperty) { - this._openColorBatches[shadows].add(time, updater); + if (defined(updater.terrainOffsetProperty)) { + this._openColorBatches[numberOfShadowModes + shadows].add(time, updater); + } else { + this._openColorBatches[shadows].add(time, updater); + } } else { - this._openMaterialBatches[shadows].add(time, updater); + // eslint-disable-next-line no-lonely-if + if (defined(updater.terrainOffsetProperty)) { + this._openMaterialBatches[numberOfShadowModes + shadows].add(time, updater); + } else { + this._openMaterialBatches[shadows].add(time, updater); + } } } }; diff --git a/Source/DataSources/GroundGeometryUpdater.js b/Source/DataSources/GroundGeometryUpdater.js index 43f2a98afc14..4d4c4d5c629b 100644 --- a/Source/DataSources/GroundGeometryUpdater.js +++ b/Source/DataSources/GroundGeometryUpdater.js @@ -1,19 +1,31 @@ define([ + '../Core/Cartesian3', + '../Core/Check', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', + '../Core/DeveloperError', '../Core/Iso8601', '../Core/oneTimeWarning', + '../Scene/HeightReference', './ConstantProperty', - './GeometryUpdater' + './GeometryHeightProperty', + './GeometryUpdater', + './TerrainOffsetProperty' ], function( + Cartesian3, + Check, defaultValue, defined, defineProperties, + DeveloperError, Iso8601, oneTimeWarning, + HeightReference, ConstantProperty, - GeometryUpdater) { + GeometryHeightProperty, + GeometryUpdater, + TerrainOffsetProperty) { 'use strict'; var defaultZIndex = new ConstantProperty(0); @@ -33,6 +45,7 @@ define([ GeometryUpdater.call(this, options); this._zIndex = 0; + this._terrainOffsetProperty = undefined; } if (defined(Object.create)) { @@ -51,9 +64,25 @@ define([ get: function() { return this._zIndex; } + }, + + terrainOffsetProperty: { + get: function() { + return this._terrainOffsetProperty; + } } }); + /** + * @param {Entity} entity + * @param {Object} geometry + * @param {JulianDate} time + * @param {Cartesian3} result + * + * @private + */ + GroundGeometryUpdater.prototype._computeCenter = DeveloperError.throwInstantiationError; + GroundGeometryUpdater.prototype._onEntityPropertyChanged = function(entity, propertyName, newValue, oldValue) { GeometryUpdater.prototype._onEntityPropertyChanged.call(this, entity, propertyName, newValue, oldValue); if (this._observedPropertyNames.indexOf(propertyName) === -1) { @@ -69,6 +98,19 @@ define([ } this._zIndex = defaultValue(geometry.zIndex, defaultZIndex); + + var heightProperty = geometry.height; + var extrudedHeightProperty = geometry.extrudedHeight; + + if (this._terrainOffsetProperty instanceof TerrainOffsetProperty) { + this._terrainOffsetProperty.destroy(); + } + + if (heightProperty instanceof GeometryHeightProperty || extrudedHeightProperty instanceof GeometryHeightProperty) { + this._terrainOffsetProperty = new TerrainOffsetProperty(this._scene, heightProperty, extrudedHeightProperty, this._computeCenter.bind(this)); + } else { + this._terrainOffsetProperty = undefined; + } }; return GroundGeometryUpdater; diff --git a/Source/DataSources/PolygonGeometryUpdater.js b/Source/DataSources/PolygonGeometryUpdater.js index c56094f34b35..32f55711bad1 100644 --- a/Source/DataSources/PolygonGeometryUpdater.js +++ b/Source/DataSources/PolygonGeometryUpdater.js @@ -1,4 +1,5 @@ define([ + '../Core/Cartesian3', '../Core/Check', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', @@ -6,21 +7,26 @@ define([ '../Core/DeveloperError', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', '../Core/GeometryInstance', + '../Core/GeometryOffsetAttribute', '../Core/isArray', '../Core/Iso8601', + '../Core/OffsetGeometryInstanceAttribute', '../Core/PolygonGeometry', '../Core/PolygonHierarchy', '../Core/PolygonOutlineGeometry', '../Core/ShowGeometryInstanceAttribute', '../Scene/GroundPrimitive', + '../Scene/HeightReference', '../Scene/MaterialAppearance', '../Scene/PerInstanceColorAppearance', './ColorMaterialProperty', './DynamicGeometryUpdater', + './GeometryHeightProperty', './GeometryUpdater', './GroundGeometryUpdater', './Property' ], function( + Cartesian3, Check, Color, ColorGeometryInstanceAttribute, @@ -28,23 +34,30 @@ define([ DeveloperError, DistanceDisplayConditionGeometryInstanceAttribute, GeometryInstance, + GeometryOffsetAttribute, isArray, Iso8601, + OffsetGeometryInstanceAttribute, PolygonGeometry, PolygonHierarchy, PolygonOutlineGeometry, ShowGeometryInstanceAttribute, GroundPrimitive, + HeightReference, MaterialAppearance, PerInstanceColorAppearance, ColorMaterialProperty, DynamicGeometryUpdater, + GeometryHeightProperty, GeometryUpdater, GroundGeometryUpdater, Property) { 'use strict'; var scratchColor = new Color(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); + var scratchPolygonGeometry = new PolygonGeometry({polygonHierarchy: new PolygonHierarchy}); function PolygonGeometryOptions(entity) { this.id = entity; @@ -57,6 +70,7 @@ define([ this.extrudedHeight = undefined; this.granularity = undefined; this.stRotation = undefined; + this.offsetAttribute = undefined; } /** @@ -105,12 +119,11 @@ define([ var entity = this._entity; var isAvailable = entity.isAvailable(time); - var attributes; + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)) + }; - var color; - var show = new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)); - var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue(time); - var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition); if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if (defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable)) { @@ -119,17 +132,10 @@ define([ if (!defined(currentColor)) { currentColor = Color.WHITE; } - color = ColorGeometryInstanceAttribute.fromColor(currentColor); - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayConditionAttribute, - color : color - }; - } else { - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayConditionAttribute - }; + attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); + } + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); } return new GeometryInstance({ @@ -161,17 +167,44 @@ define([ var outlineColor = Property.getValueOrDefault(this._outlineColorProperty, time, Color.BLACK, scratchColor); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue(time); + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), + color : ColorGeometryInstanceAttribute.fromColor(outlineColor), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition) + }; + + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); + } + return new GeometryInstance({ id : entity, geometry : new PolygonOutlineGeometry(this._options), - attributes : { - show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), - color : ColorGeometryInstanceAttribute.fromColor(outlineColor), - distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition) - } + attributes : attributes }); }; + PolygonGeometryUpdater.prototype._computeCenter = function(time, result) { + var positions = Property.getValueOrUndefined(this._entity.polygon.hierarchy, time); + if (defined(positions) && !isArray(positions)) { + positions = positions.positions; + } + if (positions.length === 0) { + return; + } + + var centroid = Cartesian3.clone(Cartesian3.ZERO, result); + var length = positions.length; + for (var i = 0; i < length; i++) { + centroid = Cartesian3.add(positions[i], centroid, centroid); + } + centroid = Cartesian3.multiplyByScalar(centroid, 1 / length, centroid); + if (defined(this._scene.globe)) { + centroid = this._scene.globe.ellipsoid.scaleToGeodeticSurface(centroid, centroid); + } + return centroid; + }; + PolygonGeometryUpdater.prototype._isHidden = function(entity, polygon) { return !defined(polygon.hierarchy) || GeometryUpdater.prototype._isHidden.call(this, entity, polygon); }; @@ -208,10 +241,13 @@ define([ hierarchyValue = new PolygonHierarchy(hierarchyValue); } - var heightValue = Property.getValueOrUndefined(polygon.height, Iso8601.MINIMUM_VALUE); + var height = polygon.height; + var extrudedHeight = polygon.extrudedHeight; + + var heightValue = Property.getValueOrUndefined(height, Iso8601.MINIMUM_VALUE); var closeTopValue = Property.getValueOrDefault(polygon.closeTop, Iso8601.MINIMUM_VALUE, true); var closeBottomValue = Property.getValueOrDefault(polygon.closeBottom, Iso8601.MINIMUM_VALUE, true); - var extrudedHeightValue = Property.getValueOrUndefined(polygon.extrudedHeight, Iso8601.MINIMUM_VALUE); + var extrudedHeightValue = Property.getValueOrUndefined(extrudedHeight, Iso8601.MINIMUM_VALUE); var perPositionHeightValue = Property.getValueOrUndefined(polygon.perPositionHeight, Iso8601.MINIMUM_VALUE); if (defined(extrudedHeightValue) && !defined(heightValue) && !defined(perPositionHeightValue)) { @@ -226,6 +262,12 @@ define([ options.perPositionHeight = perPositionHeightValue; options.closeTop = closeTopValue; options.closeBottom = closeBottomValue; + options.offsetAttribute = GeometryHeightProperty.computeGeometryOffsetAttribute(height, extrudedHeight, Iso8601.MINIMUM_VALUE); + + if (extrudedHeight instanceof GeometryHeightProperty && Property.getValueOrDefault(extrudedHeight.height, Iso8601.MINIMUM_VALUE, HeightReference.NONE) === HeightReference.CLAMP_TO_GROUND) { + scratchPolygonGeometry.setOptions(options); + options.extrudedHeight = GeometryHeightProperty.getMinimumTerrainValue(scratchPolygonGeometry.rectangle); + } }; PolygonGeometryUpdater.prototype._getIsClosed = function(options) { @@ -255,6 +297,9 @@ define([ DyanmicPolygonGeometryUpdater.prototype._setOptions = function(entity, polygon, time) { var options = this._options; + var height = polygon.height; + var extrudedHeight = polygon.extrudedHeight; + var hierarchy = Property.getValueOrUndefined(polygon.hierarchy, time); if (isArray(hierarchy)) { options.polygonHierarchy = new PolygonHierarchy(hierarchy); @@ -262,13 +307,20 @@ define([ options.polygonHierarchy = hierarchy; } - options.height = Property.getValueOrUndefined(polygon.height, time); - options.extrudedHeight = Property.getValueOrUndefined(polygon.extrudedHeight, time); + options.height = Property.getValueOrUndefined(height, time); + options.extrudedHeight = Property.getValueOrUndefined(extrudedHeight, time); options.granularity = Property.getValueOrUndefined(polygon.granularity, time); options.stRotation = Property.getValueOrUndefined(polygon.stRotation, time); options.perPositionHeight = Property.getValueOrUndefined(polygon.perPositionHeight, time); options.closeTop = Property.getValueOrDefault(polygon.closeTop, time, true); options.closeBottom = Property.getValueOrDefault(polygon.closeBottom, time, true); + options.offsetAttribute = GeometryHeightProperty.computeGeometryOffsetAttribute(height, extrudedHeight, time); + + if (extrudedHeight instanceof GeometryHeightProperty && Property.getValueOrDefault(extrudedHeight.height, time, HeightReference.NONE) === HeightReference.CLAMP_TO_GROUND) { + scratchPolygonGeometry.setOptions(options); + options.extrudedHeight = GeometryHeightProperty.getMinimumTerrainValue(scratchPolygonGeometry.rectangle); + } + }; return PolygonGeometryUpdater; diff --git a/Source/DataSources/RectangleGeometryUpdater.js b/Source/DataSources/RectangleGeometryUpdater.js index 21e6c35a89a0..48416ea3bd83 100644 --- a/Source/DataSources/RectangleGeometryUpdater.js +++ b/Source/DataSources/RectangleGeometryUpdater.js @@ -1,46 +1,67 @@ define([ + '../Core/Cartesian3', + '../Core/Cartographic', '../Core/Check', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', '../Core/defined', '../Core/DeveloperError', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', + '../Core/Ellipsoid', '../Core/GeometryInstance', + '../Core/GeometryOffsetAttribute', '../Core/Iso8601', + '../Core/OffsetGeometryInstanceAttribute', + '../Core/Rectangle', '../Core/RectangleGeometry', '../Core/RectangleOutlineGeometry', '../Core/ShowGeometryInstanceAttribute', '../Scene/GroundPrimitive', + '../Scene/HeightReference', '../Scene/MaterialAppearance', '../Scene/PerInstanceColorAppearance', './ColorMaterialProperty', './DynamicGeometryUpdater', + './GeometryHeightProperty', './GeometryUpdater', './GroundGeometryUpdater', './Property' ], function( + Cartesian3, + Cartographic, Check, Color, ColorGeometryInstanceAttribute, defined, DeveloperError, DistanceDisplayConditionGeometryInstanceAttribute, + Ellipsoid, GeometryInstance, + GeometryOffsetAttribute, Iso8601, + OffsetGeometryInstanceAttribute, + Rectangle, RectangleGeometry, RectangleOutlineGeometry, ShowGeometryInstanceAttribute, GroundPrimitive, + HeightReference, MaterialAppearance, PerInstanceColorAppearance, ColorMaterialProperty, DynamicGeometryUpdater, + GeometryHeightProperty, GeometryUpdater, GroundGeometryUpdater, Property) { 'use strict'; var scratchColor = new Color(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); + var scratchRectangleGeometry = new RectangleGeometry({rectangle: new Rectangle()}); + var scratchCenterRect = new Rectangle(); + var scratchCarto = new Cartographic(); function RectangleGeometryOptions(entity) { this.id = entity; @@ -51,6 +72,7 @@ define([ this.granularity = undefined; this.stRotation = undefined; this.rotation = undefined; + this.offsetAttribute = undefined; } /** @@ -99,12 +121,11 @@ define([ var entity = this._entity; var isAvailable = entity.isAvailable(time); - var attributes; + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(this._distanceDisplayConditionProperty.getValue(time)) + }; - var color; - var show = new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time)); - var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue(time); - var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition); if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if (defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable)) { @@ -113,17 +134,10 @@ define([ if (!defined(currentColor)) { currentColor = Color.WHITE; } - color = ColorGeometryInstanceAttribute.fromColor(currentColor); - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayConditionAttribute, - color : color - }; - } else { - attributes = { - show : show, - distanceDisplayCondition : distanceDisplayConditionAttribute - }; + attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); + } + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); } return new GeometryInstance({ @@ -155,17 +169,32 @@ define([ var outlineColor = Property.getValueOrDefault(this._outlineColorProperty, time, Color.BLACK, scratchColor); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue(time); + var attributes = { + show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), + color : ColorGeometryInstanceAttribute.fromColor(outlineColor), + distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition) + }; + + if (defined(this._options.offsetAttribute)) { + attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3(Property.getValueOrDefault(this._terrainOffsetProperty, time, defaultOffset, offsetScratch)); + } + return new GeometryInstance({ id : entity, geometry : new RectangleOutlineGeometry(this._options), - attributes : { - show : new ShowGeometryInstanceAttribute(isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time)), - color : ColorGeometryInstanceAttribute.fromColor(outlineColor), - distanceDisplayCondition : DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition) - } + attributes : attributes }); }; + RectangleGeometryUpdater.prototype._computeCenter = function(time, result) { + var rect = Property.getValueOrUndefined(this._entity.rectangle.coordinates, time, scratchCenterRect); + if (!defined(rect)) { + return; + } + var center = Rectangle.center(rect, scratchCarto); + return Cartographic.toCartesian(center, Ellipsoid.WGS84, result); + }; + RectangleGeometryUpdater.prototype._isHidden = function(entity, rectangle) { return !defined(rectangle.coordinates) || GeometryUpdater.prototype._isHidden.call(this, entity, rectangle); }; @@ -203,6 +232,12 @@ define([ options.granularity = defined(granularity) ? granularity.getValue(Iso8601.MINIMUM_VALUE) : undefined; options.stRotation = defined(stRotation) ? stRotation.getValue(Iso8601.MINIMUM_VALUE) : undefined; options.rotation = defined(rotation) ? rotation.getValue(Iso8601.MINIMUM_VALUE) : undefined; + options.offsetAttribute = GeometryHeightProperty.computeGeometryOffsetAttribute(height, extrudedHeight, Iso8601.MINIMUM_VALUE); + + if (extrudedHeight instanceof GeometryHeightProperty && Property.getValueOrDefault(extrudedHeight.height, Iso8601.MINIMUM_VALUE, HeightReference.NONE) === HeightReference.CLAMP_TO_GROUND) { + scratchRectangleGeometry.setOptions(options); + options.extrudedHeight = GeometryHeightProperty.getMinimumTerrainValue(scratchRectangleGeometry.rectangle); + } }; RectangleGeometryUpdater.prototype._getIsClosed = function(options) { @@ -231,12 +266,21 @@ define([ DynamicRectangleGeometryUpdater.prototype._setOptions = function(entity, rectangle, time) { var options = this._options; + var height = rectangle.height; + var extrudedHeight = rectangle.extrudedHeight; + options.rectangle = Property.getValueOrUndefined(rectangle.coordinates, time, options.rectangle); - options.height = Property.getValueOrUndefined(rectangle.height, time); - options.extrudedHeight = Property.getValueOrUndefined(rectangle.extrudedHeight, time); + options.height = Property.getValueOrUndefined(height, time); + options.extrudedHeight = Property.getValueOrUndefined(extrudedHeight, time); options.granularity = Property.getValueOrUndefined(rectangle.granularity, time); options.stRotation = Property.getValueOrUndefined(rectangle.stRotation, time); options.rotation = Property.getValueOrUndefined(rectangle.rotation, time); + options.offsetAttribute = GeometryHeightProperty.computeGeometryOffsetAttribute(height, extrudedHeight, time); + + if (extrudedHeight instanceof GeometryHeightProperty && Property.getValueOrDefault(extrudedHeight.height, time, HeightReference.NONE) === HeightReference.CLAMP_TO_GROUND) { + scratchRectangleGeometry.setOptions(options); + options.extrudedHeight = GeometryHeightProperty.getMinimumTerrainValue(scratchRectangleGeometry.rectangle); + } }; return RectangleGeometryUpdater; diff --git a/Source/DataSources/StaticGeometryColorBatch.js b/Source/DataSources/StaticGeometryColorBatch.js index 32168a30c123..7f87f7a1f699 100644 --- a/Source/DataSources/StaticGeometryColorBatch.js +++ b/Source/DataSources/StaticGeometryColorBatch.js @@ -1,10 +1,12 @@ define([ '../Core/AssociativeArray', + '../Core/Cartesian3', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', '../Core/defined', '../Core/DistanceDisplayCondition', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', + '../Core/OffsetGeometryInstanceAttribute', '../Core/ShowGeometryInstanceAttribute', '../Scene/Primitive', './BoundingSphereState', @@ -13,11 +15,13 @@ define([ './Property' ], function( AssociativeArray, + Cartesian3, Color, ColorGeometryInstanceAttribute, defined, DistanceDisplayCondition, DistanceDisplayConditionGeometryInstanceAttribute, + OffsetGeometryInstanceAttribute, ShowGeometryInstanceAttribute, Primitive, BoundingSphereState, @@ -29,6 +33,8 @@ define([ var colorScratch = new Color(); var distanceDisplayConditionScratch = new DistanceDisplayCondition(); var defaultDistanceDisplayCondition = new DistanceDisplayCondition(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); function Batch(primitives, translucent, appearanceType, depthFailAppearanceType, depthFailMaterialProperty, closed, shadows) { this.translucent = translucent; @@ -80,7 +86,7 @@ define([ this.createPrimitive = true; this.geometry.set(id, instance); this.updaters.set(id, updater); - if (!updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty)) { + if (!updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty) || !Property.isConstant(updater.terrainOffsetProperty)) { this.updatersWithAttributes.set(id, updater); } else { var that = this; @@ -245,6 +251,15 @@ define([ attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue(distanceDisplayCondition, attributes.distanceDisplayCondition); } } + + var offsetProperty = updater.terrainOffsetProperty; + if (!Property.isConstant(offsetProperty)) { + var offset = Property.getValueOrDefault(offsetProperty, time, defaultOffset, offsetScratch); + if (!Cartesian3.equals(offset, attributes._lastOffset)) { + attributes._lastOffset = Cartesian3.clone(offset, attributes._lastOffset); + attributes.offset = OffsetGeometryInstanceAttribute.toValue(offset, attributes.offset); + } + } } this.updateShows(primitive); diff --git a/Source/DataSources/StaticGeometryPerMaterialBatch.js b/Source/DataSources/StaticGeometryPerMaterialBatch.js index 4734c5be14fd..0fd9eed26e2c 100644 --- a/Source/DataSources/StaticGeometryPerMaterialBatch.js +++ b/Source/DataSources/StaticGeometryPerMaterialBatch.js @@ -1,10 +1,12 @@ define([ '../Core/AssociativeArray', + '../Core/Cartesian3', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', '../Core/defined', '../Core/DistanceDisplayCondition', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', + '../Core/OffsetGeometryInstanceAttribute', '../Core/ShowGeometryInstanceAttribute', '../Scene/Primitive', './BoundingSphereState', @@ -13,11 +15,13 @@ define([ './Property' ], function( AssociativeArray, + Cartesian3, Color, ColorGeometryInstanceAttribute, defined, DistanceDisplayCondition, DistanceDisplayConditionGeometryInstanceAttribute, + OffsetGeometryInstanceAttribute, ShowGeometryInstanceAttribute, Primitive, BoundingSphereState, @@ -28,6 +32,8 @@ define([ var distanceDisplayConditionScratch = new DistanceDisplayCondition(); var defaultDistanceDisplayCondition = new DistanceDisplayCondition(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); function Batch(primitives, appearanceType, materialProperty, depthFailAppearanceType, depthFailMaterialProperty, closed, shadows) { this.primitives = primitives; @@ -74,7 +80,7 @@ define([ var id = updater.id; this.updaters.set(id, updater); this.geometry.set(id, updater.createFillGeometryInstance(time)); - if (!updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty)) { + if (!updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty) || !Property.isConstant(updater.terrainOffsetProperty)) { this.updatersWithAttributes.set(id, updater); } else { var that = this; @@ -234,6 +240,15 @@ define([ attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue(distanceDisplayCondition, attributes.distanceDisplayCondition); } } + + var offsetProperty = updater.terrainOffsetProperty; + if (!Property.isConstant(offsetProperty)) { + var offset = Property.getValueOrDefault(offsetProperty, time, defaultOffset, offsetScratch); + if (!Cartesian3.equals(offset, attributes._lastOffset)) { + attributes._lastOffset = Cartesian3.clone(offset, attributes._lastOffset); + attributes.offset = OffsetGeometryInstanceAttribute.toValue(offset, attributes.offset); + } + } } this.updateShows(primitive); diff --git a/Source/DataSources/StaticOutlineGeometryBatch.js b/Source/DataSources/StaticOutlineGeometryBatch.js index 848154fa3c51..def326371c71 100644 --- a/Source/DataSources/StaticOutlineGeometryBatch.js +++ b/Source/DataSources/StaticOutlineGeometryBatch.js @@ -1,10 +1,12 @@ define([ '../Core/AssociativeArray', + '../Core/Cartesian3', '../Core/Color', '../Core/ColorGeometryInstanceAttribute', '../Core/defined', '../Core/DistanceDisplayCondition', '../Core/DistanceDisplayConditionGeometryInstanceAttribute', + '../Core/OffsetGeometryInstanceAttribute', '../Core/ShowGeometryInstanceAttribute', '../Scene/PerInstanceColorAppearance', '../Scene/Primitive', @@ -12,11 +14,13 @@ define([ './Property' ], function( AssociativeArray, + Cartesian3, Color, ColorGeometryInstanceAttribute, defined, DistanceDisplayCondition, DistanceDisplayConditionGeometryInstanceAttribute, + OffsetGeometryInstanceAttribute, ShowGeometryInstanceAttribute, PerInstanceColorAppearance, Primitive, @@ -27,6 +31,8 @@ define([ var colorScratch = new Color(); var distanceDisplayConditionScratch = new DistanceDisplayCondition(); var defaultDistanceDisplayCondition = new DistanceDisplayCondition(); + var defaultOffset = Cartesian3.ZERO; + var offsetScratch = new Cartesian3(); function Batch(primitives, translucent, width, shadows) { this.translucent = translucent; @@ -50,7 +56,7 @@ define([ this.createPrimitive = true; this.geometry.set(id, instance); this.updaters.set(id, updater); - if (!updater.hasConstantOutline || !updater.outlineColorProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty)) { + if (!updater.hasConstantOutline || !updater.outlineColorProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty) || !Property.isConstant(updater.terrainOffsetProperty)) { this.updatersWithAttributes.set(id, updater); } else { var that = this; @@ -189,6 +195,15 @@ define([ attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue(distanceDisplayCondition, attributes.distanceDisplayCondition); } } + + var offsetProperty = updater.terrainOffsetProperty; + if (!Property.isConstant(offsetProperty)) { + var offset = Property.getValueOrDefault(offsetProperty, time, defaultOffset, offsetScratch); + if (!Cartesian3.equals(offset, attributes._lastOffset)) { + attributes._lastOffset = Cartesian3.clone(offset, attributes._lastOffset); + attributes.offset = OffsetGeometryInstanceAttribute.toValue(offset, attributes.offset); + } + } } this.updateShows(primitive); diff --git a/Source/DataSources/TerrainOffsetProperty.js b/Source/DataSources/TerrainOffsetProperty.js new file mode 100644 index 000000000000..7a0612ae1c8f --- /dev/null +++ b/Source/DataSources/TerrainOffsetProperty.js @@ -0,0 +1,199 @@ +define([ + '../Core/Cartesian3', + '../Core/Cartographic', + '../Core/Check', + '../Core/defined', + '../Core/defineProperties', + '../Core/destroyObject', + '../Core/Event', + '../Core/Iso8601', + '../Core/Math', + '../Scene/HeightReference', + './Property' +], function( + Cartesian3, + Cartographic, + Check, + defined, + defineProperties, + destroyObject, + Event, + Iso8601, + CesiumMath, + HeightReference, + Property) { + 'use strict'; + + var scratchPosition = new Cartesian3(); + var scratchCarto = new Cartographic(); + + /** + * @private + * @param {Scene} scene + * @param {Property} height + * @param {Property} extrudedHeight + * @param {TerrainOffsetProperty~PositionFunction} getPosition + * @constructor + */ + function TerrainOffsetProperty(scene, height, extrudedHeight, getPosition) { + //>>includeStart('debug', pragmas.debug); + Check.defined('scene', scene); + Check.defined('height', height); + Check.defined('extrudedHeight', extrudedHeight); + Check.typeOf.func('getPosition', getPosition); + //>>includeEnd('debug'); + + this._scene = scene; + this._height = height; + this._extrudedHeight = extrudedHeight; + this._getPosition = getPosition; + + this._position = new Cartesian3(); + this._cartographicPosition = new Cartographic(); + this._normal = new Cartesian3(); + + this._definitionChanged = new Event(); + this._terrainHeight = 0; + this._removeCallbackFunc = undefined; + this._removeEventListener = undefined; + this._removeModeListener = undefined; + + var that = this; + if (defined(scene.globe)) { + this._removeEventListener = scene.terrainProviderChanged.addEventListener(function() { + that._updateClamping(); + }); + this._removeModeListener = scene.morphComplete.addEventListener(function() { + that._updateClamping(); + }); + } + } + + defineProperties(TerrainOffsetProperty.prototype, { + /** + * Gets a value indicating if this property is constant. + * @memberof TerrainOffsetProperty.prototype + * + * @type {Boolean} + * @readonly + */ + isConstant : { + get : function() { + return false; + } + }, + /** + * Gets the event that is raised whenever the definition of this property changes. + * @memberof TerrainOffsetProperty.prototype + * + * @type {Event} + * @readonly + */ + definitionChanged : { + get : function() { + return this._definitionChanged; + } + } + }); + + /** + * @private + */ + TerrainOffsetProperty.prototype._updateClamping = function() { + var scene = this._scene; + var globe = scene.globe; + if (!defined(globe)) { + this._terrainHeight = 0; + return; + } + var ellipsoid = globe.ellipsoid; + var surface = globe._surface; + + var position = this._position; + if (defined(this._removeCallbackFunc)) { + this._removeCallbackFunc(); + } + + var that = this; + var cartographicPosition = ellipsoid.cartesianToCartographic(position, this._cartographicPosition); + var height = globe.getHeight(cartographicPosition); + if (defined(height)) { + this._terrainHeight = height; + } else { + this._terrainHeight = 0; + } + + function updateFunction(clampedPosition) { + var carto = ellipsoid.cartesianToCartographic(clampedPosition, scratchCarto); + that._terrainHeight = carto.height; + that.definitionChanged.raiseEvent(); + } + this._removeCallbackFunc = surface.updateHeight(cartographicPosition, updateFunction); + }; + + /** + * Gets the height relative to the terrain based on the positions. + * + * @returns {Cartesian3} The offset + */ + TerrainOffsetProperty.prototype.getValue = function(time, result) { + var heightProperty = this._height; + var extrudedHeightProperty = this._extrudedHeight; + var heightReference = HeightReference.NONE; + var extrudedHeightReference = HeightReference.NONE; + if (defined(heightProperty)) { + heightReference = Property.getValueOrDefault(heightProperty.heightReference, time, HeightReference.NONE); + } + if (defined(extrudedHeightProperty)) { + extrudedHeightReference = Property.getValueOrDefault(extrudedHeightProperty.heightReference, time, HeightReference.NONE); + } + + if (heightReference === HeightReference.NONE && extrudedHeightReference !== HeightReference.RELATIVE_TO_GROUND) { + return Cartesian3.clone(Cartesian3.ZERO, result); + } + + var scene = this._scene; + var position = this._getPosition(time, scratchPosition); + if (!defined(position) || Cartesian3.equals(position, Cartesian3.ZERO) || !defined(scene.globe)) { + return Cartesian3.clone(Cartesian3.ZERO, result); + } + + if (Cartesian3.equalsEpsilon(this._position, position, CesiumMath.EPSILON10)) { + return Cartesian3.multiplyByScalar(this._normal, this._terrainHeight, result); + } + + this._position = Cartesian3.clone(position, this._position); + + this._updateClamping(); + + var normal = scene.globe.ellipsoid.geodeticSurfaceNormal(position, this._normal); + return Cartesian3.multiplyByScalar(normal, this._terrainHeight, result); + }; + + TerrainOffsetProperty.prototype.isDestroyed = function() { + return false; + }; + + TerrainOffsetProperty.prototype.destroy = function() { + if (defined(this._removeEventListener)) { + this._removeEventListener(); + } + if (defined(this._removeModeListener)) { + this._removeModeListener(); + } + if (defined(this._removeCallbackFunc)) { + this._removeCallbackFunc(); + } + return destroyObject(this); + }; + + /** + * A function which creates one or more providers. + * @callback TerrainOffsetProperty~PositionFunction + * @param {JulianDate} time The clock time at which to retrieve the position + * @param {Cartesian3} result The result position + * @returns {Cartesian3} The position at which to do the terrain height check + */ + + return TerrainOffsetProperty; +}); diff --git a/Specs/DataSources/CorridorGeometryUpdaterSpec.js b/Specs/DataSources/CorridorGeometryUpdaterSpec.js index 940a2718d92b..d074c244febb 100644 --- a/Specs/DataSources/CorridorGeometryUpdaterSpec.js +++ b/Specs/DataSources/CorridorGeometryUpdaterSpec.js @@ -2,7 +2,9 @@ defineSuite([ 'DataSources/CorridorGeometryUpdater', 'Core/Cartesian3', 'Core/CornerType', + 'Core/GeometryOffsetAttribute', 'Core/JulianDate', + 'Core/Math', 'Core/TimeInterval', 'Core/TimeIntervalCollection', 'DataSources/ConstantProperty', @@ -22,7 +24,9 @@ defineSuite([ CorridorGeometryUpdater, Cartesian3, CornerType, + GeometryOffsetAttribute, JulianDate, + CesiumMath, TimeInterval, TimeIntervalCollection, ConstantProperty, @@ -54,7 +58,7 @@ defineSuite([ function createBasicCorridor() { var corridor = new CorridorGraphics(); - corridor.positions = new ConstantProperty(Cartesian3.fromRadiansArray([ + corridor.positions = new ConstantProperty(Cartesian3.fromDegreesArray([ 0, 0, 1, 0, 1, 1, @@ -69,13 +73,13 @@ defineSuite([ function createDynamicCorridor() { var entity = createBasicCorridor(); - entity.corridor.positions = createDynamicProperty(Cartesian3.fromRadiansArray([0, 0, 1, 0, 1, 1, 0, 1])); + entity.corridor.positions = createDynamicProperty(Cartesian3.fromDegreesArray([0, 0, 1, 0, 1, 1, 0, 1])); return entity; } function createBasicCorridorWithoutHeight() { var corridor = new CorridorGraphics(); - corridor.positions = new ConstantProperty(Cartesian3.fromRadiansArray([ + corridor.positions = new ConstantProperty(Cartesian3.fromDegreesArray([ 0, 0, 1, 0, 1, 1, @@ -89,7 +93,7 @@ defineSuite([ function createDynamicCorridorWithoutHeight() { var entity = createBasicCorridorWithoutHeight(); - entity.corridor.positions = createDynamicProperty(Cartesian3.fromRadiansArray([0, 0, 1, 0, 1, 1, 0, 1])); + entity.corridor.positions = createDynamicProperty(Cartesian3.fromDegreesArray([0, 0, 1, 0, 1, 1, 0, 1])); return entity; } @@ -193,6 +197,7 @@ defineSuite([ expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); expect(geometry._cornerType).toEqual(options.cornerType); + expect(geometry._offsetAttribute).toBeUndefined(); instance = updater.createOutlineGeometryInstance(time); geometry = instance.geometry; @@ -201,11 +206,12 @@ defineSuite([ expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); expect(geometry._cornerType).toEqual(options.cornerType); + expect(geometry._offsetAttribute).toBeUndefined(); }); it('dynamic updater sets properties', function() { var corridor = new CorridorGraphics(); - corridor.positions = createDynamicProperty(Cartesian3.fromRadiansArray([ + corridor.positions = createDynamicProperty(Cartesian3.fromDegreesArray([ 0, 0, 1, 0, 1, 1, @@ -234,6 +240,7 @@ defineSuite([ expect(options.width).toEqual(corridor.width.getValue()); expect(options.granularity).toEqual(corridor.granularity.getValue()); expect(options.cornerType).toEqual(corridor.cornerType.getValue()); + expect(options.offsetAttribute).toBeUndefined(); }); it('geometryChanged event is raised when expected', function() { @@ -268,6 +275,13 @@ defineSuite([ expect(listener.calls.count()).toEqual(4); }); + it('computes center', function() { + var entity = createBasicCorridor(); + var updater = new CorridorGeometryUpdater(entity, scene); + + expect(updater._computeCenter(time)).toEqualEpsilon(Cartesian3.fromDegrees(1.0, 1.0), CesiumMath.EPSILON10); + }); + function getScene() { return scene; } diff --git a/Specs/DataSources/EllipseGeometryUpdaterSpec.js b/Specs/DataSources/EllipseGeometryUpdaterSpec.js index a429a8dd859a..a10f242244c3 100644 --- a/Specs/DataSources/EllipseGeometryUpdaterSpec.js +++ b/Specs/DataSources/EllipseGeometryUpdaterSpec.js @@ -1,6 +1,7 @@ defineSuite([ 'DataSources/EllipseGeometryUpdater', 'Core/Cartesian3', + 'Core/GeometryOffsetAttribute', 'Core/JulianDate', 'Core/TimeIntervalCollection', 'DataSources/ConstantPositionProperty', @@ -18,6 +19,7 @@ defineSuite([ ], function( EllipseGeometryUpdater, Cartesian3, + GeometryOffsetAttribute, JulianDate, TimeIntervalCollection, ConstantPositionProperty, @@ -234,6 +236,7 @@ defineSuite([ expect(geometry._height).toEqual(options.height); expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); + expect(geometry._offsetAttribute).toBeUndefined(); instance = updater.createOutlineGeometryInstance(time); geometry = instance.geometry; @@ -245,6 +248,7 @@ defineSuite([ expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); expect(geometry._numberOfVerticalLines).toEqual(options.numberOfVerticalLines); + expect(geometry._offsetAttribute).toBeUndefined(); }); it('dynamic updater sets properties', function() { @@ -265,6 +269,7 @@ defineSuite([ expect(options.semiMajorAxis).toEqual(ellipse.semiMajorAxis.getValue()); expect(options.semiMinorAxis).toEqual(ellipse.semiMinorAxis.getValue()); expect(options.height).toEqual(ellipse.height.getValue()); + expect(options.offsetAttribute).toBeUndefined(); }); it('geometryChanged event is raised when expected', function() { @@ -304,6 +309,13 @@ defineSuite([ expect(listener.calls.count()).toEqual(5); }); + it('computes center', function() { + var entity = createBasicEllipse(); + var updater = new EllipseGeometryUpdater(entity, scene); + + expect(updater._computeCenter(time)).toEqual(entity.position.getValue(time)); + }); + function getScene() { return scene; } diff --git a/Specs/DataSources/GeometryHeightPropertySpec.js b/Specs/DataSources/GeometryHeightPropertySpec.js new file mode 100644 index 000000000000..bdaf1021c505 --- /dev/null +++ b/Specs/DataSources/GeometryHeightPropertySpec.js @@ -0,0 +1,150 @@ +defineSuite([ + 'DataSources/GeometryHeightProperty', + 'Core/ApproximateTerrainHeights', + 'Core/Event', + 'Core/JulianDate', + 'Core/Rectangle', + 'Scene/HeightReference', + 'DataSources/ConstantProperty' +], function( + GeometryHeightProperty, + ApproximateTerrainHeights, + Event, + JulianDate, + Rectangle, + HeightReference, + ConstantProperty) { + 'use strict'; + + var time = JulianDate.now(); + beforeAll(function() { + return ApproximateTerrainHeights.initialize(); + }); + + afterAll(function() { + ApproximateTerrainHeights._initPromise = undefined; + ApproximateTerrainHeights._terrainHeights = undefined; + }); + + it('can default construct', function() { + var property = new GeometryHeightProperty(); + expect(property.isConstant).toBe(true); + expect(property.definitionChanged).toBeInstanceOf(Event); + expect(property.height).toBeUndefined(); + expect(property.heightReference).toBeUndefined(); + expect(property.getValue(time)).toBe(0); + }); + + it('can construct with arguments', function() { + var height = new ConstantProperty(30); + var heightReference = new ConstantProperty(HeightReference.NONE); + var property = new GeometryHeightProperty(height, heightReference); + expect(property.isConstant).toBe(true); + expect(property.definitionChanged).toBeInstanceOf(Event); + expect(property.height).toBe(height); + expect(property.heightReference).toBe(heightReference); + expect(property.getValue(time)).toBe(30); + }); + + it('setting height raises definitionChanged event', function() { + var property = new GeometryHeightProperty(); + + var listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + property.height = new ConstantProperty(); + expect(listener).toHaveBeenCalledWith(property); + }); + + it('setting heightReference raises definitionChanged event', function() { + var property = new GeometryHeightProperty(); + + var listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + property.heightReference = new ConstantProperty(); + expect(listener).toHaveBeenCalledWith(property); + }); + + it('setting height does not raise definitionChanged event for same data', function() { + var height = new ConstantProperty(30); + var property = new GeometryHeightProperty(height); + + var listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + property.height = height; + expect(listener.calls.count()).toBe(0); + }); + + it('setting heightReference does not raise definitionChanged event for same data', function() { + var heightReference = new ConstantProperty(HeightReference.NONE); + var property = new GeometryHeightProperty(undefined, heightReference); + + var listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + property.heightReference = heightReference; + expect(listener.calls.count()).toBe(0); + }); + + it('getValue works for for height reference NONE and RELATIVE_TO_GROUND', function() { + var expected = 30; + var height = new ConstantProperty(expected); + var heightReference = new ConstantProperty(HeightReference.NONE); + var property = new GeometryHeightProperty(height, heightReference); + expect(property.getValue(time)).toBe(expected); + + property.heightReference = new ConstantProperty(HeightReference.RELATIVE_TO_GROUND); + expect(property.getValue(time)).toBe(expected); + }); + + it('getValue works for for height reference CLAMP_TO_GROUND', function() { + var height = new ConstantProperty(50); + var heightReference = new ConstantProperty(HeightReference.CLAMP_TO_GROUND); + var property = new GeometryHeightProperty(height, heightReference); + expect(property.getValue(time)).toBe(0); + }); + + it('equals works', function() { + var height = new ConstantProperty(50); + var heightReference = new ConstantProperty(HeightReference.NONE); + + var left = new GeometryHeightProperty(); + var right = new GeometryHeightProperty(); + + expect(left.equals(undefined)).toBe(false); + expect(left.equals(right)).toBe(true); + + left.height = height; + expect(left.equals(right)).toBe(false); + + right.height = height; + expect(left.equals(right)).toBe(true); + + left.heightReference = heightReference; + expect(left.equals(right)).toBe(false); + + right.heightReference = heightReference; + expect(left.equals(right)).toBe(true); + }); + + it('getValue throws without time', function() { + var property = new GeometryHeightProperty(); + expect(function() { + property.getValue(); + }).toThrowDeveloperError(); + }); + + it('getMinimumTerrainValue return terrain value', function() { + var rectangle = Rectangle.fromDegrees(0, 0, 1, 1); + var expected = ApproximateTerrainHeights.getApproximateTerrainHeights(rectangle).minimumTerrainHeight; + expect(GeometryHeightProperty.getMinimumTerrainValue(rectangle)).toBe(expected); + }); + + it('getMinimumTerrainValue throws without rectangle', function() { + expect(function() { + return GeometryHeightProperty.getMinimumTerrainValue(); + }).toThrowDeveloperError(); + }); +}); diff --git a/Specs/DataSources/PolygonGeometryUpdaterSpec.js b/Specs/DataSources/PolygonGeometryUpdaterSpec.js index 1e87f87d4d5a..f9b13de57acb 100644 --- a/Specs/DataSources/PolygonGeometryUpdaterSpec.js +++ b/Specs/DataSources/PolygonGeometryUpdaterSpec.js @@ -1,7 +1,10 @@ defineSuite([ 'DataSources/PolygonGeometryUpdater', 'Core/Cartesian3', + 'Core/Ellipsoid', + 'Core/GeometryOffsetAttribute', 'Core/JulianDate', + 'Core/Math', 'Core/PolygonHierarchy', 'Core/TimeIntervalCollection', 'DataSources/ConstantProperty', @@ -20,7 +23,10 @@ defineSuite([ ], function( PolygonGeometryUpdater, Cartesian3, + Ellipsoid, + GeometryOffsetAttribute, JulianDate, + CesiumMath, PolygonHierarchy, TimeIntervalCollection, ConstantProperty, @@ -55,10 +61,10 @@ defineSuite([ function createBasicPolygon() { var polygon = new PolygonGraphics(); polygon.hierarchy = new ConstantProperty(new PolygonHierarchy(Cartesian3.fromRadiansArray([ - 0, 0, - 1, 0, + -1, -1, + 1, -1, 1, 1, - 0, 1 + -1, 1 ]))); polygon.height = new ConstantProperty(0); var entity = new Entity(); @@ -225,6 +231,7 @@ defineSuite([ expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); expect(geometry._closeTop).toEqual(options.closeTop); expect(geometry._closeBottom).toEqual(options.closeBottom); + expect(geometry._offsetAttribute).toBeUndefined(); instance = updater.createOutlineGeometryInstance(time); geometry = instance.geometry; @@ -232,6 +239,7 @@ defineSuite([ expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); expect(geometry._perPositionHeight).toEqual(options.perPositionHeight); + expect(geometry._offsetAttribute).toBeUndefined(); }); it('Checks that a polygon with per position heights isn\'t on terrain', function() { @@ -291,6 +299,7 @@ defineSuite([ expect(options.stRotation).toEqual(polygon.stRotation.getValue()); expect(options.closeTop).toEqual(polygon.closeTop.getValue()); expect(options.closeBottom).toEqual(polygon.closeBottom.getValue()); + expect(options.offsetAttribute).toBeUndefined(); }); it('geometryChanged event is raised when expected', function() { @@ -333,6 +342,14 @@ defineSuite([ expect(updater.onTerrain).toBe(false); }); + it('computes center', function() { + var entity = createBasicPolygon(); + var updater = new PolygonGeometryUpdater(entity, scene); + var result = updater._computeCenter(time); + result = Ellipsoid.WGS84.scaleToGeodeticSurface(result, result); + expect(result).toEqualEpsilon(Cartesian3.fromDegrees(0.0, 0.0), CesiumMath.EPSILON10); + }); + function getScene() { return scene; } diff --git a/Specs/DataSources/PolylineGeometryUpdaterSpec.js b/Specs/DataSources/PolylineGeometryUpdaterSpec.js index cc14250f3aef..67f2e43daff3 100644 --- a/Specs/DataSources/PolylineGeometryUpdaterSpec.js +++ b/Specs/DataSources/PolylineGeometryUpdaterSpec.js @@ -800,5 +800,7 @@ defineSuite([ var instance = updater.createFillGeometryInstance(time); expect(instance.geometry instanceof GroundPolylineGeometry).toBe(false); + + updater.destroy(); }); }, 'WebGL'); diff --git a/Specs/DataSources/RectangleGeometryUpdaterSpec.js b/Specs/DataSources/RectangleGeometryUpdaterSpec.js index 2608b931511f..5f14ca3741c2 100644 --- a/Specs/DataSources/RectangleGeometryUpdaterSpec.js +++ b/Specs/DataSources/RectangleGeometryUpdaterSpec.js @@ -1,7 +1,9 @@ defineSuite([ 'DataSources/RectangleGeometryUpdater', 'Core/Cartesian3', + 'Core/GeometryOffsetAttribute', 'Core/JulianDate', + 'Core/Math', 'Core/Rectangle', 'Core/TimeIntervalCollection', 'DataSources/ConstantProperty', @@ -17,7 +19,9 @@ defineSuite([ ], function( RectangleGeometryUpdater, Cartesian3, + GeometryOffsetAttribute, JulianDate, + CesiumMath, Rectangle, TimeIntervalCollection, ConstantProperty, @@ -48,7 +52,7 @@ defineSuite([ var rectangle = new RectangleGraphics(); var entity = new Entity(); entity.rectangle = rectangle; - entity.rectangle.coordinates = new ConstantProperty(new Rectangle(0, 0, 1, 1)); + entity.rectangle.coordinates = new ConstantProperty(new Rectangle(-1, -1, 1, 1)); entity.rectangle.height = new ConstantProperty(0); return entity; } @@ -161,12 +165,14 @@ defineSuite([ expect(geometry._surfaceHeight).toEqual(options.height); expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); + expect(geometry._offsetAttribute).toBeUndefined(); instance = updater.createOutlineGeometryInstance(time); geometry = instance.geometry; expect(geometry._surfaceHeight).toEqual(options.height); expect(geometry._granularity).toEqual(options.granularity); expect(geometry._extrudedHeight).toEqual(options.extrudedHeight); + expect(geometry._offsetAttribute).toBeUndefined(); }); it('dynamic updater sets properties', function() { @@ -194,6 +200,7 @@ defineSuite([ expect(options.extrudedHeight).toEqual(rectangle.extrudedHeight.getValue()); expect(options.granularity).toEqual(rectangle.granularity.getValue()); expect(options.stRotation).toEqual(rectangle.stRotation.getValue()); + expect(options.offsetAttribute).toBeUndefined(); }); it('geometryChanged event is raised when expected', function() { @@ -224,6 +231,13 @@ defineSuite([ expect(listener.calls.count()).toEqual(3); }); + it('computes center', function() { + var entity = createBasicRectangle(); + var updater = new RectangleGeometryUpdater(entity, scene); + + expect(updater._computeCenter(time)).toEqualEpsilon(Cartesian3.fromDegrees(0.0, 0.0), CesiumMath.EPSILON10); + }); + function getScene() { return scene; } diff --git a/Specs/DataSources/TerrainOffsetPropertySpec.js b/Specs/DataSources/TerrainOffsetPropertySpec.js new file mode 100644 index 000000000000..d565db1d6e27 --- /dev/null +++ b/Specs/DataSources/TerrainOffsetPropertySpec.js @@ -0,0 +1,77 @@ +defineSuite([ + 'DataSources/TerrainOffsetProperty', + 'Core/Cartesian3', + 'Core/Event', + 'Core/JulianDate', + 'Core/Rectangle', + 'Scene/HeightReference', + 'DataSources/ConstantProperty', + 'Specs/createGlobe', + 'Specs/createScene' +], function( + TerrainOffsetProperty, + Cartesian3, + Event, + JulianDate, + Rectangle, + HeightReference, + ConstantProperty, + createGlobe, + createScene) { + 'use strict'; + + var scene; + var time = JulianDate.now(); + beforeAll(function() { + scene = createScene(); + scene.globe = createGlobe(); + }); + + afterAll(function() { + scene.destroyForSpecs(); + }); + + it('can construct and destroy', function() { + var getPosition = jasmine.createSpy(); + var height = new ConstantProperty(30); + var extrudedHeight = new ConstantProperty(0); + var property = new TerrainOffsetProperty(scene, height, extrudedHeight, getPosition); + expect(property.isConstant).toBe(false); + expect(property.getValue(time)).toEqual(Cartesian3.ZERO); + property.destroy(); + expect(property.isDestroyed()).toBe(true); + }); + + it('throws without scene', function() { + var getPosition = jasmine.createSpy(); + var height = new ConstantProperty(30); + var extrudedHeight = new ConstantProperty(0); + expect(function() { + return new TerrainOffsetProperty(undefined, height, extrudedHeight, getPosition); + }).toThrowDeveloperError(); + }); + + it('throws without height', function() { + var getPosition = jasmine.createSpy(); + var extrudedHeight = new ConstantProperty(0); + expect(function() { + return new TerrainOffsetProperty(scene, undefined, extrudedHeight, getPosition); + }).toThrowDeveloperError(); + }); + + it('throws without extrudedHeight', function() { + var getPosition = jasmine.createSpy(); + var height = new ConstantProperty(30); + expect(function() { + return new TerrainOffsetProperty(scene, height, undefined, getPosition); + }).toThrowDeveloperError(); + }); + + it('throws without getPosition', function() { + var height = new ConstantProperty(30); + var extrudedHeight = new ConstantProperty(0); + expect(function() { + return new TerrainOffsetProperty(scene, height, extrudedHeight, undefined); + }).toThrowDeveloperError(); + }); +}); diff --git a/Specs/createGeometryUpdaterGroundGeometrySpecs.js b/Specs/createGeometryUpdaterGroundGeometrySpecs.js index b0ed38ab479c..9334437e320b 100644 --- a/Specs/createGeometryUpdaterGroundGeometrySpecs.js +++ b/Specs/createGeometryUpdaterGroundGeometrySpecs.js @@ -1,20 +1,26 @@ define([ 'Core/Color', + 'Core/GeometryOffsetAttribute', 'Core/JulianDate', 'DataSources/ColorMaterialProperty', 'DataSources/ConstantProperty', + 'DataSources/GeometryHeightProperty', 'DataSources/SampledProperty', 'Scene/ClassificationType', 'Scene/GroundPrimitive', + 'Scene/HeightReference', 'Scene/PrimitiveCollection' ], function( Color, + GeometryOffsetAttribute, JulianDate, ColorMaterialProperty, ConstantProperty, + GeometryHeightProperty, SampledProperty, ClassificationType, GroundPrimitive, + HeightReference, PrimitiveCollection) { 'use strict'; @@ -119,6 +125,108 @@ define([ expect(updater.onTerrain).toBe(false); }); + it('Creates geometry with no offsetAttribute when geometry is on terrain', function() { + var entity = createEntity(); + + var updater = new Updater(entity, getScene()); + + var instance; + var geometry; + instance = updater.createFillGeometryInstance(time); + geometry = instance.geometry; + expect(geometry._offsetAttribute).toBeUndefined(); + }); + + it('Creates geometry with expected offsetAttribute based on height and extrudedHeight', function() { + var entity = createEntity(); + var graphics = entity[geometryPropertyName]; + graphics.outline = true; + graphics.outlineColor = Color.BLACK; + var updater = new Updater(entity, getScene()); + + var instance; + + graphics.height = new ConstantProperty(20.0); + graphics.extrudedHeight = new ConstantProperty(0.0); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toBeUndefined(); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toBeUndefined(); + + graphics.height = new GeometryHeightProperty(20.0, HeightReference.NONE); + graphics.extrudedHeight = new GeometryHeightProperty(0.0, HeightReference.NONE); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toBeUndefined(); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toBeUndefined(); + + graphics.height = new GeometryHeightProperty(20.0, HeightReference.NONE); + graphics.extrudedHeight = new GeometryHeightProperty(0.0, HeightReference.CLAMP_TO_GROUND); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toBeUndefined(); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toBeUndefined(); + + graphics.height = new GeometryHeightProperty(20.0, HeightReference.NONE); + graphics.extrudedHeight = new GeometryHeightProperty(0.0, HeightReference.RELATIVE_TO_GROUND); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + + graphics.height = new GeometryHeightProperty(20.0, HeightReference.CLAMP_TO_GROUND); + graphics.extrudedHeight = new GeometryHeightProperty(0.0, HeightReference.NONE); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + + graphics.height = new GeometryHeightProperty(20.0, HeightReference.CLAMP_TO_GROUND); + graphics.extrudedHeight = new GeometryHeightProperty(0.0, HeightReference.CLAMP_TO_GROUND); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + + graphics.height = new GeometryHeightProperty(20.0, HeightReference.CLAMP_TO_GROUND); + graphics.extrudedHeight = new GeometryHeightProperty(0.0, HeightReference.RELATIVE_TO_GROUND); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.ALL); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.ALL); + + graphics.height = new GeometryHeightProperty(20.0, HeightReference.RELATIVE_TO_GROUND); + graphics.extrudedHeight = new GeometryHeightProperty(0.0, HeightReference.NONE); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + + graphics.height = new GeometryHeightProperty(20.0, HeightReference.RELATIVE_TO_GROUND); + graphics.extrudedHeight = new GeometryHeightProperty(0.0, HeightReference.CLAMP_TO_GROUND); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.TOP); + + graphics.height = new GeometryHeightProperty(20.0, HeightReference.RELATIVE_TO_GROUND); + graphics.extrudedHeight = new GeometryHeightProperty(0.0, HeightReference.RELATIVE_TO_GROUND); + updater._onEntityPropertyChanged(entity, geometryPropertyName); + instance = updater.createFillGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.ALL); + instance = updater.createOutlineGeometryInstance(time); + expect(instance.geometry._offsetAttribute).toEqual(GeometryOffsetAttribute.ALL); + }); + it('color material sets onTerrain to true', function() { var entity = createEntity(); var geometry = entity[geometryPropertyName];