PolylineVisualizer.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. import AssociativeArray from "../Core/AssociativeArray.js";
  2. import BoundingSphere from "../Core/BoundingSphere.js";
  3. import Check from "../Core/Check.js";
  4. import defaultValue from "../Core/defaultValue.js";
  5. import defined from "../Core/defined.js";
  6. import destroyObject from "../Core/destroyObject.js";
  7. import ClassificationType from "../Scene/ClassificationType.js";
  8. import PolylineColorAppearance from "../Scene/PolylineColorAppearance.js";
  9. import PolylineMaterialAppearance from "../Scene/PolylineMaterialAppearance.js";
  10. import ShadowMode from "../Scene/ShadowMode.js";
  11. import BoundingSphereState from "./BoundingSphereState.js";
  12. import ColorMaterialProperty from "./ColorMaterialProperty.js";
  13. import DynamicGeometryBatch from "./DynamicGeometryBatch.js";
  14. import PolylineGeometryUpdater from "./PolylineGeometryUpdater.js";
  15. import StaticGeometryColorBatch from "./StaticGeometryColorBatch.js";
  16. import StaticGeometryPerMaterialBatch from "./StaticGeometryPerMaterialBatch.js";
  17. import StaticGroundPolylinePerMaterialBatch from "./StaticGroundPolylinePerMaterialBatch.js";
  18. const emptyArray = [];
  19. function removeUpdater(that, updater) {
  20. //We don't keep track of which batch an updater is in, so just remove it from all of them.
  21. const batches = that._batches;
  22. const length = batches.length;
  23. for (let i = 0; i < length; i++) {
  24. batches[i].remove(updater);
  25. }
  26. }
  27. function insertUpdaterIntoBatch(that, time, updater) {
  28. if (updater.isDynamic) {
  29. that._dynamicBatch.add(time, updater);
  30. return;
  31. }
  32. if (updater.clampToGround && updater.fillEnabled) {
  33. // Also checks for support
  34. const classificationType = updater.classificationTypeProperty.getValue(
  35. time
  36. );
  37. that._groundBatches[classificationType].add(time, updater);
  38. return;
  39. }
  40. let shadows;
  41. if (updater.fillEnabled) {
  42. shadows = updater.shadowsProperty.getValue(time);
  43. }
  44. let multiplier = 0;
  45. if (defined(updater.depthFailMaterialProperty)) {
  46. multiplier =
  47. updater.depthFailMaterialProperty instanceof ColorMaterialProperty
  48. ? 1
  49. : 2;
  50. }
  51. let index;
  52. if (defined(shadows)) {
  53. index = shadows + multiplier * ShadowMode.NUMBER_OF_SHADOW_MODES;
  54. }
  55. if (updater.fillEnabled) {
  56. if (updater.fillMaterialProperty instanceof ColorMaterialProperty) {
  57. that._colorBatches[index].add(time, updater);
  58. } else {
  59. that._materialBatches[index].add(time, updater);
  60. }
  61. }
  62. }
  63. /**
  64. * A visualizer for polylines represented by {@link Primitive} instances.
  65. * @alias PolylineVisualizer
  66. * @constructor
  67. *
  68. * @param {Scene} scene The scene the primitives will be rendered in.
  69. * @param {EntityCollection} entityCollection The entityCollection to visualize.
  70. * @param {PrimitiveCollection} [primitives=scene.primitives] A collection to add primitives related to the entities
  71. * @param {PrimitiveCollection} [groundPrimitives=scene.groundPrimitives] A collection to add ground primitives related to the entities
  72. */
  73. function PolylineVisualizer(
  74. scene,
  75. entityCollection,
  76. primitives,
  77. groundPrimitives
  78. ) {
  79. //>>includeStart('debug', pragmas.debug);
  80. Check.defined("scene", scene);
  81. Check.defined("entityCollection", entityCollection);
  82. //>>includeEnd('debug');
  83. groundPrimitives = defaultValue(groundPrimitives, scene.groundPrimitives);
  84. primitives = defaultValue(primitives, scene.primitives);
  85. this._scene = scene;
  86. this._primitives = primitives;
  87. this._entityCollection = undefined;
  88. this._addedObjects = new AssociativeArray();
  89. this._removedObjects = new AssociativeArray();
  90. this._changedObjects = new AssociativeArray();
  91. let i;
  92. const numberOfShadowModes = ShadowMode.NUMBER_OF_SHADOW_MODES;
  93. this._colorBatches = new Array(numberOfShadowModes * 3);
  94. this._materialBatches = new Array(numberOfShadowModes * 3);
  95. for (i = 0; i < numberOfShadowModes; ++i) {
  96. this._colorBatches[i] = new StaticGeometryColorBatch(
  97. primitives,
  98. PolylineColorAppearance,
  99. undefined,
  100. false,
  101. i
  102. ); // no depth fail appearance
  103. this._materialBatches[i] = new StaticGeometryPerMaterialBatch(
  104. primitives,
  105. PolylineMaterialAppearance,
  106. undefined,
  107. false,
  108. i
  109. );
  110. this._colorBatches[i + numberOfShadowModes] = new StaticGeometryColorBatch(
  111. primitives,
  112. PolylineColorAppearance,
  113. PolylineColorAppearance,
  114. false,
  115. i
  116. ); //depth fail appearance variations
  117. this._materialBatches[
  118. i + numberOfShadowModes
  119. ] = new StaticGeometryPerMaterialBatch(
  120. primitives,
  121. PolylineMaterialAppearance,
  122. PolylineColorAppearance,
  123. false,
  124. i
  125. );
  126. this._colorBatches[
  127. i + numberOfShadowModes * 2
  128. ] = new StaticGeometryColorBatch(
  129. primitives,
  130. PolylineColorAppearance,
  131. PolylineMaterialAppearance,
  132. false,
  133. i
  134. );
  135. this._materialBatches[
  136. i + numberOfShadowModes * 2
  137. ] = new StaticGeometryPerMaterialBatch(
  138. primitives,
  139. PolylineMaterialAppearance,
  140. PolylineMaterialAppearance,
  141. false,
  142. i
  143. );
  144. }
  145. this._dynamicBatch = new DynamicGeometryBatch(primitives, groundPrimitives);
  146. const numberOfClassificationTypes =
  147. ClassificationType.NUMBER_OF_CLASSIFICATION_TYPES;
  148. this._groundBatches = new Array(numberOfClassificationTypes);
  149. for (i = 0; i < numberOfClassificationTypes; ++i) {
  150. this._groundBatches[i] = new StaticGroundPolylinePerMaterialBatch(
  151. groundPrimitives,
  152. i
  153. );
  154. }
  155. this._batches = this._colorBatches.concat(
  156. this._materialBatches,
  157. this._dynamicBatch,
  158. this._groundBatches
  159. );
  160. this._subscriptions = new AssociativeArray();
  161. this._updaters = new AssociativeArray();
  162. this._entityCollection = entityCollection;
  163. entityCollection.collectionChanged.addEventListener(
  164. PolylineVisualizer.prototype._onCollectionChanged,
  165. this
  166. );
  167. this._onCollectionChanged(
  168. entityCollection,
  169. entityCollection.values,
  170. emptyArray
  171. );
  172. }
  173. /**
  174. * Updates all of the primitives created by this visualizer to match their
  175. * Entity counterpart at the given time.
  176. *
  177. * @param {JulianDate} time The time to update to.
  178. * @returns {boolean} True if the visualizer successfully updated to the provided time,
  179. * false if the visualizer is waiting for asynchronous primitives to be created.
  180. */
  181. PolylineVisualizer.prototype.update = function (time) {
  182. //>>includeStart('debug', pragmas.debug);
  183. Check.defined("time", time);
  184. //>>includeEnd('debug');
  185. const addedObjects = this._addedObjects;
  186. const added = addedObjects.values;
  187. const removedObjects = this._removedObjects;
  188. const removed = removedObjects.values;
  189. const changedObjects = this._changedObjects;
  190. const changed = changedObjects.values;
  191. let i;
  192. let entity;
  193. let id;
  194. let updater;
  195. for (i = changed.length - 1; i > -1; i--) {
  196. entity = changed[i];
  197. id = entity.id;
  198. updater = this._updaters.get(id);
  199. //If in a single update, an entity gets removed and a new instance
  200. //re-added with the same id, the updater no longer tracks the
  201. //correct entity, we need to both remove the old one and
  202. //add the new one, which is done by pushing the entity
  203. //onto the removed/added lists.
  204. if (updater.entity === entity) {
  205. removeUpdater(this, updater);
  206. insertUpdaterIntoBatch(this, time, updater);
  207. } else {
  208. removed.push(entity);
  209. added.push(entity);
  210. }
  211. }
  212. for (i = removed.length - 1; i > -1; i--) {
  213. entity = removed[i];
  214. id = entity.id;
  215. updater = this._updaters.get(id);
  216. removeUpdater(this, updater);
  217. updater.destroy();
  218. this._updaters.remove(id);
  219. this._subscriptions.get(id)();
  220. this._subscriptions.remove(id);
  221. }
  222. for (i = added.length - 1; i > -1; i--) {
  223. entity = added[i];
  224. id = entity.id;
  225. updater = new PolylineGeometryUpdater(entity, this._scene);
  226. this._updaters.set(id, updater);
  227. insertUpdaterIntoBatch(this, time, updater);
  228. this._subscriptions.set(
  229. id,
  230. updater.geometryChanged.addEventListener(
  231. PolylineVisualizer._onGeometryChanged,
  232. this
  233. )
  234. );
  235. }
  236. addedObjects.removeAll();
  237. removedObjects.removeAll();
  238. changedObjects.removeAll();
  239. let isUpdated = true;
  240. const batches = this._batches;
  241. const length = batches.length;
  242. for (i = 0; i < length; i++) {
  243. isUpdated = batches[i].update(time) && isUpdated;
  244. }
  245. return isUpdated;
  246. };
  247. const getBoundingSphereArrayScratch = [];
  248. const getBoundingSphereBoundingSphereScratch = new BoundingSphere();
  249. /**
  250. * Computes a bounding sphere which encloses the visualization produced for the specified entity.
  251. * The bounding sphere is in the fixed frame of the scene's globe.
  252. *
  253. * @param {Entity} entity The entity whose bounding sphere to compute.
  254. * @param {BoundingSphere} result The bounding sphere onto which to store the result.
  255. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere,
  256. * BoundingSphereState.PENDING if the result is still being computed, or
  257. * BoundingSphereState.FAILED if the entity has no visualization in the current scene.
  258. * @private
  259. */
  260. PolylineVisualizer.prototype.getBoundingSphere = function (entity, result) {
  261. //>>includeStart('debug', pragmas.debug);
  262. Check.defined("entity", entity);
  263. Check.defined("result", result);
  264. //>>includeEnd('debug');
  265. const boundingSpheres = getBoundingSphereArrayScratch;
  266. const tmp = getBoundingSphereBoundingSphereScratch;
  267. let count = 0;
  268. let state = BoundingSphereState.DONE;
  269. const batches = this._batches;
  270. const batchesLength = batches.length;
  271. const updater = this._updaters.get(entity.id);
  272. for (let i = 0; i < batchesLength; i++) {
  273. state = batches[i].getBoundingSphere(updater, tmp);
  274. if (state === BoundingSphereState.PENDING) {
  275. return BoundingSphereState.PENDING;
  276. } else if (state === BoundingSphereState.DONE) {
  277. boundingSpheres[count] = BoundingSphere.clone(
  278. tmp,
  279. boundingSpheres[count]
  280. );
  281. count++;
  282. }
  283. }
  284. if (count === 0) {
  285. return BoundingSphereState.FAILED;
  286. }
  287. boundingSpheres.length = count;
  288. BoundingSphere.fromBoundingSpheres(boundingSpheres, result);
  289. return BoundingSphereState.DONE;
  290. };
  291. /**
  292. * Returns true if this object was destroyed; otherwise, false.
  293. *
  294. * @returns {boolean} True if this object was destroyed; otherwise, false.
  295. */
  296. PolylineVisualizer.prototype.isDestroyed = function () {
  297. return false;
  298. };
  299. /**
  300. * Removes and destroys all primitives created by this instance.
  301. */
  302. PolylineVisualizer.prototype.destroy = function () {
  303. this._entityCollection.collectionChanged.removeEventListener(
  304. PolylineVisualizer.prototype._onCollectionChanged,
  305. this
  306. );
  307. this._addedObjects.removeAll();
  308. this._removedObjects.removeAll();
  309. let i;
  310. const batches = this._batches;
  311. let length = batches.length;
  312. for (i = 0; i < length; i++) {
  313. batches[i].removeAllPrimitives();
  314. }
  315. const subscriptions = this._subscriptions.values;
  316. length = subscriptions.length;
  317. for (i = 0; i < length; i++) {
  318. subscriptions[i]();
  319. }
  320. this._subscriptions.removeAll();
  321. return destroyObject(this);
  322. };
  323. /**
  324. * @private
  325. */
  326. PolylineVisualizer._onGeometryChanged = function (updater) {
  327. const removedObjects = this._removedObjects;
  328. const changedObjects = this._changedObjects;
  329. const entity = updater.entity;
  330. const id = entity.id;
  331. if (!defined(removedObjects.get(id)) && !defined(changedObjects.get(id))) {
  332. changedObjects.set(id, entity);
  333. }
  334. };
  335. /**
  336. * @private
  337. */
  338. PolylineVisualizer.prototype._onCollectionChanged = function (
  339. entityCollection,
  340. added,
  341. removed
  342. ) {
  343. const addedObjects = this._addedObjects;
  344. const removedObjects = this._removedObjects;
  345. const changedObjects = this._changedObjects;
  346. let i;
  347. let id;
  348. let entity;
  349. for (i = removed.length - 1; i > -1; i--) {
  350. entity = removed[i];
  351. id = entity.id;
  352. if (!addedObjects.remove(id)) {
  353. removedObjects.set(id, entity);
  354. changedObjects.remove(id);
  355. }
  356. }
  357. for (i = added.length - 1; i > -1; i--) {
  358. entity = added[i];
  359. id = entity.id;
  360. if (removedObjects.remove(id)) {
  361. changedObjects.set(id, entity);
  362. } else {
  363. addedObjects.set(id, entity);
  364. }
  365. }
  366. };
  367. export default PolylineVisualizer;