bruce-cesium 6.9.1 → 6.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2597,7 +2597,9 @@
2597
2597
  constructor(params) {
2598
2598
  this.widgetId = BModels.ObjectUtils.UId();
2599
2599
  let { viewer, container, manager } = params;
2600
- if (!container) {
2600
+ // A host app can pass an undefined viewer (eg. mid-teardown); fall back to
2601
+ // no container rather than crashing on viewer.container.
2602
+ if (!container && viewer) {
2601
2603
  container = viewer.container;
2602
2604
  }
2603
2605
  this._viewer = viewer;
@@ -3153,7 +3155,10 @@
3153
3155
  this.customControls = new Map();
3154
3156
  this._generateStyles();
3155
3157
  this._generateElement();
3156
- this._viewer[VIEWER_VIEW_BAR_WIDGET_KEY] = this;
3158
+ // Guard against a host app constructing this widget with no live viewer.
3159
+ if (this._viewer) {
3160
+ this._viewer[VIEWER_VIEW_BAR_WIDGET_KEY] = this;
3161
+ }
3157
3162
  }
3158
3163
  Dispose() {
3159
3164
  super.Dispose();
@@ -23350,7 +23355,10 @@
23350
23355
  this._viewId = params.viewId;
23351
23356
  this._generateStyles();
23352
23357
  this._generateElement();
23353
- this._viewer[VIEWER_LEFT_PANEL_WIDGET_KEY] = this;
23358
+ // Guard against a host app constructing this widget with no live viewer.
23359
+ if (this._viewer) {
23360
+ this._viewer[VIEWER_LEFT_PANEL_WIDGET_KEY] = this;
23361
+ }
23354
23362
  this._loading = this.loadBookmarks();
23355
23363
  this._loading.then(() => {
23356
23364
  if (params.runFirstBookmark && this.bookmarks.length) {
@@ -23792,10 +23800,18 @@
23792
23800
  return;
23793
23801
  }
23794
23802
  const clazz = this.customTabs.get(this._enabledPanelTab);
23795
- tabContent = new clazz({
23796
- widget: this,
23797
- container: container
23798
- });
23803
+ try {
23804
+ tabContent = new clazz({
23805
+ widget: this,
23806
+ container: container
23807
+ });
23808
+ }
23809
+ catch (e) {
23810
+ // Partner-supplied tab content class; a throwing constructor shouldn't take
23811
+ // down the whole panel.
23812
+ console.error(e);
23813
+ return;
23814
+ }
23799
23815
  break;
23800
23816
  }
23801
23817
  this._enabledTabContent = tabContent;
@@ -23817,17 +23833,18 @@
23817
23833
  document.documentElement.style.setProperty(VIEWER_LEFT_PANEL_CSS_VAR_LEFT, "0px");
23818
23834
  }
23819
23835
  SelectBookmark(bookmark) {
23836
+ var _a;
23820
23837
  const isFirstEnabled = !this._lastEnabledBookmarkId;
23821
23838
  this._lastEnabledBookmarkId = bookmark.ID;
23822
23839
  exports.ViewRenderEngine.Render({
23823
- apiGetter: this._apiGetters.GetBruceGetter(),
23840
+ apiGetter: (_a = this._apiGetters) === null || _a === void 0 ? void 0 : _a.GetBruceGetter(),
23824
23841
  bookmark: bookmark,
23825
23842
  bookmarkId: bookmark.ID,
23826
23843
  manager: this._manager,
23827
23844
  skipTransition: isFirstEnabled,
23828
23845
  viewer: this._viewer,
23829
23846
  viewId: this._viewId
23830
- });
23847
+ }).catch((e) => console.error(e));
23831
23848
  if (this._onBookmarkSelect) {
23832
23849
  this._onBookmarkSelect.Trigger(bookmark);
23833
23850
  }
@@ -26523,7 +26540,10 @@
26523
26540
  }
26524
26541
  this._updateBookmarks();
26525
26542
  });
26526
- this._viewer[VIEWER_BOOKMARKS_WIDGET_KEY] = this;
26543
+ // Guard against a host app constructing this widget with no live viewer.
26544
+ if (this._viewer) {
26545
+ this._viewer[VIEWER_BOOKMARKS_WIDGET_KEY] = this;
26546
+ }
26527
26547
  }
