PrimitiveCollection.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /*global define*/
  2. define([
  3. '../Core/createGuid',
  4. '../Core/defaultValue',
  5. '../Core/defined',
  6. '../Core/defineProperties',
  7. '../Core/destroyObject',
  8. '../Core/DeveloperError'
  9. ], function(
  10. createGuid,
  11. defaultValue,
  12. defined,
  13. defineProperties,
  14. destroyObject,
  15. DeveloperError) {
  16. "use strict";
  17. /**
  18. * A collection of primitives. This is most often used with {@link Scene#primitives},
  19. * but <code>PrimitiveCollection</code> is also a primitive itself so collections can
  20. * be added to collections forming a hierarchy.
  21. *
  22. * @alias PrimitiveCollection
  23. * @constructor
  24. *
  25. * @param {Object} [options] Object with the following properties:
  26. * @param {Boolean} [options.show=true] Determines if the primitives in the collection will be shown.
  27. * @param {Boolean} [options.destroyPrimitives=true] Determines if primitives in the collection are destroyed when they are removed.
  28. *
  29. * @example
  30. * var billboards = new Cesium.BillboardCollection();
  31. * var labels = new Cesium.LabelCollection();
  32. *
  33. * var collection = new Cesium.PrimitiveCollection();
  34. * collection.add(billboards);
  35. *
  36. * scene.primitives.add(collection); // Add collection
  37. * scene.primitives.add(labels); // Add regular primitive
  38. */
  39. var PrimitiveCollection = function(options) {
  40. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  41. this._primitives = [];
  42. this._guid = createGuid();
  43. /**
  44. * Determines if primitives in this collection will be shown.
  45. *
  46. * @type {Boolean}
  47. * @default true
  48. */
  49. this.show = defaultValue(options.show, true);
  50. /**
  51. * Determines if primitives in the collection are destroyed when they are removed by
  52. * {@link PrimitiveCollection#destroy} or {@link PrimitiveCollection#remove} or implicitly
  53. * by {@link PrimitiveCollection#removeAll}.
  54. *
  55. * @type {Boolean}
  56. * @default true
  57. *
  58. * @example
  59. * // Example 1. Primitives are destroyed by default.
  60. * var primitives = new Cesium.PrimitiveCollection();
  61. * var labels = primitives.add(new Cesium.LabelCollection());
  62. * primitives = primitives.destroy();
  63. * var b = labels.isDestroyed(); // true
  64. *
  65. * @example
  66. * // Example 2. Do not destroy primitives in a collection.
  67. * var primitives = new Cesium.PrimitiveCollection();
  68. * primitives.destroyPrimitives = false;
  69. * var labels = primitives.add(new Cesium.LabelCollection());
  70. * primitives = primitives.destroy();
  71. * var b = labels.isDestroyed(); // false
  72. * labels = labels.destroy(); // explicitly destroy
  73. */
  74. this.destroyPrimitives = defaultValue(options.destroyPrimitives, true);
  75. };
  76. defineProperties(PrimitiveCollection.prototype, {
  77. /**
  78. * Gets the number of primitives in the collection.
  79. *
  80. * @memberof PrimitiveCollection.prototype
  81. *
  82. * @type {Number}
  83. * @readonly
  84. */
  85. length : {
  86. get : function() {
  87. return this._primitives.length;
  88. }
  89. }
  90. });
  91. /**
  92. * Adds a primitive to the collection.
  93. *
  94. * @param {Object} primitive The primitive to add.
  95. * @returns {Object} The primitive added to the collection.
  96. *
  97. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  98. *
  99. * @example
  100. * var billboards = scene.primitives.add(new Cesium.BillboardCollection());
  101. */
  102. PrimitiveCollection.prototype.add = function(primitive) {
  103. //>>includeStart('debug', pragmas.debug);
  104. if (!defined(primitive)) {
  105. throw new DeveloperError('primitive is required.');
  106. }
  107. //>>includeEnd('debug');
  108. var external = (primitive._external = primitive._external || {});
  109. var composites = (external._composites = external._composites || {});
  110. composites[this._guid] = {
  111. collection : this
  112. };
  113. this._primitives.push(primitive);
  114. return primitive;
  115. };
  116. /**
  117. * Removes a primitive from the collection.
  118. *
  119. * @param {Object} [primitive] The primitive to remove.
  120. * @returns {Boolean} <code>true</code> if the primitive was removed; <code>false</code> if the primitive is <code>undefined</code> or was not found in the collection.
  121. *
  122. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  123. *
  124. * @see PrimitiveCollection#destroyPrimitives
  125. *
  126. * @example
  127. * var billboards = scene.primitives.add(new Cesium.BillboardCollection());
  128. * scene.primitives.remove(p); // Returns true
  129. */
  130. PrimitiveCollection.prototype.remove = function(primitive) {
  131. // PERFORMANCE_IDEA: We can obviously make this a lot faster.
  132. if (this.contains(primitive)) {
  133. var index = this._primitives.indexOf(primitive);
  134. if (index !== -1) {
  135. this._primitives.splice(index, 1);
  136. delete primitive._external._composites[this._guid];
  137. if (this.destroyPrimitives) {
  138. primitive.destroy();
  139. }
  140. return true;
  141. }
  142. // else ... this is not possible, I swear.
  143. }
  144. return false;
  145. };
  146. /**
  147. * Removes and destroys a primitive, regardless of destroyPrimitives setting.
  148. * @private
  149. */
  150. PrimitiveCollection.prototype.removeAndDestroy = function(primitive) {
  151. var removed = this.remove(primitive);
  152. if (removed && !this.destroyPrimitives) {
  153. primitive.destroy();
  154. }
  155. return removed;
  156. };
  157. /**
  158. * Removes all primitives in the collection.
  159. *
  160. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  161. *
  162. * @see PrimitiveCollection#destroyPrimitives
  163. */
  164. PrimitiveCollection.prototype.removeAll = function() {
  165. if (this.destroyPrimitives) {
  166. var primitives = this._primitives;
  167. var length = primitives.length;
  168. for ( var i = 0; i < length; ++i) {
  169. primitives[i].destroy();
  170. }
  171. }
  172. this._primitives = [];
  173. };
  174. /**
  175. * Determines if this collection contains a primitive.
  176. *
  177. * @param {Object} [primitive] The primitive to check for.
  178. * @returns {Boolean} <code>true</code> if the primitive is in the collection; <code>false</code> if the primitive is <code>undefined</code> or was not found in the collection.
  179. *
  180. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  181. *
  182. * @see PrimitiveCollection#get
  183. */
  184. PrimitiveCollection.prototype.contains = function(primitive) {
  185. return !!(defined(primitive) &&
  186. primitive._external &&
  187. primitive._external._composites &&
  188. primitive._external._composites[this._guid]);
  189. };
  190. function getPrimitiveIndex(compositePrimitive, primitive) {
  191. //>>includeStart('debug', pragmas.debug);
  192. if (!compositePrimitive.contains(primitive)) {
  193. throw new DeveloperError('primitive is not in this collection.');
  194. }
  195. //>>includeEnd('debug');
  196. return compositePrimitive._primitives.indexOf(primitive);
  197. }
  198. /**
  199. * Raises a primitive "up one" in the collection. If all primitives in the collection are drawn
  200. * on the globe surface, this visually moves the primitive up one.
  201. *
  202. * @param {Object} [primitive] The primitive to raise.
  203. *
  204. * @exception {DeveloperError} primitive is not in this collection.
  205. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  206. *
  207. * @see PrimitiveCollection#raiseToTop
  208. * @see PrimitiveCollection#lower
  209. * @see PrimitiveCollection#lowerToBottom
  210. */
  211. PrimitiveCollection.prototype.raise = function(primitive) {
  212. if (defined(primitive)) {
  213. var index = getPrimitiveIndex(this, primitive);
  214. var primitives = this._primitives;
  215. if (index !== primitives.length - 1) {
  216. var p = primitives[index];
  217. primitives[index] = primitives[index + 1];
  218. primitives[index + 1] = p;
  219. }
  220. }
  221. };
  222. /**
  223. * Raises a primitive to the "top" of the collection. If all primitives in the collection are drawn
  224. * on the globe surface, this visually moves the primitive to the top.
  225. *
  226. * @param {Object} [primitive] The primitive to raise the top.
  227. *
  228. * @exception {DeveloperError} primitive is not in this collection.
  229. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  230. *
  231. * @see PrimitiveCollection#raise
  232. * @see PrimitiveCollection#lower
  233. * @see PrimitiveCollection#lowerToBottom
  234. */
  235. PrimitiveCollection.prototype.raiseToTop = function(primitive) {
  236. if (defined(primitive)) {
  237. var index = getPrimitiveIndex(this, primitive);
  238. var primitives = this._primitives;
  239. if (index !== primitives.length - 1) {
  240. // PERFORMANCE_IDEA: Could be faster
  241. primitives.splice(index, 1);
  242. primitives.push(primitive);
  243. }
  244. }
  245. };
  246. /**
  247. * Lowers a primitive "down one" in the collection. If all primitives in the collection are drawn
  248. * on the globe surface, this visually moves the primitive down one.
  249. *
  250. * @param {Object} [primitive] The primitive to lower.
  251. *
  252. * @exception {DeveloperError} primitive is not in this collection.
  253. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  254. *
  255. * @see PrimitiveCollection#lowerToBottom
  256. * @see PrimitiveCollection#raise
  257. * @see PrimitiveCollection#raiseToTop
  258. */
  259. PrimitiveCollection.prototype.lower = function(primitive) {
  260. if (defined(primitive)) {
  261. var index = getPrimitiveIndex(this, primitive);
  262. var primitives = this._primitives;
  263. if (index !== 0) {
  264. var p = primitives[index];
  265. primitives[index] = primitives[index - 1];
  266. primitives[index - 1] = p;
  267. }
  268. }
  269. };
  270. /**
  271. * Lowers a primitive to the "bottom" of the collection. If all primitives in the collection are drawn
  272. * on the globe surface, this visually moves the primitive to the bottom.
  273. *
  274. * @param {Object} [primitive] The primitive to lower to the bottom.
  275. *
  276. * @exception {DeveloperError} primitive is not in this collection.
  277. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  278. *
  279. * @see PrimitiveCollection#lower
  280. * @see PrimitiveCollection#raise
  281. * @see PrimitiveCollection#raiseToTop
  282. */
  283. PrimitiveCollection.prototype.lowerToBottom = function(primitive) {
  284. if (defined(primitive)) {
  285. var index = getPrimitiveIndex(this, primitive);
  286. var primitives = this._primitives;
  287. if (index !== 0) {
  288. // PERFORMANCE_IDEA: Could be faster
  289. primitives.splice(index, 1);
  290. primitives.unshift(primitive);
  291. }
  292. }
  293. };
  294. /**
  295. * Returns the primitive in the collection at the specified index.
  296. *
  297. * @param {Number} index The zero-based index of the primitive to return.
  298. * @returns {Object} The primitive at the <code>index</code>.
  299. *
  300. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  301. *
  302. * @see PrimitiveCollection#length
  303. *
  304. * @example
  305. * // Toggle the show property of every primitive in the collection.
  306. * var primitives = scene.primitives;
  307. * var length = primitives.length;
  308. * for (var i = 0; i < length; ++i) {
  309. * var p = primitives.get(i);
  310. * p.show = !p.show;
  311. * }
  312. */
  313. PrimitiveCollection.prototype.get = function(index) {
  314. //>>includeStart('debug', pragmas.debug);
  315. if (!defined(index)) {
  316. throw new DeveloperError('index is required.');
  317. }
  318. //>>includeEnd('debug');
  319. return this._primitives[index];
  320. };
  321. /**
  322. * @private
  323. */
  324. PrimitiveCollection.prototype.update = function(context, frameState, commandList) {
  325. if (!this.show) {
  326. return;
  327. }
  328. var primitives = this._primitives;
  329. // Using primitives.length in the loop is a temporary workaround
  330. // to allow quadtree updates to add and remove primitives in
  331. // update(). This will be changed to manage added and removed lists.
  332. for (var i = 0; i < primitives.length; ++i) {
  333. primitives[i].update(context, frameState, commandList);
  334. }
  335. };
  336. /**
  337. * Returns true if this object was destroyed; otherwise, false.
  338. * <br /><br />
  339. * If this object was destroyed, it should not be used; calling any function other than
  340. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  341. *
  342. * @returns {Boolean} True if this object was destroyed; otherwise, false.
  343. *
  344. * @see PrimitiveCollection#destroy
  345. */
  346. PrimitiveCollection.prototype.isDestroyed = function() {
  347. return false;
  348. };
  349. /**
  350. * Destroys the WebGL resources held by each primitive in this collection. Explicitly destroying this
  351. * collection allows for deterministic release of WebGL resources, instead of relying on the garbage
  352. * collector to destroy this collection.
  353. * <br /><br />
  354. * Since destroying a collection destroys all the contained primitives, only destroy a collection
  355. * when you are sure no other code is still using any of the contained primitives.
  356. * <br /><br />
  357. * Once this collection is destroyed, it should not be used; calling any function other than
  358. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  359. * assign the return value (<code>undefined</code>) to the object as done in the example.
  360. *
  361. * @returns {undefined}
  362. *
  363. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  364. *
  365. * @see PrimitiveCollection#isDestroyed
  366. *
  367. * @example
  368. * primitives = primitives && primitives.destroy();
  369. */
  370. PrimitiveCollection.prototype.destroy = function() {
  371. this.removeAll();
  372. return destroyObject(this);
  373. };
  374. return PrimitiveCollection;
  375. });