import arraySlice from '../Core/arraySlice.js'; import Cartesian2 from '../Core/Cartesian2.js'; import Cartesian3 from '../Core/Cartesian3.js'; import Color from '../Core/Color.js'; import defined from '../Core/defined.js'; import defineProperties from '../Core/defineProperties.js'; import destroyObject from '../Core/destroyObject.js'; import DistanceDisplayCondition from '../Core/DistanceDisplayCondition.js'; import Ellipsoid from '../Core/Ellipsoid.js'; import NearFarScalar from '../Core/NearFarScalar.js'; import Rectangle from '../Core/Rectangle.js'; import TaskProcessor from '../Core/TaskProcessor.js'; import when from '../ThirdParty/when.js'; import BillboardCollection from './BillboardCollection.js'; import Cesium3DTilePointFeature from './Cesium3DTilePointFeature.js'; import HorizontalOrigin from './HorizontalOrigin.js'; import LabelCollection from './LabelCollection.js'; import LabelStyle from './LabelStyle.js'; import PolylineCollection from './PolylineCollection.js'; import VerticalOrigin from './VerticalOrigin.js'; /** * Creates a batch of points or billboards and labels. * * @alias Vector3DTilePoints * @constructor * * @param {Object} options An object with following properties: * @param {Uint16Array} options.positions The positions of the polygons. * @param {Number} options.minimumHeight The minimum height of the terrain covered by the tile. * @param {Number} options.maximumHeight The maximum height of the terrain covered by the tile. * @param {Rectangle} options.rectangle The rectangle containing the tile. * @param {Cesium3DTileBatchTable} options.batchTable The batch table for the tile containing the batched polygons. * @param {Uint16Array} options.batchIds The batch ids for each polygon. * * @private */ function Vector3DTilePoints(options) { // released after the first update this._positions = options.positions; this._batchTable = options.batchTable; this._batchIds = options.batchIds; this._rectangle = options.rectangle; this._minHeight = options.minimumHeight; this._maxHeight = options.maximumHeight; this._billboardCollection = undefined; this._labelCollection = undefined; this._polylineCollection = undefined; this._verticesPromise = undefined; this._packedBuffer = undefined; this._ready = false; this._readyPromise = when.defer(); this._resolvedPromise = false; } defineProperties(Vector3DTilePoints.prototype, { /** * Gets the number of points. * * @memberof Vector3DTilePoints.prototype * * @type {Number} * @readonly */ pointsLength : { get : function() { return this._billboardCollection.length; } }, /** * Gets the texture atlas memory in bytes. * * @memberof Vector3DTilePoints.prototype * * @type {Number} * @readonly */ texturesByteLength : { get : function() { var billboardSize = this._billboardCollection.textureAtlas.texture.sizeInBytes; var labelSize = this._labelCollection._textureAtlas.texture.sizeInBytes; return billboardSize + labelSize; } }, /** * Gets a promise that resolves when the primitive is ready to render. * @memberof Vector3DTilePoints.prototype * @type {Promise} * @readonly */ readyPromise : { get : function() { return this._readyPromise.promise; } } }); function packBuffer(points, ellipsoid) { var rectangle = points._rectangle; var minimumHeight = points._minHeight; var maximumHeight = points._maxHeight; var packedLength = 2 + Rectangle.packedLength + Ellipsoid.packedLength; var packedBuffer = new Float64Array(packedLength); var offset = 0; packedBuffer[offset++] = minimumHeight; packedBuffer[offset++] = maximumHeight; Rectangle.pack(rectangle, packedBuffer, offset); offset += Rectangle.packedLength; Ellipsoid.pack(ellipsoid, packedBuffer, offset); return packedBuffer; } var createVerticesTaskProcessor = new TaskProcessor('createVectorTilePoints'); var scratchPosition = new Cartesian3(); function createPoints(points, ellipsoid) { if (defined(points._billboardCollection)) { return; } var positions; if (!defined(points._verticesPromise)) { positions = points._positions; var packedBuffer = points._packedBuffer; if (!defined(packedBuffer)) { // Copy because they may be the views on the same buffer. positions = points._positions = arraySlice(positions); points._batchIds = arraySlice(points._batchIds); packedBuffer = points._packedBuffer = packBuffer(points, ellipsoid); } var transferrableObjects = [positions.buffer, packedBuffer.buffer]; var parameters = { positions : positions.buffer, packedBuffer : packedBuffer.buffer }; var verticesPromise = points._verticesPromise = createVerticesTaskProcessor.scheduleTask(parameters, transferrableObjects); if (!defined(verticesPromise)) { // Postponed return; } verticesPromise.then(function(result) { points._positions = new Float64Array(result.positions); points._ready = true; }); } if (points._ready && !defined(points._billboardCollection)) { positions = points._positions; var batchTable = points._batchTable; var batchIds = points._batchIds; var billboardCollection = points._billboardCollection = new BillboardCollection({batchTable : batchTable}); var labelCollection = points._labelCollection = new LabelCollection({batchTable : batchTable}); var polylineCollection = points._polylineCollection = new PolylineCollection(); polylineCollection._useHighlightColor = true; var numberOfPoints = positions.length / 3; for (var i = 0; i < numberOfPoints; ++i) { var id = batchIds[i]; var position = Cartesian3.unpack(positions, i * 3, scratchPosition); var b = billboardCollection.add(); b.position = position; b._batchIndex = id; var l = labelCollection.add(); l.text = ' '; l.position = position; l._batchIndex = id; var p = polylineCollection.add(); p.positions = [Cartesian3.clone(position), Cartesian3.clone(position)]; } points._positions = undefined; points._packedBuffer = undefined; } } /** * Creates features for each point and places it at the batch id index of features. * * @param {Vector3DTileContent} content The vector tile content. * @param {Cesium3DTileFeature[]} features An array of features where the point features will be placed. */ Vector3DTilePoints.prototype.createFeatures = function(content, features) { var billboardCollection = this._billboardCollection; var labelCollection = this._labelCollection; var polylineCollection = this._polylineCollection; var batchIds = this._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; var billboard = billboardCollection.get(i); var label = labelCollection.get(i); var polyline = polylineCollection.get(i); features[batchId] = new Cesium3DTilePointFeature(content, batchId, billboard, label, polyline); } }; /** * Colors the entire tile when enabled is true. The resulting color will be (batch table color * color). * * @param {Boolean} enabled Whether to enable debug coloring. * @param {Color} color The debug color. */ Vector3DTilePoints.prototype.applyDebugSettings = function(enabled, color) { if (enabled) { Color.clone(color, this._billboardCollection._highlightColor); Color.clone(color, this._labelCollection._highlightColor); Color.clone(color, this._polylineCollection._highlightColor); } else { Color.clone(Color.WHITE, this._billboardCollection._highlightColor); Color.clone(Color.WHITE, this._labelCollection._highlightColor); Color.clone(Color.WHITE, this._polylineCollection._highlightColor); } }; function clearStyle(polygons, features) { var batchIds = polygons._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; var feature = features[batchId]; feature.show = true; feature.pointSize = Cesium3DTilePointFeature.defaultPointSize; feature.color = Cesium3DTilePointFeature.defaultColor; feature.pointOutlineColor = Cesium3DTilePointFeature.defaultPointOutlineColor; feature.pointOutlineWidth = Cesium3DTilePointFeature.defaultPointOutlineWidth; feature.labelColor = Color.WHITE; feature.labelOutlineColor = Color.WHITE; feature.labelOutlineWidth = 1.0; feature.font = '30px sans-serif'; feature.labelStyle = LabelStyle.FILL; feature.labelText = undefined; feature.backgroundColor = new Color(0.165, 0.165, 0.165, 0.8); feature.backgroundPadding = new Cartesian2(7, 5); feature.backgroundEnabled = false; feature.scaleByDistance = undefined; feature.translucencyByDistance = undefined; feature.distanceDisplayCondition = undefined; feature.heightOffset = 0.0; feature.anchorLineEnabled = false; feature.anchorLineColor = Color.WHITE; feature.image = undefined; feature.disableDepthTestDistance = 0.0; feature.horizontalOrigin = HorizontalOrigin.CENTER; feature.verticalOrigin = VerticalOrigin.CENTER; feature.labelHorizontalOrigin = HorizontalOrigin.RIGHT; feature.labelVerticalOrigin = VerticalOrigin.BASELINE; } } var scratchColor = new Color(); var scratchColor2 = new Color(); var scratchColor3 = new Color(); var scratchColor4 = new Color(); var scratchColor5 = new Color(); var scratchColor6 = new Color(); var scratchScaleByDistance = new NearFarScalar(); var scratchTranslucencyByDistance = new NearFarScalar(); var scratchDistanceDisplayCondition = new DistanceDisplayCondition(); /** * Apply a style to the content. * * @param {Cesium3DTileStyle} style The style. * @param {Cesium3DTileFeature[]} features The array of features. */ Vector3DTilePoints.prototype.applyStyle = function(style, features) { if (!defined(style)) { clearStyle(this, features); return; } var batchIds = this._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; var feature = features[batchId]; if (defined(style.show)) { feature.show = style.show.evaluate(feature); } if (defined(style.pointSize)) { feature.pointSize = style.pointSize.evaluate(feature); } if (defined(style.color)) { feature.color = style.color.evaluateColor(feature, scratchColor); } if (defined(style.pointOutlineColor)) { feature.pointOutlineColor = style.pointOutlineColor.evaluateColor(feature, scratchColor2); } if (defined(style.pointOutlineWidth)) { feature.pointOutlineWidth = style.pointOutlineWidth.evaluate(feature); } if (defined(style.labelColor)) { feature.labelColor = style.labelColor.evaluateColor(feature, scratchColor3); } if (defined(style.labelOutlineColor)) { feature.labelOutlineColor = style.labelOutlineColor.evaluateColor(feature, scratchColor4); } if (defined(style.labelOutlineWidth)) { feature.labelOutlineWidth = style.labelOutlineWidth.evaluate(feature); } if (defined(style.font)) { feature.font = style.font.evaluate(feature); } if (defined(style.labelStyle)) { feature.labelStyle = style.labelStyle.evaluate(feature); } if (defined(style.labelText)) { feature.labelText = style.labelText.evaluate(feature); } else { feature.labelText = undefined; } if (defined(style.backgroundColor)) { feature.backgroundColor = style.backgroundColor.evaluateColor(feature, scratchColor5); } if (defined(style.backgroundPadding)) { feature.backgroundPadding = style.backgroundPadding.evaluate(feature); } if (defined(style.backgroundEnabled)) { feature.backgroundEnabled = style.backgroundEnabled.evaluate(feature); } if (defined(style.scaleByDistance)) { var scaleByDistanceCart4 = style.scaleByDistance.evaluate(feature); scratchScaleByDistance.near = scaleByDistanceCart4.x; scratchScaleByDistance.nearValue = scaleByDistanceCart4.y; scratchScaleByDistance.far = scaleByDistanceCart4.z; scratchScaleByDistance.farValue = scaleByDistanceCart4.w; feature.scaleByDistance = scratchScaleByDistance; } else { feature.scaleByDistance = undefined; } if (defined(style.translucencyByDistance)) { var translucencyByDistanceCart4 = style.translucencyByDistance.evaluate(feature); scratchTranslucencyByDistance.near = translucencyByDistanceCart4.x; scratchTranslucencyByDistance.nearValue = translucencyByDistanceCart4.y; scratchTranslucencyByDistance.far = translucencyByDistanceCart4.z; scratchTranslucencyByDistance.farValue = translucencyByDistanceCart4.w; feature.translucencyByDistance = scratchTranslucencyByDistance; } else { feature.translucencyByDistance = undefined; } if (defined(style.distanceDisplayCondition)) { var distanceDisplayConditionCart2 = style.distanceDisplayCondition.evaluate(feature); scratchDistanceDisplayCondition.near = distanceDisplayConditionCart2.x; scratchDistanceDisplayCondition.far = distanceDisplayConditionCart2.y; feature.distanceDisplayCondition = scratchDistanceDisplayCondition; } else { feature.distanceDisplayCondition = undefined; } if (defined(style.heightOffset)) { feature.heightOffset = style.heightOffset.evaluate(feature); } if (defined(style.anchorLineEnabled)) { feature.anchorLineEnabled = style.anchorLineEnabled.evaluate(feature); } if (defined(style.anchorLineColor)) { feature.anchorLineColor = style.anchorLineColor.evaluateColor(feature, scratchColor6); } if (defined(style.image)) { feature.image = style.image.evaluate(feature); } else { feature.image = undefined; } if (defined(style.disableDepthTestDistance)) { feature.disableDepthTestDistance = style.disableDepthTestDistance.evaluate(feature); } if (defined(style.horizontalOrigin)) { feature.horizontalOrigin = style.horizontalOrigin.evaluate(feature); } if (defined(style.verticalOrigin)) { feature.verticalOrigin = style.verticalOrigin.evaluate(feature); } if (defined(style.labelHorizontalOrigin)) { feature.labelHorizontalOrigin = style.labelHorizontalOrigin.evaluate(feature); } if (defined(style.labelVerticalOrigin)) { feature.labelVerticalOrigin = style.labelVerticalOrigin.evaluate(feature); } } }; /** * @private */ Vector3DTilePoints.prototype.update = function(frameState) { createPoints(this, frameState.mapProjection.ellipsoid); if (!this._ready) { return; } this._polylineCollection.update(frameState); this._billboardCollection.update(frameState); this._labelCollection.update(frameState); if (!this._resolvedPromise) { this._readyPromise.resolve(); this._resolvedPromise = true; } }; /** * Returns true if this object was destroyed; otherwise, false. *
* If this object was destroyed, it should not be used; calling any function other than
* isDestroyed
will result in a {@link DeveloperError} exception.
*
true
if this object was destroyed; otherwise, false
.
*/
Vector3DTilePoints.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.
*
* Once an object is destroyed, it should not be used; calling any function other than
* isDestroyed
will result in a {@link DeveloperError} exception. Therefore,
* assign the return value (undefined
) to the object as done in the example.
*