LabelVisualizer.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. import AssociativeArray from "../Core/AssociativeArray.js";
  2. import Cartesian2 from "../Core/Cartesian2.js";
  3. import Cartesian3 from "../Core/Cartesian3.js";
  4. import Color from "../Core/Color.js";
  5. import defaultValue from "../Core/defaultValue.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 LabelStyle from "../Scene/LabelStyle.js";
  14. import VerticalOrigin from "../Scene/VerticalOrigin.js";
  15. import BoundingSphereState from "./BoundingSphereState.js";
  16. import Property from "./Property.js";
  17. const defaultScale = 1.0;
  18. const defaultFont = "30px sans-serif";
  19. const defaultStyle = LabelStyle.FILL;
  20. const defaultFillColor = Color.WHITE;
  21. const defaultOutlineColor = Color.BLACK;
  22. const defaultOutlineWidth = 1.0;
  23. const defaultShowBackground = false;
  24. const defaultBackgroundColor = new Color(0.165, 0.165, 0.165, 0.8);
  25. const defaultBackgroundPadding = new Cartesian2(7, 5);
  26. const defaultPixelOffset = Cartesian2.ZERO;
  27. const defaultEyeOffset = Cartesian3.ZERO;
  28. const defaultHeightReference = HeightReference.NONE;
  29. const defaultHorizontalOrigin = HorizontalOrigin.CENTER;
  30. const defaultVerticalOrigin = VerticalOrigin.CENTER;
  31. const positionScratch = new Cartesian3();
  32. const fillColorScratch = new Color();
  33. const outlineColorScratch = new Color();
  34. const backgroundColorScratch = new Color();
  35. const backgroundPaddingScratch = new Cartesian2();
  36. const eyeOffsetScratch = new Cartesian3();
  37. const pixelOffsetScratch = new Cartesian2();
  38. const translucencyByDistanceScratch = new NearFarScalar();
  39. const pixelOffsetScaleByDistanceScratch = new NearFarScalar();
  40. const scaleByDistanceScratch = new NearFarScalar();
  41. const distanceDisplayConditionScratch = new DistanceDisplayCondition();
  42. function EntityData(entity) {
  43. this.entity = entity;
  44. this.label = undefined;
  45. this.index = undefined;
  46. }
  47. /**
  48. * A {@link Visualizer} which maps the {@link LabelGraphics} instance
  49. * in {@link Entity#label} to a {@link Label}.
  50. * @alias LabelVisualizer
  51. * @constructor
  52. *
  53. * @param {EntityCluster} entityCluster The entity cluster to manage the collection of billboards and optionally cluster with other entities.
  54. * @param {EntityCollection} entityCollection The entityCollection to visualize.
  55. */
  56. function LabelVisualizer(entityCluster, entityCollection) {
  57. //>>includeStart('debug', pragmas.debug);
  58. if (!defined(entityCluster)) {
  59. throw new DeveloperError("entityCluster is required.");
  60. }
  61. if (!defined(entityCollection)) {
  62. throw new DeveloperError("entityCollection is required.");
  63. }
  64. //>>includeEnd('debug');
  65. entityCollection.collectionChanged.addEventListener(
  66. LabelVisualizer.prototype._onCollectionChanged,
  67. this
  68. );
  69. this._cluster = entityCluster;
  70. this._entityCollection = entityCollection;
  71. this._items = new AssociativeArray();
  72. this._onCollectionChanged(entityCollection, entityCollection.values, [], []);
  73. }
  74. /**
  75. * Updates the primitives created by this visualizer to match their
  76. * Entity counterpart at the given time.
  77. *
  78. * @param {JulianDate} time The time to update to.
  79. * @returns {boolean} This function always returns true.
  80. */
  81. LabelVisualizer.prototype.update = function (time) {
  82. //>>includeStart('debug', pragmas.debug);
  83. if (!defined(time)) {
  84. throw new DeveloperError("time is required.");
  85. }
  86. //>>includeEnd('debug');
  87. const items = this._items.values;
  88. const cluster = this._cluster;
  89. for (let i = 0, len = items.length; i < len; i++) {
  90. const item = items[i];
  91. const entity = item.entity;
  92. const labelGraphics = entity._label;
  93. let text;
  94. let label = item.label;
  95. let show =
  96. entity.isShowing &&
  97. entity.isAvailable(time) &&
  98. Property.getValueOrDefault(labelGraphics._show, time, true);
  99. let position;
  100. if (show) {
  101. position = Property.getValueOrUndefined(
  102. entity._position,
  103. time,
  104. positionScratch
  105. );
  106. text = Property.getValueOrUndefined(labelGraphics._text, time);
  107. show = defined(position) && defined(text);
  108. }
  109. if (!show) {
  110. //don't bother creating or updating anything else
  111. returnPrimitive(item, entity, cluster);
  112. continue;
  113. }
  114. if (!Property.isConstant(entity._position)) {
  115. cluster._clusterDirty = true;
  116. }
  117. let updateClamping = false;
  118. const heightReference = Property.getValueOrDefault(
  119. labelGraphics._heightReference,
  120. time,
  121. defaultHeightReference
  122. );
  123. if (!defined(label)) {
  124. label = cluster.getLabel(entity);
  125. label.id = entity;
  126. item.label = label;
  127. // If this new label happens to have a position and height reference that match our new values,
  128. // label._updateClamping will not be called automatically. That's a problem because the clamped
  129. // height may be based on different terrain than is now loaded. So we'll manually call
  130. // _updateClamping below.
  131. updateClamping =
  132. Cartesian3.equals(label.position, position) &&
  133. label.heightReference === heightReference;
  134. }
  135. label.show = true;
  136. label.position = position;
  137. label.text = text;
  138. label.scale = Property.getValueOrDefault(
  139. labelGraphics._scale,
  140. time,
  141. defaultScale
  142. );
  143. label.font = Property.getValueOrDefault(
  144. labelGraphics._font,
  145. time,
  146. defaultFont
  147. );
  148. label.style = Property.getValueOrDefault(
  149. labelGraphics._style,
  150. time,
  151. defaultStyle
  152. );
  153. label.fillColor = Property.getValueOrDefault(
  154. labelGraphics._fillColor,
  155. time,
  156. defaultFillColor,
  157. fillColorScratch
  158. );
  159. label.outlineColor = Property.getValueOrDefault(
  160. labelGraphics._outlineColor,
  161. time,
  162. defaultOutlineColor,
  163. outlineColorScratch
  164. );
  165. label.outlineWidth = Property.getValueOrDefault(
  166. labelGraphics._outlineWidth,
  167. time,
  168. defaultOutlineWidth
  169. );
  170. label.showBackground = Property.getValueOrDefault(
  171. labelGraphics._showBackground,
  172. time,
  173. defaultShowBackground
  174. );
  175. label.backgroundColor = Property.getValueOrDefault(
  176. labelGraphics._backgroundColor,
  177. time,
  178. defaultBackgroundColor,
  179. backgroundColorScratch
  180. );
  181. label.backgroundPadding = Property.getValueOrDefault(
  182. labelGraphics._backgroundPadding,
  183. time,
  184. defaultBackgroundPadding,
  185. backgroundPaddingScratch
  186. );
  187. label.pixelOffset = Property.getValueOrDefault(
  188. labelGraphics._pixelOffset,
  189. time,
  190. defaultPixelOffset,
  191. pixelOffsetScratch
  192. );
  193. label.eyeOffset = Property.getValueOrDefault(
  194. labelGraphics._eyeOffset,
  195. time,
  196. defaultEyeOffset,
  197. eyeOffsetScratch
  198. );
  199. label.heightReference = heightReference;
  200. label.horizontalOrigin = Property.getValueOrDefault(
  201. labelGraphics._horizontalOrigin,
  202. time,
  203. defaultHorizontalOrigin
  204. );
  205. label.verticalOrigin = Property.getValueOrDefault(
  206. labelGraphics._verticalOrigin,
  207. time,
  208. defaultVerticalOrigin
  209. );
  210. label.translucencyByDistance = Property.getValueOrUndefined(
  211. labelGraphics._translucencyByDistance,
  212. time,
  213. translucencyByDistanceScratch
  214. );
  215. label.pixelOffsetScaleByDistance = Property.getValueOrUndefined(
  216. labelGraphics._pixelOffsetScaleByDistance,
  217. time,
  218. pixelOffsetScaleByDistanceScratch
  219. );
  220. label.scaleByDistance = Property.getValueOrUndefined(
  221. labelGraphics._scaleByDistance,
  222. time,
  223. scaleByDistanceScratch
  224. );
  225. label.distanceDisplayCondition = Property.getValueOrUndefined(
  226. labelGraphics._distanceDisplayCondition,
  227. time,
  228. distanceDisplayConditionScratch
  229. );
  230. label.disableDepthTestDistance = Property.getValueOrUndefined(
  231. labelGraphics._disableDepthTestDistance,
  232. time
  233. );
  234. if (updateClamping) {
  235. label._updateClamping();
  236. }
  237. }
  238. return true;
  239. };
  240. /**
  241. * Computes a bounding sphere which encloses the visualization produced for the specified entity.
  242. * The bounding sphere is in the fixed frame of the scene's globe.
  243. *
  244. * @param {Entity} entity The entity whose bounding sphere to compute.
  245. * @param {BoundingSphere} result The bounding sphere onto which to store the result.
  246. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere,
  247. * BoundingSphereState.PENDING if the result is still being computed, or
  248. * BoundingSphereState.FAILED if the entity has no visualization in the current scene.
  249. * @private
  250. */
  251. LabelVisualizer.prototype.getBoundingSphere = function (entity, result) {
  252. //>>includeStart('debug', pragmas.debug);
  253. if (!defined(entity)) {
  254. throw new DeveloperError("entity is required.");
  255. }
  256. if (!defined(result)) {
  257. throw new DeveloperError("result is required.");
  258. }
  259. //>>includeEnd('debug');
  260. const item = this._items.get(entity.id);
  261. if (!defined(item) || !defined(item.label)) {
  262. return BoundingSphereState.FAILED;
  263. }
  264. const label = item.label;
  265. result.center = Cartesian3.clone(
  266. defaultValue(label._clampedPosition, label.position),
  267. result.center
  268. );
  269. result.radius = 0;
  270. return BoundingSphereState.DONE;
  271. };
  272. /**
  273. * Returns true if this object was destroyed; otherwise, false.
  274. *
  275. * @returns {boolean} True if this object was destroyed; otherwise, false.
  276. */
  277. LabelVisualizer.prototype.isDestroyed = function () {
  278. return false;
  279. };
  280. /**
  281. * Removes and destroys all primitives created by this instance.
  282. */
  283. LabelVisualizer.prototype.destroy = function () {
  284. this._entityCollection.collectionChanged.removeEventListener(
  285. LabelVisualizer.prototype._onCollectionChanged,
  286. this
  287. );
  288. const entities = this._entityCollection.values;
  289. for (let i = 0; i < entities.length; i++) {
  290. this._cluster.removeLabel(entities[i]);
  291. }
  292. return destroyObject(this);
  293. };
  294. LabelVisualizer.prototype._onCollectionChanged = function (
  295. entityCollection,
  296. added,
  297. removed,
  298. changed
  299. ) {
  300. let i;
  301. let entity;
  302. const items = this._items;
  303. const cluster = this._cluster;
  304. for (i = added.length - 1; i > -1; i--) {
  305. entity = added[i];
  306. if (defined(entity._label) && defined(entity._position)) {
  307. items.set(entity.id, new EntityData(entity));
  308. }
  309. }
  310. for (i = changed.length - 1; i > -1; i--) {
  311. entity = changed[i];
  312. if (defined(entity._label) && defined(entity._position)) {
  313. if (!items.contains(entity.id)) {
  314. items.set(entity.id, new EntityData(entity));
  315. }
  316. } else {
  317. returnPrimitive(items.get(entity.id), entity, cluster);
  318. items.remove(entity.id);
  319. }
  320. }
  321. for (i = removed.length - 1; i > -1; i--) {
  322. entity = removed[i];
  323. returnPrimitive(items.get(entity.id), entity, cluster);
  324. items.remove(entity.id);
  325. }
  326. };
  327. function returnPrimitive(item, entity, cluster) {
  328. if (defined(item)) {
  329. item.label = undefined;
  330. cluster.removeLabel(entity);
  331. }
  332. }
  333. export default LabelVisualizer;