26528
26548
  LoadViewId(params) {
26529
26549
  let { viewId, runFirstBookmark, autoPlay, bookmarkId } = params;
@@ -26901,9 +26921,10 @@
26901
26921
  `;
26902
26922
  }
26903
26923
  _updateBookmarks() {
26924
+ var _a;
26904
26925
  const bookmarks = this.bookmarks;
26905
26926
  this._element.style.display = bookmarks.length ? "flex" : "none";
26906
- const api = this._apiGetters.GetBruceApi();
26927
+ const api = (_a = this._apiGetters) === null || _a === void 0 ? void 0 : _a.GetBruceApi();
26907
26928
  const row = this._row;
26908
26929
  row.innerHTML = "";
26909
26930
  for (const bookmark of bookmarks) {
@@ -26945,7 +26966,7 @@
26945
26966
  skipTransition: isFirstEnabled,
26946
26967
  viewId: this._viewId,
26947
26968
  viewer: this._viewer
26948
- });
26969
+ }).catch((e) => console.error(e));
26949
26970
  }
26950
26971
  _updateBookmark() {
26951
26972
  const allBookmarks = this._row.querySelectorAll(".NextspaceBookmarksRowItem");
@@ -29026,7 +29047,12 @@
29026
29047
  this._cesiumEventRemoval = null;
29027
29048
  }
29028
29049
  _listenSelection() {
29050
+ var _a, _b;
29029
29051
  this._disposeCesiumEvent();
29052
+ // No live viewer/scene to attach cursor listeners to (eg. widget constructed without one).
29053
+ if (!((_b = (_a = this._viewer) === null || _a === void 0 ? void 0 : _a.scene) === null || _b === void 0 ? void 0 : _b.canvas)) {
29054
+ return;
29055
+ }
29030
29056
  const events = new Cesium.ScreenSpaceEventHandler(this._viewer.scene.canvas);
29031
29057
  const throttle = new CursorBarMouseEventThrottle(this._viewer);
29032
29058
  let lastHoverPos = null;
@@ -36531,7 +36557,7 @@
36531
36557
  }
36532
36558
 
36533
36559
  (function (EntityRenderEnginePolygon) {
36534
- function Render(params) {
36560
+ async function Render(params) {
36535
36561
  var _a, _b;
36536
36562
  const entity = params.entity;
36537
36563
  const pRings = BModels.Entity.GetValue({
@@ -36542,9 +36568,26 @@
36542
36568
  return null;
36543
36569
  }
36544
36570
  const style = params.style;
36571
+ const fillType = style.fillType || BModels.Style.EPolygonFillType.Color;
36545
36572
  const fillColorTrace = style.fillColor ? BModels.Calculator.TraceGetColor(style.fillColor, entity, params.tags) : { value: null, effective: null };
36546
36573
  const bFillColor = fillColorTrace.value;
36547
36574
  const cFillColor = bFillColor ? ColorToCColor(bFillColor) : Cesium.Color.fromCssColorString("rgba(139, 195, 74, 0.8)");
36575
+ let textureDataUri = null;
36576
+ let textureTraceEffective = null;
36577
+ if (fillType === BModels.Style.EPolygonFillType.Texture) {
36578
+ const resolved = await resolveTexturedFill({
36579
+ api: params.api,
36580
+ style,
36581
+ entity,
36582
+ tags: params.tags
36583
+ });
36584
+ textureDataUri = resolved.dataUri;
36585
+ textureTraceEffective = resolved.effective;
36586
+ }
36587
+ const hasFill = fillType === BModels.Style.EPolygonFillType.Texture ? Boolean(textureDataUri) : cFillColor.alpha > 0;
36588
+ const fillMaterial = textureDataUri
36589
+ ? new Cesium.ImageMaterialProperty({ image: textureDataUri, transparent: true })
36590
+ : cFillColor;
36548
36591
  const lineColorTrace = style.lineColor ? BModels.Calculator.TraceGetColor(style.lineColor, entity, params.tags) : { value: null, effective: null };
36549
36592
  const bLineColor = lineColorTrace.value;
36550
36593
  const cLineColor = bLineColor ? ColorToCColor(bLineColor) : Cesium.Color.fromCssColorString("rgba(80, 80, 80, 0.8)");
@@ -36563,7 +36606,7 @@
36563
36606
  }
36564
36607
  // If both outline and fill is not available then don't render anything.
36565
36608
  if ((width <= 0 || cLineColor.alpha <= 0) &&
36566
- cFillColor.alpha <= 0) {
36609
+ !hasFill) {
36567
36610
  return null;
36568
36611
  }
36569
36612
  let heightRef = getHeightRef$3(style);
@@ -36618,7 +36661,7 @@
36618
36661
  id: BModels.ObjectUtils.UId(15),
36619
36662
  polygon: {
36620
36663
  hierarchy: new Cesium.PolygonHierarchy(posses, holePosses.map(x => new Cesium.PolygonHierarchy(x))),
36621
- material: cFillColor,
36664
+ material: fillMaterial,
36622
36665
  extrudedHeight: extrusion.value,
36623
36666
  extrudedHeightReference: extrusion.exHeightRef,
36624
36667
  shadows: Cesium.ShadowMode.ENABLED,
@@ -36683,15 +36726,21 @@
36683
36726
  cEntity.polygon.distanceDisplayCondition = new Cesium.ConstantProperty(exports.EntityRenderEngine.GetDisplayCondition(params.minDistance, params.maxDistance, width <= 0 || !cLineColor || units == "m" ? size : null, true));
36684
36727
  cEntity.polygon.shadows = new Cesium.ConstantProperty(Cesium.ShadowMode.ENABLED);
36685
36728
  cEntity.polygon.classificationType = new Cesium.ConstantProperty(classification);
36686
- // We'll use "SetDefaultColor" to updating the internal reference and to allow for an animation.
36687
- // WARNING: polygon does not support animation (yet?).
36688
- exports.CesiumEntityStyler.SetDefaultColor({
36689
- color: cFillColor,
36690
- entity: cEntity,
36691
- viewer: params.viewer,
36692
- override: true,
36693
- requestRender: false
36694
- });
36729
+ // TODO: animate the texture!
36730
+ if (textureDataUri) {
36731
+ cEntity.polygon.material = fillMaterial;
36732
+ }
36733
+ else {
36734
+ // We'll use "SetDefaultColor" to updating the internal reference and to allow for an animation.
36735
+ // WARNING: polygon does not support animation (yet?).
36736
+ exports.CesiumEntityStyler.SetDefaultColor({
36737
+ color: cFillColor,
36738
+ entity: cEntity,
36739
+ viewer: params.viewer,
36740
+ override: true,
36741
+ requestRender: false
36742
+ });
36743
+ }
36695
36744
  cEntity.show = true;
36696
36745
  }
36697
36746
  if (hasOutline) {
@@ -36839,6 +36888,7 @@
36839
36888
  }
36840
36889
  cEntity.styleEffective = exports.StyleEffective.Combine([
36841
36890
  { key: "color", effective: fillColorTrace.effective },
36891
+ { key: "texture", effective: textureTraceEffective },
36842
36892
  { key: "lineColor", effective: lineColorTrace.effective },
36843
36893
  { key: "lineWidth", effective: lineWidthTrace.effective }
36844
36894
  ]);
@@ -36890,11 +36940,12 @@
36890
36940
  }
36891
36941
  }
36892
36942
  const pStyle = (_b = style === null || style === void 0 ? void 0 : style.polygonStyle) !== null && _b !== void 0 ? _b : {};
36893
- const cEntity = Render({
36943
+ const cEntity = await Render({
36894
36944
  entity: entity,
36895
36945
  style: pStyle,
36896
36946
  tags: tags,
36897
36947
  viewer: params.viewer,
36948
+ api,
36898
36949
  maxDistance: zoomItem.MaxZoom,
36899
36950
  minDistance: zoomItem.MinZoom,
36900
36951
  // Temporarily disabling re-using graphics for polygons as they have some bugs, and
@@ -37012,6 +37063,95 @@
37012
37063
  }
37013
37064
  return style;
37014
37065
  }
37066
+ // Cache of tinted texture data URIs, keyed by resolved url + color mask, so repeated renders of the
37067
+ // same polygon (or multiple polygons sharing a texture) don't refetch/reprocess the same image.
37068
+ const _textureCache = new BModels.LRUCache(50);
37069
+ /**
37070
+ * Returns if a resolved texture value looks like a URL rather than a client file ID.
37071
+ * @param value
37072
+ */
37073
+ function looksLikeTextureUrl(value) {
37074
+ return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(value) || value.startsWith("//") || value.startsWith("data:");
37075
+ }
37076
+ /**
37077
+ * Resolves a polygon style's texture field and if a color ask is needed it tints the resulting image.
37078
+ * @param params
37079
+ */
37080
+ async function resolveTexturedFill(params) {
37081
+ const { api, style, entity, tags } = params;
37082
+ const textureTrace = style.texture ? BModels.Calculator.TraceGetString(style.texture, entity, tags) : { value: null, effective: null };
37083
+ const raw = textureTrace.value;
37084
+ if (!raw || typeof raw != "string" || !api) {
37085
+ return { dataUri: null, effective: textureTrace.effective };
37086
+ }
37087
+ let url = raw;
37088
+ if (!looksLikeTextureUrl(raw)) {
37089
+ await api.Loading;
37090
+ url = BModels.ClientFile.GetUrl({
37091
+ api,
37092
+ fileId: raw,
37093
+ viaCdn: true
37094
+ });
37095
+ }
37096
+ const mask = style.textureColorMask;
37097
+ const cacheKey = "texture-" + url + "-" + (mask ? mask.type + "-" + mask.minColor + "-" + mask.maxColor : "none");
37098
+ let prom = _textureCache.Get(cacheKey);
37099
+ if (!prom) {
37100
+ prom = buildTintedTextureDataUri(url, mask).catch(() => null);
37101
+ _textureCache.Set(cacheKey, prom);
37102
+ }
37103
+ return { dataUri: await prom, effective: textureTrace.effective };
37104
+ }
37105
+ /**
37106
+ * Fetches an image and, if a color mask is provided, remaps its (assumed grayscale) pixel values
37107
+ * into a gradient between the mask's minColor/maxColor, preserving source alpha.
37108
+ * @param url
37109
+ * @param mask
37110
+ */
37111
+ async function buildTintedTextureDataUri(url, mask) {
37112
+ const response = await fetch(url);
37113
+ const blob = await response.blob();
37114
+ const objectUrl = URL.createObjectURL(blob);
37115
+ let image;
37116
+ try {
37117
+ image = await loadImage(objectUrl);
37118
+ }
37119
+ finally {
37120
+ URL.revokeObjectURL(objectUrl);
37121
+ }
37122
+ const canvas = document.createElement("canvas");
37123
+ canvas.width = image.width;
37124
+ canvas.height = image.height;
37125
+ const context = canvas.getContext("2d");
37126
+ context.drawImage(image, 0, 0);
37127
+ if (mask && mask.type === BModels.Style.ETextureColorMaskType.BlackWhite) {
37128
+ const minColor = BModels.Color.ColorFromStr(mask.minColor) || { red: 0, green: 0, blue: 0, alpha: 1 };
37129
+ const maxColor = BModels.Color.ColorFromStr(mask.maxColor) || { red: 255, green: 255, blue: 255, alpha: 1 };
37130
+ const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
37131
+ const data = imageData.data;
37132
+ for (let i = 0; i < data.length; i += 4) {
37133
+ // The texture is rendered black-to-white server-side, so any channel carries the value.
37134
+ const t = data[i] / 255;
37135
+ data[i] = Math.round(minColor.red + (maxColor.red - minColor.red) * t);
37136
+ data[i + 1] = Math.round(minColor.green + (maxColor.green - minColor.green) * t);
37137
+ data[i + 2] = Math.round(minColor.blue + (maxColor.blue - minColor.blue) * t);
37138
+ // Preserve source transparency (eg unrasterized pixels) blended with the mask's own alpha.
37139
+ const srcAlpha = data[i + 3] / 255;
37140
+ const maskAlpha = minColor.alpha + (maxColor.alpha - minColor.alpha) * t;
37141
+ data[i + 3] = Math.round(srcAlpha * maskAlpha * 255);
37142
+ }
37143
+ context.putImageData(imageData, 0, 0);
37144
+ }
37145
+ return canvas.toDataURL("image/png");
37146
+ }
37147
+ function loadImage(src) {
37148
+ return new Promise((res, rej) => {
37149
+ const image = new Image();
37150
+ image.onload = () => res(image);
37151
+ image.onerror = (e) => rej(e);
37152
+ image.src = src;
37153
+ });
37154
+ }
37015
37155
  /**
37016
37156
  * Compares two color materials.
37017
37157
  * Returns if they are equal.
@@ -38297,7 +38437,7 @@
38297
38437
  StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
38298
38438
  })(exports.StyleUtils || (exports.StyleUtils = {}));
38299
38439
 
38300
- const VERSION = "6.9.1";
38440
+ const VERSION = "6.9.3";
38301
38441
  /**
38302
38442
  * Updates the environment instance used by bruce-cesium to one specified.
38303
38443
  * This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.