Polyline.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. /*global define*/
  2. define([
  3. '../Core/BoundingSphere',
  4. '../Core/Cartesian3',
  5. '../Core/Color',
  6. '../Core/defaultValue',
  7. '../Core/defined',
  8. '../Core/defineProperties',
  9. '../Core/DeveloperError',
  10. '../Core/Matrix4',
  11. '../Core/PolylinePipeline',
  12. './Material'
  13. ], function(
  14. BoundingSphere,
  15. Cartesian3,
  16. Color,
  17. defaultValue,
  18. defined,
  19. defineProperties,
  20. DeveloperError,
  21. Matrix4,
  22. PolylinePipeline,
  23. Material) {
  24. "use strict";
  25. /**
  26. * A renderable polyline. Create this by calling {@link PolylineCollection#add}
  27. *
  28. * @alias Polyline
  29. * @internalConstructor
  30. *
  31. * @param {Object} [options] Object with the following properties:
  32. * @param {Boolean} [options.show=true] <code>true</code> if this polyline will be shown; otherwise, <code>false</code>.
  33. * @param {Number} [options.width=1.0] The width of the polyline in pixels.
  34. * @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.
  35. * @param {Material} [options.material=Material.ColorType] The material.
  36. * @param {Cartesian3[]} [options.positions] The positions.
  37. * @param {Object} [options.id] The user-defined object to be returned when this polyline is picked.
  38. *
  39. * @see PolylineCollection
  40. *
  41. * @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Polylines.html|Cesium Sandcastle Polyline Demo}
  42. */
  43. var Polyline = function(options, polylineCollection) {
  44. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  45. this._show = defaultValue(options.show, true);
  46. this._width = defaultValue(options.width, 1.0);
  47. this._loop = defaultValue(options.loop, false);
  48. this._material = options.material;
  49. if (!defined(this._material)) {
  50. this._material = Material.fromType(Material.ColorType, {
  51. color : new Color(1.0, 1.0, 1.0, 1.0)
  52. });
  53. }
  54. var positions = options.positions;
  55. if (!defined(positions)) {
  56. positions = [];
  57. }
  58. this._positions = positions;
  59. this._actualPositions = PolylinePipeline.removeDuplicates(positions);
  60. if (!defined(this._actualPositions)) {
  61. this._actualPositions = positions;
  62. }
  63. if (this._loop && this._actualPositions.length > 2) {
  64. if (this._actualPositions === this._positions) {
  65. this._actualPositions = positions.slice();
  66. }
  67. this._actualPositions.push(Cartesian3.clone(this._actualPositions[0]));
  68. }
  69. this._length = this._actualPositions.length;
  70. this._id = options.id;
  71. var modelMatrix;
  72. if (defined(polylineCollection)) {
  73. modelMatrix = Matrix4.clone(polylineCollection.modelMatrix);
  74. }
  75. this._modelMatrix = modelMatrix;
  76. this._segments = PolylinePipeline.wrapLongitude(this._actualPositions, modelMatrix);
  77. this._actualLength = undefined;
  78. this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES);
  79. this._polylineCollection = polylineCollection;
  80. this._dirty = false;
  81. this._pickId = undefined;
  82. this._boundingVolume = BoundingSphere.fromPoints(this._actualPositions);
  83. this._boundingVolumeWC = BoundingSphere.transform(this._boundingVolume, this._modelMatrix);
  84. this._boundingVolume2D = new BoundingSphere(); // modified in PolylineCollection
  85. };
  86. var SHOW_INDEX = Polyline.SHOW_INDEX = 0;
  87. var WIDTH_INDEX = Polyline.WIDTH_INDEX = 1;
  88. var POSITION_INDEX = Polyline.POSITION_INDEX = 2;
  89. var MATERIAL_INDEX = Polyline.MATERIAL_INDEX = 3;
  90. var POSITION_SIZE_INDEX = Polyline.POSITION_SIZE_INDEX = 4;
  91. var NUMBER_OF_PROPERTIES = Polyline.NUMBER_OF_PROPERTIES = 5;
  92. function makeDirty(polyline, propertyChanged) {
  93. ++polyline._propertiesChanged[propertyChanged];
  94. var polylineCollection = polyline._polylineCollection;
  95. if (defined(polylineCollection)) {
  96. polylineCollection._updatePolyline(polyline, propertyChanged);
  97. polyline._dirty = true;
  98. }
  99. }
  100. defineProperties(Polyline.prototype, {
  101. /**
  102. * Determines if this polyline will be shown. Use this to hide or show a polyline, instead
  103. * of removing it and re-adding it to the collection.
  104. * @memberof Polyline.prototype
  105. * @type {Boolean}
  106. */
  107. show: {
  108. get: function() {
  109. return this._show;
  110. },
  111. set: function(value) {
  112. //>>includeStart('debug', pragmas.debug);
  113. if (!defined(value)) {
  114. throw new DeveloperError('value is required.');
  115. }
  116. //>>includeEnd('debug');
  117. if (value !== this._show) {
  118. this._show = value;
  119. makeDirty(this, SHOW_INDEX);
  120. }
  121. }
  122. },
  123. /**
  124. * Gets or sets the positions of the polyline.
  125. * @memberof Polyline.prototype
  126. * @type {Cartesian3[]}
  127. * @example
  128. * polyline.positions = Cesium.Cartesian3.fromDegreesArray([
  129. * 0.0, 0.0,
  130. * 10.0, 0.0,
  131. * 0.0, 20.0
  132. * ]);
  133. */
  134. positions : {
  135. get: function() {
  136. return this._positions;
  137. },
  138. set: function(value) {
  139. //>>includeStart('debug', pragmas.debug);
  140. if (!defined(value)) {
  141. throw new DeveloperError('value is required.');
  142. }
  143. //>>includeEnd('debug');
  144. var positions = PolylinePipeline.removeDuplicates(value);
  145. if (!defined(positions)) {
  146. positions = value;
  147. }
  148. if (this._loop && positions.length > 2) {
  149. if (positions === value) {
  150. positions = value.slice();
  151. }
  152. positions.push(Cartesian3.clone(positions[0]));
  153. }
  154. if (this._actualPositions.length !== positions.length || this._actualPositions.length !== this._length) {
  155. makeDirty(this, POSITION_SIZE_INDEX);
  156. }
  157. this._positions = value;
  158. this._actualPositions = positions;
  159. this._length = positions.length;
  160. this._boundingVolume = BoundingSphere.fromPoints(this._actualPositions, this._boundingVolume);
  161. this._boundingVolumeWC = BoundingSphere.transform(this._boundingVolume, this._modelMatrix, this._boundingVolumeWC);
  162. makeDirty(this, POSITION_INDEX);
  163. this.update();
  164. }
  165. },
  166. /**
  167. * 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
  168. * {@link https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric|Fabric}.
  169. * @memberof Polyline.prototype
  170. * @type {Material}
  171. */
  172. material: {
  173. get: function() {
  174. return this._material;
  175. },
  176. set: function(material) {
  177. //>>includeStart('debug', pragmas.debug);
  178. if (!defined(material)) {
  179. throw new DeveloperError('material is required.');
  180. }
  181. //>>includeEnd('debug');
  182. if (this._material !== material) {
  183. this._material = material;
  184. makeDirty(this, MATERIAL_INDEX);
  185. }
  186. }
  187. },
  188. /**
  189. * Gets or sets the width of the polyline.
  190. * @memberof Polyline.prototype
  191. * @type {Number}
  192. */
  193. width: {
  194. get: function() {
  195. return this._width;
  196. },
  197. set: function(value) {
  198. //>>includeStart('debug', pragmas.debug)
  199. if (!defined(value)) {
  200. throw new DeveloperError('value is required.');
  201. }
  202. //>>includeEnd('debug');
  203. var width = this._width;
  204. if (value !== width) {
  205. this._width = value;
  206. makeDirty(this, WIDTH_INDEX);
  207. }
  208. }
  209. },
  210. /**
  211. * Gets or sets whether a line segment will be added between the first and last polyline positions.
  212. * @memberof Polyline.prototype
  213. * @type {Boolean}
  214. */
  215. loop: {
  216. get: function() {
  217. return this._loop;
  218. },
  219. set: function(value) {
  220. //>>includeStart('debug', pragmas.debug)
  221. if (!defined(value)) {
  222. throw new DeveloperError('value is required.');
  223. }
  224. //>>includeEnd('debug');
  225. if (value !== this._loop) {
  226. var positions = this._actualPositions;
  227. if (value) {
  228. if (positions.length > 2 && !Cartesian3.equals(positions[0], positions[positions.length - 1])) {
  229. if (positions.length === this._positions.length) {
  230. this._actualPositions = positions = this._positions.slice();
  231. }
  232. positions.push(Cartesian3.clone(positions[0]));
  233. }
  234. } else {
  235. if (positions.length > 2 && Cartesian3.equals(positions[0], positions[positions.length - 1])) {
  236. if (positions.length - 1 === this._positions.length) {
  237. this._actualPositions = this._positions;
  238. } else {
  239. positions.pop();
  240. }
  241. }
  242. }
  243. this._loop = value;
  244. makeDirty(this, POSITION_SIZE_INDEX);
  245. }
  246. }
  247. },
  248. /**
  249. * Gets or sets the user-defined object returned when the polyline is picked.
  250. * @memberof Polyline.prototype
  251. * @type {Object}
  252. */
  253. id : {
  254. get : function() {
  255. return this._id;
  256. },
  257. set : function(value) {
  258. this._id = value;
  259. if (defined(this._pickId)) {
  260. this._pickId.object.id = value;
  261. }
  262. }
  263. }
  264. });
  265. /**
  266. * @private
  267. */
  268. Polyline.prototype.update = function() {
  269. var modelMatrix = Matrix4.IDENTITY;
  270. if (defined(this._polylineCollection)) {
  271. modelMatrix = this._polylineCollection.modelMatrix;
  272. }
  273. var segmentPositionsLength = this._segments.positions.length;
  274. var segmentLengths = this._segments.lengths;
  275. var positionsChanged = this._propertiesChanged[POSITION_INDEX] > 0 || this._propertiesChanged[POSITION_SIZE_INDEX] > 0;
  276. if (!Matrix4.equals(modelMatrix, this._modelMatrix) || positionsChanged) {
  277. this._segments = PolylinePipeline.wrapLongitude(this._actualPositions, modelMatrix);
  278. this._boundingVolumeWC = BoundingSphere.transform(this._boundingVolume, modelMatrix, this._boundingVolumeWC);
  279. }
  280. this._modelMatrix = modelMatrix;
  281. if (this._segments.positions.length !== segmentPositionsLength) {
  282. // number of positions changed
  283. makeDirty(this, POSITION_SIZE_INDEX);
  284. } else {
  285. var length = segmentLengths.length;
  286. for (var i = 0; i < length; ++i) {
  287. if (segmentLengths[i] !== this._segments.lengths[i]) {
  288. // indices changed
  289. makeDirty(this, POSITION_SIZE_INDEX);
  290. break;
  291. }
  292. }
  293. }
  294. };
  295. /**
  296. * @private
  297. */
  298. Polyline.prototype.getPickId = function(context) {
  299. if (!defined(this._pickId)) {
  300. this._pickId = context.createPickId({
  301. primitive : this,
  302. collection : this._polylineCollection,
  303. id : this._id
  304. });
  305. }
  306. return this._pickId;
  307. };
  308. Polyline.prototype._clean = function() {
  309. this._dirty = false;
  310. var properties = this._propertiesChanged;
  311. for ( var k = 0; k < NUMBER_OF_PROPERTIES - 1; ++k) {
  312. properties[k] = 0;
  313. }
  314. };
  315. Polyline.prototype._destroy = function() {
  316. this._pickId = this._pickId && this._pickId.destroy();
  317. this._material = this._material && this._material.destroy();
  318. this._polylineCollection = undefined;
  319. };
  320. return Polyline;
  321. });