LabelVisualizer.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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. var defaultScale = 1.0;
  18. var defaultFont = '30px sans-serif';
  19. var defaultStyle = LabelStyle.FILL;
  20. var defaultFillColor = Color.WHITE;
  21. var defaultOutlineColor = Color.BLACK;
  22. var defaultOutlineWidth = 1.0;
  23. var defaultShowBackground = false;
  24. var defaultBackgroundColor = new Color(0.165, 0.165, 0.165, 0.8);
  25. var defaultBackgroundPadding = new Cartesian2(7, 5);
  26. var defaultPixelOffset = Cartesian2.ZERO;
  27. var defaultEyeOffset = Cartesian3.ZERO;
  28. var defaultHeightReference = HeightReference.NONE;
  29. var defaultHorizontalOrigin = HorizontalOrigin.CENTER;
  30. var defaultVerticalOrigin = VerticalOrigin.CENTER;
  31. var positionScratch = new Cartesian3();
  32. var fillColorScratch = new Color();
  33. var outlineColorScratch = new Color();
  34. var backgroundColorScratch = new Color();
  35. var backgroundPaddingScratch = new Cartesian2();
  36. var eyeOffsetScratch = new Cartesian3();
  37. var pixelOffsetScratch = new Cartesian2();
  38. var translucencyByDistanceScratch = new NearFarScalar();
  39. var pixelOffsetScaleByDistanceScratch = new NearFarScalar();
  40. var scaleByDistanceScratch = new NearFarScalar();
  41. var 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(LabelVisualizer.prototype._onCollectionChanged, this);
  66. this._cluster = entityCluster;
  67. this._entityCollection = entityCollection;
  68. this._items = new AssociativeArray();
  69. this._onCollectionChanged(entityCollection, entityCollection.values, [], []);
  70. }
  71. /**
  72. * Updates the primitives created by this visualizer to match their
  73. * Entity counterpart at the given time.
  74. *
  75. * @param {JulianDate} time The time to update to.
  76. * @returns {Boolean} This function always returns true.
  77. */
  78. LabelVisualizer.prototype.update = function(time) {
  79. //>>includeStart('debug', pragmas.debug);
  80. if (!defined(time)) {
  81. throw new DeveloperError('time is required.');
  82. }
  83. //>>includeEnd('debug');
  84. var items = this._items.values;
  85. var cluster = this._cluster;
  86. for (var i = 0, len = items.length; i < len; i++) {
  87. var item = items[i];
  88. var entity = item.entity;
  89. var labelGraphics = entity._label;
  90. var text;
  91. var label = item.label;
  92. var show = entity.isShowing && entity.isAvailable(time) && Property.getValueOrDefault(labelGraphics._show, time, true);
  93. var position;
  94. if (show) {
  95. position = Property.getValueOrUndefined(entity._position, time, positionScratch);
  96. text = Property.getValueOrUndefined(labelGraphics._text, time);
  97. show = defined(position) && defined(text);
  98. }
  99. if (!show) {
  100. //don't bother creating or updating anything else
  101. returnPrimitive(item, entity, cluster);
  102. continue;
  103. }
  104. if (!Property.isConstant(entity._position)) {
  105. cluster._clusterDirty = true;
  106. }
  107. var updateClamping = false;
  108. var heightReference = Property.getValueOrDefault(labelGraphics._heightReference, time, defaultHeightReference);
  109. if (!defined(label)) {
  110. label = cluster.getLabel(entity);
  111. label.id = entity;
  112. item.label = label;
  113. // If this new label happens to have a position and height reference that match our new values,
  114. // label._updateClamping will not be called automatically. That's a problem because the clamped
  115. // height may be based on different terrain than is now loaded. So we'll manually call
  116. // _updateClamping below.
  117. updateClamping = Cartesian3.equals(label.position, position) && label.heightReference === heightReference;
  118. }
  119. label.show = true;
  120. label.position = position;
  121. label.text = text;
  122. label.scale = Property.getValueOrDefault(labelGraphics._scale, time, defaultScale);
  123. label.font = Property.getValueOrDefault(labelGraphics._font, time, defaultFont);
  124. label.style = Property.getValueOrDefault(labelGraphics._style, time, defaultStyle);
  125. label.fillColor = Property.getValueOrDefault(labelGraphics._fillColor, time, defaultFillColor, fillColorScratch);
  126. label.outlineColor = Property.getValueOrDefault(labelGraphics._outlineColor, time, defaultOutlineColor, outlineColorScratch);
  127. label.outlineWidth = Property.getValueOrDefault(labelGraphics._outlineWidth, time, defaultOutlineWidth);
  128. label.showBackground = Property.getValueOrDefault(labelGraphics._showBackground, time, defaultShowBackground);
  129. label.backgroundColor = Property.getValueOrDefault(labelGraphics._backgroundColor, time, defaultBackgroundColor, backgroundColorScratch);
  130. label.backgroundPadding = Property.getValueOrDefault(labelGraphics._backgroundPadding, time, defaultBackgroundPadding, backgroundPaddingScratch);
  131. label.pixelOffset = Property.getValueOrDefault(labelGraphics._pixelOffset, time, defaultPixelOffset, pixelOffsetScratch);
  132. label.eyeOffset = Property.getValueOrDefault(labelGraphics._eyeOffset, time, defaultEyeOffset, eyeOffsetScratch);
  133. label.heightReference = heightReference;
  134. label.horizontalOrigin = Property.getValueOrDefault(labelGraphics._horizontalOrigin, time, defaultHorizontalOrigin);
  135. label.verticalOrigin = Property.getValueOrDefault(labelGraphics._verticalOrigin, time, defaultVerticalOrigin);
  136. label.translucencyByDistance = Property.getValueOrUndefined(labelGraphics._translucencyByDistance, time, translucencyByDistanceScratch);
  137. label.pixelOffsetScaleByDistance = Property.getValueOrUndefined(labelGraphics._pixelOffsetScaleByDistance, time, pixelOffsetScaleByDistanceScratch);
  138. label.scaleByDistance = Property.getValueOrUndefined(labelGraphics._scaleByDistance, time, scaleByDistanceScratch);
  139. label.distanceDisplayCondition = Property.getValueOrUndefined(labelGraphics._distanceDisplayCondition, time, distanceDisplayConditionScratch);
  140. label.disableDepthTestDistance = Property.getValueOrUndefined(labelGraphics._disableDepthTestDistance, time);
  141. if (updateClamping) {
  142. label._updateClamping();
  143. }
  144. }
  145. return true;
  146. };
  147. /**
  148. * Computes a bounding sphere which encloses the visualization produced for the specified entity.
  149. * The bounding sphere is in the fixed frame of the scene's globe.
  150. *
  151. * @param {Entity} entity The entity whose bounding sphere to compute.
  152. * @param {BoundingSphere} result The bounding sphere onto which to store the result.
  153. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere,
  154. * BoundingSphereState.PENDING if the result is still being computed, or
  155. * BoundingSphereState.FAILED if the entity has no visualization in the current scene.
  156. * @private
  157. */
  158. LabelVisualizer.prototype.getBoundingSphere = function(entity, result) {
  159. //>>includeStart('debug', pragmas.debug);
  160. if (!defined(entity)) {
  161. throw new DeveloperError('entity is required.');
  162. }
  163. if (!defined(result)) {
  164. throw new DeveloperError('result is required.');
  165. }
  166. //>>includeEnd('debug');
  167. var item = this._items.get(entity.id);
  168. if (!defined(item) || !defined(item.label)) {
  169. return BoundingSphereState.FAILED;
  170. }
  171. var label = item.label;
  172. result.center = Cartesian3.clone(defaultValue(label._clampedPosition, label.position), result.center);
  173. result.radius = 0;
  174. return BoundingSphereState.DONE;
  175. };
  176. /**
  177. * Returns true if this object was destroyed; otherwise, false.
  178. *
  179. * @returns {Boolean} True if this object was destroyed; otherwise, false.
  180. */
  181. LabelVisualizer.prototype.isDestroyed = function() {
  182. return false;
  183. };
  184. /**
  185. * Removes and destroys all primitives created by this instance.
  186. */
  187. LabelVisualizer.prototype.destroy = function() {
  188. this._entityCollection.collectionChanged.removeEventListener(LabelVisualizer.prototype._onCollectionChanged, this);
  189. var entities = this._entityCollection.values;
  190. for (var i = 0; i < entities.length; i++) {
  191. this._cluster.removeLabel(entities[i]);
  192. }
  193. return destroyObject(this);
  194. };
  195. LabelVisualizer.prototype._onCollectionChanged = function(entityCollection, added, removed, changed) {
  196. var i;
  197. var entity;
  198. var items = this._items;
  199. var cluster = this._cluster;
  200. for (i = added.length - 1; i > -1; i--) {
  201. entity = added[i];
  202. if (defined(entity._label) && defined(entity._position)) {
  203. items.set(entity.id, new EntityData(entity));
  204. }
  205. }
  206. for (i = changed.length - 1; i > -1; i--) {
  207. entity = changed[i];
  208. if (defined(entity._label) && defined(entity._position)) {
  209. if (!items.contains(entity.id)) {
  210. items.set(entity.id, new EntityData(entity));
  211. }
  212. } else {
  213. returnPrimitive(items.get(entity.id), entity, cluster);
  214. items.remove(entity.id);
  215. }
  216. }
  217. for (i = removed.length - 1; i > -1; i--) {
  218. entity = removed[i];
  219. returnPrimitive(items.get(entity.id), entity, cluster);
  220. items.remove(entity.id);
  221. }
  222. };
  223. function returnPrimitive(item, entity, cluster) {
  224. if (defined(item)) {
  225. item.label = undefined;
  226. cluster.removeLabel(entity);
  227. }
  228. }
  229. export default LabelVisualizer;