BillboardVisualizer.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. import AssociativeArray from "../Core/AssociativeArray.js";
  2. import BoundingRectangle from "../Core/BoundingRectangle.js";
  3. import Cartesian2 from "../Core/Cartesian2.js";
  4. import Cartesian3 from "../Core/Cartesian3.js";
  5. import Color from "../Core/Color.js";
  6. import defined from "../Core/defined.js";
  7. import destroyObject from "../Core/destroyObject.js";
  8. import DeveloperError from "../Core/DeveloperError.js";
  9. import DistanceDisplayCondition from "../Core/DistanceDisplayCondition.js";
  10. import NearFarScalar from "../Core/NearFarScalar.js";
  11. import HeightReference from "../Scene/HeightReference.js";
  12. import HorizontalOrigin from "../Scene/HorizontalOrigin.js";
  13. import VerticalOrigin from "../Scene/VerticalOrigin.js";
  14. import BoundingSphereState from "./BoundingSphereState.js";
  15. import Property from "./Property.js";
  16. const defaultColor = Color.WHITE;
  17. const defaultEyeOffset = Cartesian3.ZERO;
  18. const defaultHeightReference = HeightReference.NONE;
  19. const defaultPixelOffset = Cartesian2.ZERO;
  20. const defaultScale = 1.0;
  21. const defaultRotation = 0.0;
  22. const defaultAlignedAxis = Cartesian3.ZERO;
  23. const defaultHorizontalOrigin = HorizontalOrigin.CENTER;
  24. const defaultVerticalOrigin = VerticalOrigin.CENTER;
  25. const defaultSizeInMeters = false;
  26. const positionScratch = new Cartesian3();
  27. const colorScratch = new Color();
  28. const eyeOffsetScratch = new Cartesian3();
  29. const pixelOffsetScratch = new Cartesian2();
  30. const scaleByDistanceScratch = new NearFarScalar();
  31. const translucencyByDistanceScratch = new NearFarScalar();
  32. const pixelOffsetScaleByDistanceScratch = new NearFarScalar();
  33. const boundingRectangleScratch = new BoundingRectangle();
  34. const distanceDisplayConditionScratch = new DistanceDisplayCondition();
  35. function EntityData(entity) {
  36. this.entity = entity;
  37. this.billboard = undefined;
  38. this.textureValue = undefined;
  39. }
  40. /**
  41. * A {@link Visualizer} which maps {@link Entity#billboard} to a {@link Billboard}.
  42. * @alias BillboardVisualizer
  43. * @constructor
  44. *
  45. * @param {EntityCluster} entityCluster The entity cluster to manage the collection of billboards and optionally cluster with other entities.
  46. * @param {EntityCollection} entityCollection The entityCollection to visualize.
  47. */
  48. function BillboardVisualizer(entityCluster, entityCollection) {
  49. //>>includeStart('debug', pragmas.debug);
  50. if (!defined(entityCluster)) {
  51. throw new DeveloperError("entityCluster is required.");
  52. }
  53. if (!defined(entityCollection)) {
  54. throw new DeveloperError("entityCollection is required.");
  55. }
  56. //>>includeEnd('debug');
  57. entityCollection.collectionChanged.addEventListener(
  58. BillboardVisualizer.prototype._onCollectionChanged,
  59. this
  60. );
  61. this._cluster = entityCluster;
  62. this._entityCollection = entityCollection;
  63. this._items = new AssociativeArray();
  64. this._onCollectionChanged(entityCollection, entityCollection.values, [], []);
  65. }
  66. /**
  67. * Updates the primitives created by this visualizer to match their
  68. * Entity counterpart at the given time.
  69. *
  70. * @param {JulianDate} time The time to update to.
  71. * @returns {boolean} This function always returns true.
  72. */
  73. BillboardVisualizer.prototype.update = function (time) {
  74. //>>includeStart('debug', pragmas.debug);
  75. if (!defined(time)) {
  76. throw new DeveloperError("time is required.");
  77. }
  78. //>>includeEnd('debug');
  79. const items = this._items.values;
  80. const cluster = this._cluster;
  81. for (let i = 0, len = items.length; i < len; i++) {
  82. const item = items[i];
  83. const entity = item.entity;
  84. const billboardGraphics = entity._billboard;
  85. let textureValue;
  86. let billboard = item.billboard;
  87. let show =
  88. entity.isShowing &&
  89. entity.isAvailable(time) &&
  90. Property.getValueOrDefault(billboardGraphics._show, time, true);
  91. let position;
  92. if (show) {
  93. position = Property.getValueOrUndefined(
  94. entity._position,
  95. time,
  96. positionScratch
  97. );
  98. textureValue = Property.getValueOrUndefined(
  99. billboardGraphics._image,
  100. time
  101. );
  102. show = defined(position) && defined(textureValue);
  103. }
  104. if (!show) {
  105. //don't bother creating or updating anything else
  106. returnPrimitive(item, entity, cluster);
  107. continue;
  108. }
  109. if (!Property.isConstant(entity._position)) {
  110. cluster._clusterDirty = true;
  111. }
  112. if (!defined(billboard)) {
  113. billboard = cluster.getBillboard(entity);
  114. billboard.id = entity;
  115. billboard.image = undefined;
  116. item.billboard = billboard;
  117. }
  118. billboard.show = show;
  119. if (!defined(billboard.image) || item.textureValue !== textureValue) {
  120. billboard.image = textureValue;
  121. item.textureValue = textureValue;
  122. }
  123. billboard.position = position;
  124. billboard.color = Property.getValueOrDefault(
  125. billboardGraphics._color,
  126. time,
  127. defaultColor,
  128. colorScratch
  129. );
  130. billboard.eyeOffset = Property.getValueOrDefault(
  131. billboardGraphics._eyeOffset,
  132. time,
  133. defaultEyeOffset,
  134. eyeOffsetScratch
  135. );
  136. billboard.heightReference = Property.getValueOrDefault(
  137. billboardGraphics._heightReference,
  138. time,
  139. defaultHeightReference
  140. );
  141. billboard.pixelOffset = Property.getValueOrDefault(
  142. billboardGraphics._pixelOffset,
  143. time,
  144. defaultPixelOffset,
  145. pixelOffsetScratch
  146. );
  147. billboard.scale = Property.getValueOrDefault(
  148. billboardGraphics._scale,
  149. time,
  150. defaultScale
  151. );
  152. billboard.rotation = Property.getValueOrDefault(
  153. billboardGraphics._rotation,
  154. time,
  155. defaultRotation
  156. );
  157. billboard.alignedAxis = Property.getValueOrDefault(
  158. billboardGraphics._alignedAxis,
  159. time,
  160. defaultAlignedAxis
  161. );
  162. billboard.horizontalOrigin = Property.getValueOrDefault(
  163. billboardGraphics._horizontalOrigin,
  164. time,
  165. defaultHorizontalOrigin
  166. );
  167. billboard.verticalOrigin = Property.getValueOrDefault(
  168. billboardGraphics._verticalOrigin,
  169. time,
  170. defaultVerticalOrigin
  171. );
  172. billboard.width = Property.getValueOrUndefined(
  173. billboardGraphics._width,
  174. time
  175. );
  176. billboard.height = Property.getValueOrUndefined(
  177. billboardGraphics._height,
  178. time
  179. );
  180. billboard.scaleByDistance = Property.getValueOrUndefined(
  181. billboardGraphics._scaleByDistance,
  182. time,
  183. scaleByDistanceScratch
  184. );
  185. billboard.translucencyByDistance = Property.getValueOrUndefined(
  186. billboardGraphics._translucencyByDistance,
  187. time,
  188. translucencyByDistanceScratch
  189. );
  190. billboard.pixelOffsetScaleByDistance = Property.getValueOrUndefined(
  191. billboardGraphics._pixelOffsetScaleByDistance,
  192. time,
  193. pixelOffsetScaleByDistanceScratch
  194. );
  195. billboard.sizeInMeters = Property.getValueOrDefault(
  196. billboardGraphics._sizeInMeters,
  197. time,
  198. defaultSizeInMeters
  199. );
  200. billboard.distanceDisplayCondition = Property.getValueOrUndefined(
  201. billboardGraphics._distanceDisplayCondition,
  202. time,
  203. distanceDisplayConditionScratch
  204. );
  205. billboard.disableDepthTestDistance = Property.getValueOrUndefined(
  206. billboardGraphics._disableDepthTestDistance,
  207. time
  208. );
  209. const subRegion = Property.getValueOrUndefined(
  210. billboardGraphics._imageSubRegion,
  211. time,
  212. boundingRectangleScratch
  213. );
  214. if (defined(subRegion)) {
  215. billboard.setImageSubRegion(billboard._imageId, subRegion);
  216. }
  217. }
  218. return true;
  219. };
  220. /**
  221. * Computes a bounding sphere which encloses the visualization produced for the specified entity.
  222. * The bounding sphere is in the fixed frame of the scene's globe.
  223. *
  224. * @param {Entity} entity The entity whose bounding sphere to compute.
  225. * @param {BoundingSphere} result The bounding sphere onto which to store the result.
  226. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere,
  227. * BoundingSphereState.PENDING if the result is still being computed, or
  228. * BoundingSphereState.FAILED if the entity has no visualization in the current scene.
  229. * @private
  230. */
  231. BillboardVisualizer.prototype.getBoundingSphere = function (entity, result) {
  232. //>>includeStart('debug', pragmas.debug);
  233. if (!defined(entity)) {
  234. throw new DeveloperError("entity is required.");
  235. }
  236. if (!defined(result)) {
  237. throw new DeveloperError("result is required.");
  238. }
  239. //>>includeEnd('debug');
  240. const item = this._items.get(entity.id);
  241. if (!defined(item) || !defined(item.billboard)) {
  242. return BoundingSphereState.FAILED;
  243. }
  244. const billboard = item.billboard;
  245. if (billboard.heightReference === HeightReference.NONE) {
  246. result.center = Cartesian3.clone(billboard.position, result.center);
  247. } else {
  248. if (!defined(billboard._clampedPosition)) {
  249. return BoundingSphereState.PENDING;
  250. }
  251. result.center = Cartesian3.clone(billboard._clampedPosition, result.center);
  252. }
  253. result.radius = 0;
  254. return BoundingSphereState.DONE;
  255. };
  256. /**
  257. * Returns true if this object was destroyed; otherwise, false.
  258. *
  259. * @returns {boolean} True if this object was destroyed; otherwise, false.
  260. */
  261. BillboardVisualizer.prototype.isDestroyed = function () {
  262. return false;
  263. };
  264. /**
  265. * Removes and destroys all primitives created by this instance.
  266. */
  267. BillboardVisualizer.prototype.destroy = function () {
  268. this._entityCollection.collectionChanged.removeEventListener(
  269. BillboardVisualizer.prototype._onCollectionChanged,
  270. this
  271. );
  272. const entities = this._entityCollection.values;
  273. for (let i = 0; i < entities.length; i++) {
  274. this._cluster.removeBillboard(entities[i]);
  275. }
  276. return destroyObject(this);
  277. };
  278. BillboardVisualizer.prototype._onCollectionChanged = function (
  279. entityCollection,
  280. added,
  281. removed,
  282. changed
  283. ) {
  284. let i;
  285. let entity;
  286. const items = this._items;
  287. const cluster = this._cluster;
  288. for (i = added.length - 1; i > -1; i--) {
  289. entity = added[i];
  290. if (defined(entity._billboard) && defined(entity._position)) {
  291. items.set(entity.id, new EntityData(entity));
  292. }
  293. }
  294. for (i = changed.length - 1; i > -1; i--) {
  295. entity = changed[i];
  296. if (defined(entity._billboard) && defined(entity._position)) {
  297. if (!items.contains(entity.id)) {
  298. items.set(entity.id, new EntityData(entity));
  299. }
  300. } else {
  301. returnPrimitive(items.get(entity.id), entity, cluster);
  302. items.remove(entity.id);
  303. }
  304. }
  305. for (i = removed.length - 1; i > -1; i--) {
  306. entity = removed[i];
  307. returnPrimitive(items.get(entity.id), entity, cluster);
  308. items.remove(entity.id);
  309. }
  310. };
  311. function returnPrimitive(item, entity, cluster) {
  312. if (defined(item)) {
  313. item.billboard = undefined;
  314. cluster.removeBillboard(entity);
  315. }
  316. }
  317. export default BillboardVisualizer;