/*global define*/
define([
'../Core/BoundingSphere',
'../Core/Cartesian3',
'../Core/Color',
'../Core/defaultValue',
'../Core/defined',
'../Core/defineProperties',
'../Core/DeveloperError',
'../Core/Matrix4',
'../Core/PolylinePipeline',
'./Material'
], function(
BoundingSphere,
Cartesian3,
Color,
defaultValue,
defined,
defineProperties,
DeveloperError,
Matrix4,
PolylinePipeline,
Material) {
"use strict";
/**
* A renderable polyline. Create this by calling {@link PolylineCollection#add}
*
* @alias Polyline
* @internalConstructor
*
* @param {Object} [options] Object with the following properties:
* @param {Boolean} [options.show=true] true
if this polyline will be shown; otherwise, false
.
* @param {Number} [options.width=1.0] The width of the polyline in pixels.
* @param {Boolean} [options.loop=false] Whether a line segment will be added between the last and first line positions to make this line a loop.
* @param {Material} [options.material=Material.ColorType] The material.
* @param {Cartesian3[]} [options.positions] The positions.
* @param {Object} [options.id] The user-defined object to be returned when this polyline is picked.
*
* @see PolylineCollection
*
* @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Polylines.html|Cesium Sandcastle Polyline Demo}
*/
var Polyline = function(options, polylineCollection) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
this._show = defaultValue(options.show, true);
this._width = defaultValue(options.width, 1.0);
this._loop = defaultValue(options.loop, false);
this._material = options.material;
if (!defined(this._material)) {
this._material = Material.fromType(Material.ColorType, {
color : new Color(1.0, 1.0, 1.0, 1.0)
});
}
var positions = options.positions;
if (!defined(positions)) {
positions = [];
}
this._positions = positions;
this._actualPositions = PolylinePipeline.removeDuplicates(positions);
if (!defined(this._actualPositions)) {
this._actualPositions = positions;
}
if (this._loop && this._actualPositions.length > 2) {
if (this._actualPositions === this._positions) {
this._actualPositions = positions.slice();
}
this._actualPositions.push(Cartesian3.clone(this._actualPositions[0]));
}
this._length = this._actualPositions.length;
this._id = options.id;
var modelMatrix;
if (defined(polylineCollection)) {
modelMatrix = Matrix4.clone(polylineCollection.modelMatrix);
}
this._modelMatrix = modelMatrix;
this._segments = PolylinePipeline.wrapLongitude(this._actualPositions, modelMatrix);
this._actualLength = undefined;
this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES);
this._polylineCollection = polylineCollection;
this._dirty = false;
this._pickId = undefined;
this._boundingVolume = BoundingSphere.fromPoints(this._actualPositions);
this._boundingVolumeWC = BoundingSphere.transform(this._boundingVolume, this._modelMatrix);
this._boundingVolume2D = new BoundingSphere(); // modified in PolylineCollection
};
var SHOW_INDEX = Polyline.SHOW_INDEX = 0;
var WIDTH_INDEX = Polyline.WIDTH_INDEX = 1;
var POSITION_INDEX = Polyline.POSITION_INDEX = 2;
var MATERIAL_INDEX = Polyline.MATERIAL_INDEX = 3;
var POSITION_SIZE_INDEX = Polyline.POSITION_SIZE_INDEX = 4;
var NUMBER_OF_PROPERTIES = Polyline.NUMBER_OF_PROPERTIES = 5;
function makeDirty(polyline, propertyChanged) {
++polyline._propertiesChanged[propertyChanged];
var polylineCollection = polyline._polylineCollection;
if (defined(polylineCollection)) {
polylineCollection._updatePolyline(polyline, propertyChanged);
polyline._dirty = true;
}
}
defineProperties(Polyline.prototype, {
/**
* Determines if this polyline will be shown. Use this to hide or show a polyline, instead
* of removing it and re-adding it to the collection.
* @memberof Polyline.prototype
* @type {Boolean}
*/
show: {
get: function() {
return this._show;
},
set: function(value) {
//>>includeStart('debug', pragmas.debug);
if (!defined(value)) {
throw new DeveloperError('value is required.');
}
//>>includeEnd('debug');
if (value !== this._show) {
this._show = value;
makeDirty(this, SHOW_INDEX);
}
}
},
/**
* Gets or sets the positions of the polyline.
* @memberof Polyline.prototype
* @type {Cartesian3[]}
* @example
* polyline.positions = Cesium.Cartesian3.fromDegreesArray([
* 0.0, 0.0,
* 10.0, 0.0,
* 0.0, 20.0
* ]);
*/
positions : {
get: function() {
return this._positions;
},
set: function(value) {
//>>includeStart('debug', pragmas.debug);
if (!defined(value)) {
throw new DeveloperError('value is required.');
}
//>>includeEnd('debug');
var positions = PolylinePipeline.removeDuplicates(value);
if (!defined(positions)) {
positions = value;
}
if (this._loop && positions.length > 2) {
if (positions === value) {
positions = value.slice();
}
positions.push(Cartesian3.clone(positions[0]));
}
if (this._actualPositions.length !== positions.length || this._actualPositions.length !== this._length) {
makeDirty(this, POSITION_SIZE_INDEX);
}
this._positions = value;
this._actualPositions = positions;
this._length = positions.length;
this._boundingVolume = BoundingSphere.fromPoints(this._actualPositions, this._boundingVolume);
this._boundingVolumeWC = BoundingSphere.transform(this._boundingVolume, this._modelMatrix, this._boundingVolumeWC);
makeDirty(this, POSITION_INDEX);
this.update();
}
},
/**
* Gets or sets the surface appearance of the polyline. This can be one of several built-in {@link Material} objects or a custom material, scripted with
* {@link https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric|Fabric}.
* @memberof Polyline.prototype
* @type {Material}
*/
material: {
get: function() {
return this._material;
},
set: function(material) {
//>>includeStart('debug', pragmas.debug);
if (!defined(material)) {
throw new DeveloperError('material is required.');
}
//>>includeEnd('debug');
if (this._material !== material) {
this._material = material;
makeDirty(this, MATERIAL_INDEX);
}
}
},
/**
* Gets or sets the width of the polyline.
* @memberof Polyline.prototype
* @type {Number}
*/
width: {
get: function() {
return this._width;
},
set: function(value) {
//>>includeStart('debug', pragmas.debug)
if (!defined(value)) {
throw new DeveloperError('value is required.');
}
//>>includeEnd('debug');
var width = this._width;
if (value !== width) {
this._width = value;
makeDirty(this, WIDTH_INDEX);
}
}
},
/**
* Gets or sets whether a line segment will be added between the first and last polyline positions.
* @memberof Polyline.prototype
* @type {Boolean}
*/
loop: {
get: function() {
return this._loop;
},
set: function(value) {
//>>includeStart('debug', pragmas.debug)
if (!defined(value)) {
throw new DeveloperError('value is required.');
}
//>>includeEnd('debug');
if (value !== this._loop) {
var positions = this._actualPositions;
if (value) {
if (positions.length > 2 && !Cartesian3.equals(positions[0], positions[positions.length - 1])) {
if (positions.length === this._positions.length) {
this._actualPositions = positions = this._positions.slice();
}
positions.push(Cartesian3.clone(positions[0]));
}
} else {
if (positions.length > 2 && Cartesian3.equals(positions[0], positions[positions.length - 1])) {
if (positions.length - 1 === this._positions.length) {
this._actualPositions = this._positions;
} else {
positions.pop();
}
}
}
this._loop = value;
makeDirty(this, POSITION_SIZE_INDEX);
}
}
},
/**
* Gets or sets the user-defined object returned when the polyline is picked.
* @memberof Polyline.prototype
* @type {Object}
*/
id : {
get : function() {
return this._id;
},
set : function(value) {
this._id = value;
if (defined(this._pickId)) {
this._pickId.object.id = value;
}
}
}
});
/**
* @private
*/
Polyline.prototype.update = function() {
var modelMatrix = Matrix4.IDENTITY;
if (defined(this._polylineCollection)) {
modelMatrix = this._polylineCollection.modelMatrix;
}
var segmentPositionsLength = this._segments.positions.length;
var segmentLengths = this._segments.lengths;
var positionsChanged = this._propertiesChanged[POSITION_INDEX] > 0 || this._propertiesChanged[POSITION_SIZE_INDEX] > 0;
if (!Matrix4.equals(modelMatrix, this._modelMatrix) || positionsChanged) {
this._segments = PolylinePipeline.wrapLongitude(this._actualPositions, modelMatrix);
this._boundingVolumeWC = BoundingSphere.transform(this._boundingVolume, modelMatrix, this._boundingVolumeWC);
}
this._modelMatrix = modelMatrix;
if (this._segments.positions.length !== segmentPositionsLength) {
// number of positions changed
makeDirty(this, POSITION_SIZE_INDEX);
} else {
var length = segmentLengths.length;
for (var i = 0; i < length; ++i) {
if (segmentLengths[i] !== this._segments.lengths[i]) {
// indices changed
makeDirty(this, POSITION_SIZE_INDEX);
break;
}
}
}
};
/**
* @private
*/
Polyline.prototype.getPickId = function(context) {
if (!defined(this._pickId)) {
this._pickId = context.createPickId({
primitive : this,
collection : this._polylineCollection,
id : this._id
});
}
return this._pickId;
};
Polyline.prototype._clean = function() {
this._dirty = false;
var properties = this._propertiesChanged;
for ( var k = 0; k < NUMBER_OF_PROPERTIES - 1; ++k) {
properties[k] = 0;
}
};
Polyline.prototype._destroy = function() {
this._pickId = this._pickId && this._pickId.destroy();
this._material = this._material && this._material.destroy();
this._polylineCollection = undefined;
};
return Polyline;
});