bruce-cesium 7.5.8 → 7.6.0
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.
- package/dist/bruce-cesium.es5.js +967 -286
- package/dist/bruce-cesium.es5.js.map +1 -1
- package/dist/bruce-cesium.umd.js +965 -284
- package/dist/bruce-cesium.umd.js.map +1 -1
- package/dist/lib/bruce-cesium.js +1 -1
- package/dist/lib/rendering/displaced-surface-primitive.js +187 -36
- package/dist/lib/rendering/displaced-surface-primitive.js.map +1 -1
- package/dist/lib/rendering/entity-render-engine-polygon.js +20 -9
- package/dist/lib/rendering/entity-render-engine-polygon.js.map +1 -1
- package/dist/lib/rendering/render-managers/common/entity-label.js +31 -13
- package/dist/lib/rendering/render-managers/common/entity-label.js.map +1 -1
- package/dist/lib/rendering/texture-frame-series-animator.js +220 -20
- package/dist/lib/rendering/texture-frame-series-animator.js.map +1 -1
- package/dist/lib/rendering/texture-value-labels.js +478 -194
- package/dist/lib/rendering/texture-value-labels.js.map +1 -1
- package/dist/lib/rendering/visuals-register.js +27 -9
- package/dist/lib/rendering/visuals-register.js.map +1 -1
- package/dist/types/bruce-cesium.d.ts +1 -1
- package/dist/types/rendering/displaced-surface-primitive.d.ts +32 -0
- package/dist/types/rendering/entity-render-engine-polygon.d.ts +1 -0
- package/dist/types/rendering/texture-frame-series-animator.d.ts +42 -0
- package/dist/types/rendering/texture-value-labels.d.ts +59 -11
- package/dist/types/rendering/visuals-register.d.ts +6 -0
- package/package.json +1 -1
package/dist/bruce-cesium.umd.js
CHANGED
|
@@ -7295,7 +7295,8 @@
|
|
|
7295
7295
|
this.groupId = groupId;
|
|
7296
7296
|
this.additionalTexts = this.options.text != null ? [] : null;
|
|
7297
7297
|
this.photo = null;
|
|
7298
|
-
|
|
7298
|
+
// A label given its position keeps it, or the rebuild goes looking for the entity's own.
|
|
7299
|
+
this.createLabel(this.prePositioned ? this._pos3d : undefined);
|
|
7299
7300
|
}
|
|
7300
7301
|
/**
|
|
7301
7302
|
* Creates cesium entity for the label if one does not exist.
|
|
@@ -7887,6 +7888,22 @@
|
|
|
7887
7888
|
radius: Math.max(width, height) / 2
|
|
7888
7889
|
};
|
|
7889
7890
|
};
|
|
7891
|
+
/*
|
|
7892
|
+
* The on-screen centre and half-size of a model too small for its box, so the box is ringed like a glyph.
|
|
7893
|
+
*/
|
|
7894
|
+
const measureModel = (anchor) => {
|
|
7895
|
+
const hull = layer.modelBoxes.Hull(this.id);
|
|
7896
|
+
if (!(hull === null || hull === void 0 ? void 0 : hull.length)) {
|
|
7897
|
+
return null;
|
|
7898
|
+
}
|
|
7899
|
+
const xs = hull.map(point => point.x), ys = hull.map(point => point.y);
|
|
7900
|
+
const left = Math.min(...xs), top = Math.min(...ys);
|
|
7901
|
+
const width = Math.max(...xs) - left, height = Math.max(...ys) - top;
|
|
7902
|
+
if (Math.max(width, height) >= MODEL_BOX_MIN_SIZE) {
|
|
7903
|
+
return null;
|
|
7904
|
+
}
|
|
7905
|
+
return { x: left + width / 2 - anchor.x, y: top + height / 2 - anchor.y, radius: Math.max(width, height) / 2 };
|
|
7906
|
+
};
|
|
7890
7907
|
// Only CAD tilesets and model Entities are assembly parts with a geometry box; anything else stays a point.
|
|
7891
7908
|
const boxable = Boolean(normalLeader && ((_r = this.rego) === null || _r === void 0 ? void 0 : _r.entityId) && ((visual instanceof Cesium.Cesium3DTileFeature && this.rego.tilesetType === BModels.Tileset.EType.Cad) ||
|
|
7892
7909
|
(visual instanceof Cesium.Entity && Boolean(visual.model))));
|
|
@@ -7945,7 +7962,7 @@
|
|
|
7945
7962
|
}
|
|
7946
7963
|
let cursorMotion = this.cursorMotion;
|
|
7947
7964
|
const project = () => {
|
|
7948
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
|
|
7965
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
7949
7966
|
if (this.disposed || !this.viewer || this.viewer.isDestroyed() || ((_a = this.visible) === null || _a === void 0 ? void 0 : _a.call(this)) === false ||
|
|
7950
7967
|
(presentation === "icon" && this.options.iconScale === 0)) {
|
|
7951
7968
|
return null;
|
|
@@ -7988,7 +8005,8 @@
|
|
|
7988
8005
|
const boxBlend = this.morphToBox(boxed, shownOnce ? (_f = (_e = this.options.anchorRing) === null || _e === void 0 ? void 0 : _e.transitionDuration) !== null && _f !== void 0 ? _f : 200 : 0);
|
|
7989
8006
|
// Eased between the ring and where the stem meets the box, so gaining or losing a box never jumps.
|
|
7990
8007
|
const boxTarget = this.boxMorph.anchor;
|
|
7991
|
-
const
|
|
8008
|
+
const model = ((_g = this.modelBox) === null || _g === void 0 ? void 0 : _g.corners) ? measureModel(anchor) : null;
|
|
8009
|
+
const ringPoint = { x: anchor.x + (model || pixelOffset).x, y: anchor.y + (model || pixelOffset).y };
|
|
7992
8010
|
const boxAnchor = boxBlend > 0 && boxTarget ? {
|
|
7993
8011
|
x: ringPoint.x + (boxTarget.x - ringPoint.x) * boxBlend,
|
|
7994
8012
|
y: ringPoint.y + (boxTarget.y - ringPoint.y) * boxBlend
|
|
@@ -7999,13 +8017,13 @@
|
|
|
7999
8017
|
return null;
|
|
8000
8018
|
if (onScreen && !this.imageAsked && this.groupId && !HasIcon(this.options) && !this.options.photo) {
|
|
8001
8019
|
this.imageAsked = true;
|
|
8002
|
-
(
|
|
8020
|
+
(_h = this.visualRegister) === null || _h === void 0 ? void 0 : _h.RequestLabelImage((_j = this.rego) === null || _j === void 0 ? void 0 : _j.entityId);
|
|
8003
8021
|
}
|
|
8004
8022
|
const transientPivot = (this.Transient && !this.Persistent ?
|
|
8005
|
-
(this.hoverFade.to === 0 ? this.retainedHoverPosition : (
|
|
8023
|
+
(this.hoverFade.to === 0 ? this.retainedHoverPosition : (_k = this.visualRegister) === null || _k === void 0 ? void 0 : _k.GetLabelHoverPosition((_l = this.rego) === null || _l === void 0 ? void 0 : _l.entityId, (_m = this.rego) === null || _m === void 0 ? void 0 : _m.menuItemId)) : undefined);
|
|
8006
8024
|
const hover = (presentation !== "icon" ?
|
|
8007
8025
|
(this.Transient && !this.Persistent && this.hoverFade.to === 0 ? this.retainedHoverPosition :
|
|
8008
|
-
(
|
|
8026
|
+
(_o = this.visualRegister) === null || _o === void 0 ? void 0 : _o.GetLabelHoverPosition((_p = this.rego) === null || _p === void 0 ? void 0 : _p.entityId, (_q = this.rego) === null || _q === void 0 ? void 0 : _q.menuItemId)) : undefined);
|
|
8009
8027
|
const reused = !this.Transient || this.Persistent;
|
|
8010
8028
|
const now = Date.now();
|
|
8011
8029
|
const animateCursor = () => {
|
|
@@ -8029,13 +8047,13 @@
|
|
|
8029
8047
|
const cursorAmount = cursorMotion.amount;
|
|
8030
8048
|
leader = normalLeader || cursorAmount > 0;
|
|
8031
8049
|
// An icon keeps its callout centred on the glyph rather than following the cursor across it.
|
|
8032
|
-
const icon = boxAnchor ? null : measureIcon();
|
|
8050
|
+
const icon = boxAnchor ? null : measureIcon() || model;
|
|
8033
8051
|
const iconRing = icon ? Math.max(ICON_RING_MIN, icon.radius + ICON_RING_PADDING) : 0;
|
|
8034
8052
|
// A boxed model is pinned the same way, with its stem leaving the top of the box outline.
|
|
8035
8053
|
const pinned = Boolean(icon || boxAnchor);
|
|
8036
8054
|
const pivot = pinned ? undefined : transientPivot;
|
|
8037
|
-
let x = boxAnchor ? boxAnchor.x : ((
|
|
8038
|
-
let y = boxAnchor ? boxAnchor.y : ((
|
|
8055
|
+
let x = boxAnchor ? boxAnchor.x : ((_r = pivot === null || pivot === void 0 ? void 0 : pivot.x) !== null && _r !== void 0 ? _r : anchor.x) + (icon || pixelOffset).x;
|
|
8056
|
+
let y = boxAnchor ? boxAnchor.y : ((_s = pivot === null || pivot === void 0 ? void 0 : pivot.y) !== null && _s !== void 0 ? _s : anchor.y) + (icon || pixelOffset).y;
|
|
8039
8057
|
if (cursorAmount > 0 && !pinned) {
|
|
8040
8058
|
// A leader-less label starts centred on its anchor; preserve that box as it lifts away.
|
|
8041
8059
|
if (!normalLeader) {
|
|
@@ -8090,7 +8108,7 @@
|
|
|
8090
8108
|
const width = ele.offsetWidth / coordinates.scaleX;
|
|
8091
8109
|
const height = ele.offsetHeight / coordinates.scaleY;
|
|
8092
8110
|
let clusterBounds;
|
|
8093
|
-
if ((
|
|
8111
|
+
if ((_t = this.options.cluster) === null || _t === void 0 ? void 0 : _t.enabled) {
|
|
8094
8112
|
const textWidth = (sourceText === null || sourceText === void 0 ? void 0 : sourceText.offsetWidth) || 0;
|
|
8095
8113
|
const textHeight = (sourceText === null || sourceText === void 0 ? void 0 : sourceText.offsetHeight) || 0;
|
|
8096
8114
|
// A photo is never part of the footprint, so its arrival cannot change which cluster a label joins.
|
|
@@ -8124,7 +8142,7 @@
|
|
|
8124
8142
|
if (!leader && (bounds.x + bounds.width < 0 || bounds.y + bounds.height < 0 || bounds.x > this.viewer.canvas.clientWidth || bounds.y > this.viewer.canvas.clientHeight)) {
|
|
8125
8143
|
return null;
|
|
8126
8144
|
}
|
|
8127
|
-
const state = (
|
|
8145
|
+
const state = (_u = this.visualRegister) === null || _u === void 0 ? void 0 : _u.GetState({ entityId: (_v = this.rego) === null || _v === void 0 ? void 0 : _v.entityId, menuItemId: (_w = this.rego) === null || _w === void 0 ? void 0 : _w.menuItemId });
|
|
8128
8146
|
const opacity = this.prePositioned || hoverOnly || MAX_DISTANCE === Infinity ? 1 : 1 - distance / MAX_DISTANCE;
|
|
8129
8147
|
return {
|
|
8130
8148
|
canFlip: leader && presentation !== "icon", hasLeader: leader,
|
|
@@ -8132,8 +8150,8 @@
|
|
|
8132
8150
|
transient: this.Transient || Boolean(hover) || cursorAmount > 0,
|
|
8133
8151
|
extendedGeometry: visual instanceof Cesium.Entity && Boolean(visual.polygon || visual.polyline || visual.corridor),
|
|
8134
8152
|
viewportWidth: this.viewer.canvas.clientWidth,
|
|
8135
|
-
id: this.id, entityKey: ((
|
|
8136
|
-
entityId: (
|
|
8153
|
+
id: this.id, entityKey: ((_x = this.rego) === null || _x === void 0 ? void 0 : _x.entityId) ? JSON.stringify([this.rego.entityId, this.rego.menuItemId || "", this.Transient && !this.Persistent ? "hover" : ""]) : undefined,
|
|
8154
|
+
entityId: (_y = this.rego) === null || _y === void 0 ? void 0 : _y.entityId, group: this.groupId, x, y, width, height, anchorX, anchorY, depth, leaderLength, leaderRun, leaderSide, distance,
|
|
8137
8155
|
iconRadius: icon === null || icon === void 0 ? void 0 : icon.radius, modelBox: boxed || undefined, boxBlend,
|
|
8138
8156
|
// Priority shows the hovered member individually without dragging its cluster along.
|
|
8139
8157
|
opacity: waiting ? 0 : opacity, priority: hover || cursorAmount > 0 || (state === null || state === void 0 ? void 0 : state.highlighted) ? 1 : 0, options: this.options, text: originalText, clusterBounds
|
|
@@ -13789,7 +13807,9 @@
|
|
|
13789
13807
|
entityId: params.entityId,
|
|
13790
13808
|
menuItemId: params.menuItemId,
|
|
13791
13809
|
position: params.position ? { ...params.position } : undefined,
|
|
13792
|
-
createIfMissing: params.createIfMissing !== false
|
|
13810
|
+
createIfMissing: params.createIfMissing !== false,
|
|
13811
|
+
text: params.text,
|
|
13812
|
+
pos3d: params.pos3d ? Cesium.Cartesian3.clone(params.pos3d) : undefined
|
|
13793
13813
|
};
|
|
13794
13814
|
const doc = this.viewer.container.ownerDocument;
|
|
13795
13815
|
// Only a label following the cursor should go when the cursor leaves the canvas.
|
|
@@ -13805,6 +13825,13 @@
|
|
|
13805
13825
|
this.SyncLabelHover();
|
|
13806
13826
|
this.viewer.scene.requestRender();
|
|
13807
13827
|
}
|
|
13828
|
+
/*
|
|
13829
|
+
* Which Entity holds the label hover, if any, so a caller can leave another's hover alone.
|
|
13830
|
+
*/
|
|
13831
|
+
GetLabelHoverTarget() {
|
|
13832
|
+
const hover = this.labelHover;
|
|
13833
|
+
return hover ? { entityId: hover.entityId, menuItemId: hover.menuItemId } : undefined;
|
|
13834
|
+
}
|
|
13808
13835
|
/*
|
|
13809
13836
|
* Whether the label hover is on this Entity, with or without a cursor position.
|
|
13810
13837
|
*/
|
|
@@ -13817,7 +13844,7 @@
|
|
|
13817
13844
|
return (hover === null || hover === void 0 ? void 0 : hover.entityId) === entityId && (hover.menuItemId == null || hover.menuItemId === menuItemId) ? hover.position : undefined;
|
|
13818
13845
|
}
|
|
13819
13846
|
SyncLabelHover() {
|
|
13820
|
-
var _a, _b, _c;
|
|
13847
|
+
var _a, _b, _c, _d;
|
|
13821
13848
|
const hover = this.labelHover;
|
|
13822
13849
|
if (!hover || this.disposed) {
|
|
13823
13850
|
return;
|
|
@@ -13849,12 +13876,24 @@
|
|
|
13849
13876
|
this.ReleaseHoverLabel(true);
|
|
13850
13877
|
return;
|
|
13851
13878
|
}
|
|
13879
|
+
const hoverOptions = {
|
|
13880
|
+
presentation: "text",
|
|
13881
|
+
text: (_b = hover.text) !== null && _b !== void 0 ? _b : group === null || group === void 0 ? void 0 : group.options.text,
|
|
13882
|
+
selectOnClick: false
|
|
13883
|
+
};
|
|
13852
13884
|
if (this.hoverLabel && !this.hoverLabel.Disposed) {
|
|
13885
|
+
// Rebuilds only when the text changed, as it does when the pointer crosses a cell.
|
|
13886
|
+
if (hover.pos3d && !Cesium.Cartesian3.equals(hover.pos3d, this.hoverLabel.pos3d)) {
|
|
13887
|
+
this.hoverLabel.pos3d = hover.pos3d;
|
|
13888
|
+
}
|
|
13889
|
+
if (hover.text != null) {
|
|
13890
|
+
this.hoverLabel.Configure(hoverOptions);
|
|
13891
|
+
}
|
|
13853
13892
|
this.hoverLabel.SetHoverVisible(true);
|
|
13854
13893
|
return;
|
|
13855
13894
|
}
|
|
13856
13895
|
// Query-only icon groups have coordinates but no registered scene visual to attach to.
|
|
13857
|
-
if (!rego && group && [group.item.longitude, group.item.latitude, (
|
|
13896
|
+
if (!rego && group && [group.item.longitude, group.item.latitude, (_c = group.item.altitude) !== null && _c !== void 0 ? _c : 0].every(Number.isFinite)) {
|
|
13858
13897
|
rego = {
|
|
13859
13898
|
entityId: hover.entityId,
|
|
13860
13899
|
menuItemId: hover.menuItemId,
|
|
@@ -13876,15 +13915,12 @@
|
|
|
13876
13915
|
rego,
|
|
13877
13916
|
viewer: this.viewer,
|
|
13878
13917
|
visualRegister: this,
|
|
13879
|
-
api: (
|
|
13918
|
+
api: (_d = this.ApiGetters) === null || _d === void 0 ? void 0 : _d.GetBruceApi(),
|
|
13880
13919
|
persistent: false,
|
|
13881
13920
|
transient: true,
|
|
13882
13921
|
hoverRingState: this.hoverRingState,
|
|
13883
|
-
|
|
13884
|
-
|
|
13885
|
-
text: group === null || group === void 0 ? void 0 : group.options.text,
|
|
13886
|
-
selectOnClick: false
|
|
13887
|
-
},
|
|
13922
|
+
pos3d: hover.pos3d,
|
|
13923
|
+
options: hoverOptions,
|
|
13888
13924
|
visible: () => {
|
|
13889
13925
|
var _a;
|
|
13890
13926
|
const current = this.GetState(hover);
|
|
@@ -46708,6 +46744,10 @@
|
|
|
46708
46744
|
TextureFrameSeriesAnimator.AppearanceSignature = AppearanceSignature;
|
|
46709
46745
|
const MAX_COMPOSITE_TEXELS = 2048;
|
|
46710
46746
|
const VALUE_DILATE_PASSES = 2;
|
|
46747
|
+
// Decoded frame bytes kept at once. A 2048 square composite is 16 MB, so a long series cannot all stay.
|
|
46748
|
+
const MAX_CACHED_FRAME_BYTES = 256 * 1024 * 1024;
|
|
46749
|
+
// Fetched bytes kept for decoding again, a small fraction of what they decode to.
|
|
46750
|
+
const MAX_RAW_FRAME_BYTES = 96 * 1024 * 1024;
|
|
46711
46751
|
/*
|
|
46712
46752
|
* Rewrites a packed 16 bit value as the 8 bit grey the colour ramps index on.
|
|
46713
46753
|
*
|
|
@@ -46728,19 +46768,33 @@
|
|
|
46728
46768
|
/*
|
|
46729
46769
|
* Bleeds covered values outward into uncovered texels, leaving alpha untouched.
|
|
46730
46770
|
*/
|
|
46731
|
-
function
|
|
46771
|
+
function DilateValues(value, width, height, passes, packed) {
|
|
46732
46772
|
const texels = width * height;
|
|
46733
|
-
|
|
46773
|
+
// 255 for a texel that had data to begin with, otherwise the 1 based pass that filled it, so a pass
|
|
46774
|
+
// reads only texels filled before it and can work in place.
|
|
46775
|
+
const stamp = new Uint8Array(texels);
|
|
46734
46776
|
for (let p = 0; p < texels; p++) {
|
|
46735
|
-
|
|
46777
|
+
stamp[p] = value[p * 4 + 3] !== 0 ? 255 : 0;
|
|
46736
46778
|
}
|
|
46779
|
+
// Whether a texel or a horizontal neighbour had data, so empty space is skipped in three reads.
|
|
46780
|
+
const across = new Uint8Array(texels);
|
|
46737
46781
|
for (let pass = 0; pass < passes; pass++) {
|
|
46738
|
-
const
|
|
46739
|
-
const
|
|
46782
|
+
const current = pass + 1;
|
|
46783
|
+
const had = (p) => stamp[p] !== 0 && stamp[p] !== current;
|
|
46784
|
+
for (let y = 0; y < height; y++) {
|
|
46785
|
+
const row = y * width;
|
|
46786
|
+
for (let x = 0; x < width; x++) {
|
|
46787
|
+
const p = row + x;
|
|
46788
|
+
across[p] = (had(p) || (x > 0 && had(p - 1)) || (x + 1 < width && had(p + 1))) ? 1 : 0;
|
|
46789
|
+
}
|
|
46790
|
+
}
|
|
46740
46791
|
for (let y = 0; y < height; y++) {
|
|
46741
46792
|
for (let x = 0; x < width; x++) {
|
|
46742
46793
|
const p = y * width + x;
|
|
46743
|
-
if (
|
|
46794
|
+
if (stamp[p] !== 0) {
|
|
46795
|
+
continue;
|
|
46796
|
+
}
|
|
46797
|
+
if (!across[p] && !(y > 0 && across[p - width]) && !(y + 1 < height && across[p + width])) {
|
|
46744
46798
|
continue;
|
|
46745
46799
|
}
|
|
46746
46800
|
let sum = 0;
|
|
@@ -46749,13 +46803,13 @@
|
|
|
46749
46803
|
for (let dx = -1; dx <= 1; dx++) {
|
|
46750
46804
|
const nx = x + dx;
|
|
46751
46805
|
const ny = y + dy;
|
|
46752
|
-
if (nx < 0 || ny < 0 || nx >= width || ny >= height || !
|
|
46806
|
+
if (nx < 0 || ny < 0 || nx >= width || ny >= height || !had(ny * width + nx)) {
|
|
46753
46807
|
continue;
|
|
46754
46808
|
}
|
|
46755
46809
|
const at = (ny * width + nx) * 4;
|
|
46756
46810
|
// Averaged as a value rather than per byte: averaging a packed low byte
|
|
46757
46811
|
// on its own wraps at every 256th step and speckles the bled edge.
|
|
46758
|
-
sum += packed ?
|
|
46812
|
+
sum += packed ? value[at] * 256 + value[at + 1] : value[at];
|
|
46759
46813
|
hits++;
|
|
46760
46814
|
}
|
|
46761
46815
|
}
|
|
@@ -46771,18 +46825,25 @@
|
|
|
46771
46825
|
value[p * 4 + 1] = v;
|
|
46772
46826
|
value[p * 4 + 2] = v;
|
|
46773
46827
|
}
|
|
46774
|
-
|
|
46828
|
+
stamp[p] = current;
|
|
46775
46829
|
}
|
|
46776
46830
|
}
|
|
46777
46831
|
}
|
|
46778
46832
|
}
|
|
46779
46833
|
}
|
|
46834
|
+
TextureFrameSeriesAnimator.DilateValues = DilateValues;
|
|
46835
|
+
/*
|
|
46836
|
+
* Bytes one cached frame holds, counting a buffer shared between colour and value once.
|
|
46837
|
+
*/
|
|
46838
|
+
function frameBytes(pixels, value) {
|
|
46839
|
+
return (pixels ? pixels.byteLength : 0) + (value && value !== pixels ? value.byteLength : 0);
|
|
46840
|
+
}
|
|
46780
46841
|
const DEFAULT_CROSSFADE_MS = 900;
|
|
46781
46842
|
const DEFAULT_LOW_COLOR = { red: 255, green: 255, blue: 255, alpha: 0.12 };
|
|
46782
46843
|
const DEFAULT_HIGH_COLOR = { red: 21, green: 96, blue: 196, alpha: 0.92 };
|
|
46783
46844
|
class Animator {
|
|
46784
46845
|
constructor(options) {
|
|
46785
|
-
var _a, _b, _c;
|
|
46846
|
+
var _a, _b, _c, _d;
|
|
46786
46847
|
this.removeOnTick = null;
|
|
46787
46848
|
this.removeCrossfadeTick = null;
|
|
46788
46849
|
this.disposed = false;
|
|
@@ -46813,6 +46874,15 @@
|
|
|
46813
46874
|
this.extremesLoad = null;
|
|
46814
46875
|
this.floorPixels = null;
|
|
46815
46876
|
this.ceilingPixels = null;
|
|
46877
|
+
// How far through the fade from the previous value frame to the presented one, 1 when settled.
|
|
46878
|
+
this.fadeMix = 1;
|
|
46879
|
+
// Where the current fade started from, above zero when it took over from one still running.
|
|
46880
|
+
this.fadeMixStart = 0;
|
|
46881
|
+
// Version the value canvas was last written at, since in value only mode it is written on demand.
|
|
46882
|
+
this.valueCanvasVersion = -1;
|
|
46883
|
+
this.rawBytes = 0;
|
|
46884
|
+
this.cachedBytes = 0;
|
|
46885
|
+
this.lastFrameBytes = 0;
|
|
46816
46886
|
if (!options.entity.polygon) {
|
|
46817
46887
|
throw new Error("TextureFrameSeriesAnimator requires an entity with polygon graphics.");
|
|
46818
46888
|
}
|
|
@@ -46860,6 +46930,9 @@
|
|
|
46860
46930
|
this.maskBaseline = (_c = options.maskBaseline) !== null && _c !== void 0 ? _c : this.tiles.some((tile) => Boolean(tile.BaselineMask));
|
|
46861
46931
|
this.valueCanvas = this.produceValueCanvas ? document.createElement("canvas") : null;
|
|
46862
46932
|
this.driveMaterial = options.driveMaterial !== false;
|
|
46933
|
+
this.valueOnly = this.produceValueCanvas && !this.driveMaterial;
|
|
46934
|
+
this.rawCache = new Array(this.frames.length).fill(null);
|
|
46935
|
+
this.cacheBudget = (_d = options.cacheBytes) !== null && _d !== void 0 ? _d : MAX_CACHED_FRAME_BYTES;
|
|
46863
46936
|
this.originalMaterial = options.entity.polygon.material;
|
|
46864
46937
|
this.imageProperty = new Cesium.CallbackProperty(() => this.pool[this.poolIdx], false);
|
|
46865
46938
|
if (this.driveMaterial) {
|
|
@@ -46925,7 +46998,40 @@
|
|
|
46925
46998
|
* or null unless the instance was constructed with produceValueCanvas.
|
|
46926
46999
|
*/
|
|
46927
47000
|
GetValueCanvas() {
|
|
46928
|
-
|
|
47001
|
+
if (!this.valueDims) {
|
|
47002
|
+
return null;
|
|
47003
|
+
}
|
|
47004
|
+
if (this.valueOnly && this.valueCanvasVersion !== this.presentVersion && this.displayedValuePixels) {
|
|
47005
|
+
this.writeInto(this.valueCanvas, this.displayedValuePixels, this.valueDims);
|
|
47006
|
+
this.valueCanvasVersion = this.presentVersion;
|
|
47007
|
+
}
|
|
47008
|
+
return this.valueCanvas;
|
|
47009
|
+
}
|
|
47010
|
+
/**
|
|
47011
|
+
* The presented frame's value pixels, or null before one. Shares the cached array, so never write to it.
|
|
47012
|
+
*/
|
|
47013
|
+
GetValueRaster() {
|
|
47014
|
+
if (!this.valueDims || !this.displayedValuePixels) {
|
|
47015
|
+
return null;
|
|
47016
|
+
}
|
|
47017
|
+
return { data: this.displayedValuePixels, width: this.valueDims.width, height: this.valueDims.height };
|
|
47018
|
+
}
|
|
47019
|
+
/**
|
|
47020
|
+
* The two value frames being faded between and the mix, or null unless the material is not driven.
|
|
47021
|
+
*/
|
|
47022
|
+
GetValueFade() {
|
|
47023
|
+
if (!this.valueOnly || !this.valueDims || !this.displayedValuePixels) {
|
|
47024
|
+
return null;
|
|
47025
|
+
}
|
|
47026
|
+
const previous = this.fadeFromValuePixels;
|
|
47027
|
+
const from = previous && previous.length === this.displayedValuePixels.length ? previous : null;
|
|
47028
|
+
return {
|
|
47029
|
+
from,
|
|
47030
|
+
to: this.displayedValuePixels,
|
|
47031
|
+
width: this.valueDims.width,
|
|
47032
|
+
height: this.valueDims.height,
|
|
47033
|
+
mix: from ? this.fadeMix : 1
|
|
47034
|
+
};
|
|
46929
47035
|
}
|
|
46930
47036
|
/**
|
|
46931
47037
|
* Increments every time a frame is presented, so a consumer holding the value canvas can tell
|
|
@@ -46992,7 +47098,11 @@
|
|
|
46992
47098
|
if (this.currentFrameIndex < 0) {
|
|
46993
47099
|
return this.frameCache[0] ? -1 : 0;
|
|
46994
47100
|
}
|
|
46995
|
-
|
|
47101
|
+
// Full once another frame will not fit, so a prefetch only displaces a frame further from the
|
|
47102
|
+
// playhead than itself and never trades the two ends of the window back and forth.
|
|
47103
|
+
const full = this.cachedBytes + this.lastFrameBytes > this.cacheBudget;
|
|
47104
|
+
const reach = full ? this.farthestCachedDistance() : n;
|
|
47105
|
+
for (let d = 0; d < Math.min(n, reach); d++) {
|
|
46996
47106
|
const forward = this.currentFrameIndex + d;
|
|
46997
47107
|
if (forward < n && !this.frameCache[forward]) {
|
|
46998
47108
|
return forward;
|
|
@@ -47004,6 +47114,68 @@
|
|
|
47004
47114
|
}
|
|
47005
47115
|
return -1;
|
|
47006
47116
|
}
|
|
47117
|
+
farthestCachedDistance() {
|
|
47118
|
+
let farthest = 0;
|
|
47119
|
+
for (let i = 0; i < this.frameCache.length; i++) {
|
|
47120
|
+
if (this.frameCache[i]) {
|
|
47121
|
+
farthest = Math.max(farthest, Math.abs(i - this.currentFrameIndex));
|
|
47122
|
+
}
|
|
47123
|
+
}
|
|
47124
|
+
return farthest;
|
|
47125
|
+
}
|
|
47126
|
+
/*
|
|
47127
|
+
* Drops decoded frames furthest from the playhead until the cache fits its budget again.
|
|
47128
|
+
*/
|
|
47129
|
+
evictDecoded(keep) {
|
|
47130
|
+
// A tinted fade reads its target from the cache each tick, where a value fade holds the arrays.
|
|
47131
|
+
const fading = !this.valueOnly && this.fadeStartMs !== null ? this.fadeToIndex : -1;
|
|
47132
|
+
while (this.cachedBytes > this.cacheBudget) {
|
|
47133
|
+
let victim = -1;
|
|
47134
|
+
let distance = -1;
|
|
47135
|
+
for (let i = 0; i < this.frameCache.length; i++) {
|
|
47136
|
+
if (!this.frameCache[i] || i === keep || i === this.currentFrameIndex || i === fading) {
|
|
47137
|
+
continue;
|
|
47138
|
+
}
|
|
47139
|
+
const d = Math.abs(i - this.currentFrameIndex);
|
|
47140
|
+
if (d > distance) {
|
|
47141
|
+
distance = d;
|
|
47142
|
+
victim = i;
|
|
47143
|
+
}
|
|
47144
|
+
}
|
|
47145
|
+
if (victim < 0) {
|
|
47146
|
+
return;
|
|
47147
|
+
}
|
|
47148
|
+
this.cachedBytes -= frameBytes(this.frameCache[victim], this.valueCache[victim]);
|
|
47149
|
+
this.frameCache[victim] = null;
|
|
47150
|
+
this.valueCache[victim] = null;
|
|
47151
|
+
}
|
|
47152
|
+
}
|
|
47153
|
+
/*
|
|
47154
|
+
* Keeps a frame's fetched bytes unless they are large, as a tile major archive's are.
|
|
47155
|
+
*/
|
|
47156
|
+
keepRaw(idx, buffer, base) {
|
|
47157
|
+
if (buffer.byteLength > MAX_RAW_FRAME_BYTES / 16) {
|
|
47158
|
+
return;
|
|
47159
|
+
}
|
|
47160
|
+
this.rawCache[idx] = { buffer, base };
|
|
47161
|
+
this.rawBytes += buffer.byteLength;
|
|
47162
|
+
while (this.rawBytes > MAX_RAW_FRAME_BYTES) {
|
|
47163
|
+
let victim = -1;
|
|
47164
|
+
let distance = -1;
|
|
47165
|
+
for (let i = 0; i < this.rawCache.length; i++) {
|
|
47166
|
+
const d = Math.abs(i - this.currentFrameIndex);
|
|
47167
|
+
if (this.rawCache[i] && i !== idx && d > distance) {
|
|
47168
|
+
distance = d;
|
|
47169
|
+
victim = i;
|
|
47170
|
+
}
|
|
47171
|
+
}
|
|
47172
|
+
if (victim < 0) {
|
|
47173
|
+
return;
|
|
47174
|
+
}
|
|
47175
|
+
this.rawBytes -= this.rawCache[victim].buffer.byteLength;
|
|
47176
|
+
this.rawCache[victim] = null;
|
|
47177
|
+
}
|
|
47178
|
+
}
|
|
47007
47179
|
pumpFetchQueue() {
|
|
47008
47180
|
if (this.disposed || this.inFlightIndex !== -1) {
|
|
47009
47181
|
return;
|
|
@@ -47047,10 +47219,18 @@
|
|
|
47047
47219
|
lo = Math.min(lo, at.ByteOffset);
|
|
47048
47220
|
hi = Math.max(hi, at.ByteOffset + at.ByteLength);
|
|
47049
47221
|
}
|
|
47050
|
-
|
|
47051
|
-
const
|
|
47052
|
-
if (
|
|
47053
|
-
|
|
47222
|
+
let buffer;
|
|
47223
|
+
const raw = this.rawCache[idx];
|
|
47224
|
+
if (raw && raw.base === lo) {
|
|
47225
|
+
buffer = raw.buffer;
|
|
47226
|
+
}
|
|
47227
|
+
else {
|
|
47228
|
+
const response = await fetch(this.archiveUrl, { headers: { Range: `bytes=${lo}-${hi - 1}` } });
|
|
47229
|
+
buffer = await response.arrayBuffer();
|
|
47230
|
+
if (this.disposed) {
|
|
47231
|
+
return;
|
|
47232
|
+
}
|
|
47233
|
+
this.keepRaw(idx, buffer, lo);
|
|
47054
47234
|
}
|
|
47055
47235
|
// Awaited before decoding rather than applied later, so no frame is ever cached unmasked and
|
|
47056
47236
|
// then corrected once the mask lands.
|
|
@@ -47062,9 +47242,15 @@
|
|
|
47062
47242
|
if (this.disposed) {
|
|
47063
47243
|
return;
|
|
47064
47244
|
}
|
|
47245
|
+
if (this.frameCache[idx]) {
|
|
47246
|
+
this.cachedBytes -= frameBytes(this.frameCache[idx], this.valueCache[idx]);
|
|
47247
|
+
}
|
|
47065
47248
|
this.frameCache[idx] = decoded.pixels;
|
|
47066
47249
|
this.frameDims[idx] = { width: decoded.width, height: decoded.height };
|
|
47067
47250
|
this.valueCache[idx] = decoded.valuePixels || null;
|
|
47251
|
+
this.lastFrameBytes = frameBytes(decoded.pixels, decoded.valuePixels || null);
|
|
47252
|
+
this.cachedBytes += this.lastFrameBytes;
|
|
47253
|
+
this.evictDecoded(idx);
|
|
47068
47254
|
if (this.shouldPresent(idx, isTarget)) {
|
|
47069
47255
|
this.beginCrossfadeTo(idx);
|
|
47070
47256
|
}
|
|
@@ -47134,6 +47320,13 @@
|
|
|
47134
47320
|
*/
|
|
47135
47321
|
tint(imageData) {
|
|
47136
47322
|
const canvas = { width: imageData.width, height: imageData.height };
|
|
47323
|
+
if (this.valueOnly) {
|
|
47324
|
+
// One array serves as both, since nothing will ever display the colour.
|
|
47325
|
+
const values = imageData.data;
|
|
47326
|
+
this.applyBaselineMask(values, undefined, canvas.width, canvas.height);
|
|
47327
|
+
DilateValues(values, canvas.width, canvas.height, VALUE_DILATE_PASSES, IsPackedValue(this.metadata));
|
|
47328
|
+
return { pixels: values, width: canvas.width, height: canvas.height, valuePixels: values };
|
|
47329
|
+
}
|
|
47137
47330
|
// Copied before tinting: the ramp is a lerp between two colours, so the value cannot be
|
|
47138
47331
|
// recovered from the tinted pixels afterwards.
|
|
47139
47332
|
const valuePixels = this.produceValueCanvas ? new Uint8ClampedArray(imageData.data) : undefined;
|
|
@@ -47154,7 +47347,7 @@
|
|
|
47154
47347
|
}
|
|
47155
47348
|
this.applyBaselineMask(imageData.data, valuePixels, canvas.width, canvas.height);
|
|
47156
47349
|
if (valuePixels) {
|
|
47157
|
-
|
|
47350
|
+
DilateValues(valuePixels, canvas.width, canvas.height, VALUE_DILATE_PASSES, IsPackedValue(this.metadata));
|
|
47158
47351
|
}
|
|
47159
47352
|
return { pixels: imageData.data, width: canvas.width, height: canvas.height, valuePixels };
|
|
47160
47353
|
}
|
|
@@ -47412,6 +47605,10 @@
|
|
|
47412
47605
|
}
|
|
47413
47606
|
}
|
|
47414
47607
|
beginCrossfadeTo(idx) {
|
|
47608
|
+
if (this.valueOnly) {
|
|
47609
|
+
this.beginValueFadeTo(idx);
|
|
47610
|
+
return;
|
|
47611
|
+
}
|
|
47415
47612
|
const to = this.frameCache[idx];
|
|
47416
47613
|
this.fadeFromValuePixels = this.displayedValuePixels === null && this.valueCache[idx]
|
|
47417
47614
|
? new Uint8ClampedArray(this.valueCache[idx].length)
|
|
@@ -47421,12 +47618,53 @@
|
|
|
47421
47618
|
this.fadeToDims = this.frameDims[idx];
|
|
47422
47619
|
this.fadeStartMs = Now();
|
|
47423
47620
|
}
|
|
47621
|
+
/*
|
|
47622
|
+
* Presents a value frame at once and leaves the fade to the consumer, which blends on the GPU.
|
|
47623
|
+
*/
|
|
47624
|
+
beginValueFadeTo(idx) {
|
|
47625
|
+
const to = this.valueCache[idx];
|
|
47626
|
+
if (!to || to === this.displayedValuePixels) {
|
|
47627
|
+
return;
|
|
47628
|
+
}
|
|
47629
|
+
// A new target mid fade keeps whichever frame is mostly on screen and carries on from the current
|
|
47630
|
+
// mix, so frames landing in quick succession do not jump the sheet back.
|
|
47631
|
+
const midFade = this.fadeStartMs !== null && Boolean(this.fadeFromValuePixels);
|
|
47632
|
+
if (midFade && this.fadeMix < 0.5) {
|
|
47633
|
+
this.fadeMixStart = this.fadeMix;
|
|
47634
|
+
}
|
|
47635
|
+
else {
|
|
47636
|
+
this.fadeFromValuePixels = this.displayedValuePixels;
|
|
47637
|
+
this.fadeMixStart = 0;
|
|
47638
|
+
}
|
|
47639
|
+
this.displayedValuePixels = to;
|
|
47640
|
+
this.valueDims = this.frameDims[idx];
|
|
47641
|
+
this.fadeToIndex = idx;
|
|
47642
|
+
this.fadeMix = this.fadeFromValuePixels ? this.fadeMixStart : 1;
|
|
47643
|
+
this.fadeStartMs = this.fadeFromValuePixels ? Now() : null;
|
|
47644
|
+
this.presentVersion++;
|
|
47645
|
+
this.requestRender();
|
|
47646
|
+
}
|
|
47647
|
+
requestRender() {
|
|
47648
|
+
if (this.viewer && this.viewer.scene && this.viewer.scene.requestRenderMode) {
|
|
47649
|
+
this.viewer.scene.requestRender();
|
|
47650
|
+
}
|
|
47651
|
+
}
|
|
47424
47652
|
tickCrossfade() {
|
|
47425
47653
|
if (this.disposed || this.fadeStartMs === null) {
|
|
47426
47654
|
return;
|
|
47427
47655
|
}
|
|
47428
47656
|
const t = Math.min(1, (Now() - this.fadeStartMs) / this.crossfadeMs);
|
|
47429
47657
|
const eased = easeInOutQuad(t);
|
|
47658
|
+
if (this.valueOnly) {
|
|
47659
|
+
this.fadeMix = this.fadeMixStart + (1 - this.fadeMixStart) * eased;
|
|
47660
|
+
if (t >= 1) {
|
|
47661
|
+
this.fadeStartMs = null;
|
|
47662
|
+
this.fadeFromValuePixels = null;
|
|
47663
|
+
this.fadeMix = 1;
|
|
47664
|
+
}
|
|
47665
|
+
this.requestRender();
|
|
47666
|
+
return;
|
|
47667
|
+
}
|
|
47430
47668
|
const from = this.fadeFromPixels;
|
|
47431
47669
|
const to = this.frameCache[this.fadeToIndex];
|
|
47432
47670
|
const n = Math.min(from.length, to.length);
|
|
@@ -47466,9 +47704,7 @@
|
|
|
47466
47704
|
this.valueDims = dims;
|
|
47467
47705
|
}
|
|
47468
47706
|
this.presentVersion++;
|
|
47469
|
-
|
|
47470
|
-
this.viewer.scene.requestRender();
|
|
47471
|
-
}
|
|
47707
|
+
this.requestRender();
|
|
47472
47708
|
}
|
|
47473
47709
|
}
|
|
47474
47710
|
TextureFrameSeriesAnimator.Animator = Animator;
|
|
@@ -47487,13 +47723,15 @@
|
|
|
47487
47723
|
const MIN_VISIBLE_PIXELS = 60;
|
|
47488
47724
|
const MAX_STRIDE = 512;
|
|
47489
47725
|
const STRIDE_HYSTERESIS = 1.4;
|
|
47490
|
-
|
|
47491
|
-
const
|
|
47492
|
-
const
|
|
47493
|
-
|
|
47494
|
-
|
|
47495
|
-
|
|
47496
|
-
const
|
|
47726
|
+
// Scene labels carry a leader and are wider than a value, so they want room and not many of them.
|
|
47727
|
+
const DEFAULT_MIN_SPACING_PIXELS = 140;
|
|
47728
|
+
const DEFAULT_MAX_LABELS = 60;
|
|
47729
|
+
// How far past the edge of the view an anchor may sit before its label is dropped.
|
|
47730
|
+
const LABEL_EDGE_MARGIN_PIXELS = 120;
|
|
47731
|
+
// Distance discount for a label already up when the cap forces a choice.
|
|
47732
|
+
const LABEL_KEEP_BIAS = 0.6;
|
|
47733
|
+
// How long a label outlives its cell leaving the wanted set, so one back a frame later does not blink.
|
|
47734
|
+
const LABEL_LINGER_MS = 300;
|
|
47497
47735
|
// 8 rather than 5: below this a cell reads as texture rather than as a boundary, and it also
|
|
47498
47736
|
// bounds the worst case, since a full screen of 8 px cells fits inside MAX_GRID_CELLS.
|
|
47499
47737
|
const DEFAULT_MIN_GRID_CELL_PIXELS = 8;
|
|
@@ -47507,7 +47745,8 @@
|
|
|
47507
47745
|
const TILE_OUTLINE_ALPHA = 0.45;
|
|
47508
47746
|
// Cells per grid square at the coarsest. Beyond this a tile is outlined instead.
|
|
47509
47747
|
const MAX_GRID_STRIDE = 256;
|
|
47510
|
-
|
|
47748
|
+
// Times the pointer's ground hit is moved onto the sheet's own height before reading a cell.
|
|
47749
|
+
const HOVER_REFINE_STEPS = 3;
|
|
47511
47750
|
/**
|
|
47512
47751
|
* A clip outline from a projected ring, or null when the ring cannot be drawn.
|
|
47513
47752
|
* @param projected one entry per ring vertex, null where the vertex has no window coordinate
|
|
@@ -47601,11 +47840,25 @@
|
|
|
47601
47840
|
this.sourceMap = null;
|
|
47602
47841
|
this.source = null;
|
|
47603
47842
|
this.painted = null;
|
|
47843
|
+
this.paintedRamp = null;
|
|
47604
47844
|
this.floor = null;
|
|
47605
47845
|
this.ceiling = null;
|
|
47606
47846
|
this.disposed = false;
|
|
47607
|
-
this.drawn = 0;
|
|
47608
47847
|
this.stride = 0;
|
|
47848
|
+
this.cellLabels = new Map();
|
|
47849
|
+
this.lingerTimer = null;
|
|
47850
|
+
// What the last draw was made from, so a frame that changes none of it draws nothing.
|
|
47851
|
+
this.drawnView = null;
|
|
47852
|
+
this.drawnInputs = [];
|
|
47853
|
+
this.scratchWorld = new Cesium.Cartesian3();
|
|
47854
|
+
this.scratchWindow = new Cesium.Cartesian2();
|
|
47855
|
+
this.scratchClip = new Cesium.Cartesian4();
|
|
47856
|
+
// The camera's view and projection as one matrix, rebuilt per draw, or null outside 3D.
|
|
47857
|
+
this.viewProjection = null;
|
|
47858
|
+
this.viewProjectionScratch = new Cesium.Matrix4();
|
|
47859
|
+
this.hovering = false;
|
|
47860
|
+
this.hoverFrame = 0;
|
|
47861
|
+
this.hoverAt = null;
|
|
47609
47862
|
this.viewer = options.viewer;
|
|
47610
47863
|
this.extent = options.extent;
|
|
47611
47864
|
this.settings = options.settings || {};
|
|
@@ -47615,6 +47868,9 @@
|
|
|
47615
47868
|
this.sourceMap = exports.TextureFrameSeriesAnimator.SourceMap(options.metadata);
|
|
47616
47869
|
this.tiles = options.tiles;
|
|
47617
47870
|
this.clipRing = options.clipRing;
|
|
47871
|
+
this.heightAt = options.heightAt;
|
|
47872
|
+
this.heightKey = options.heightKey;
|
|
47873
|
+
this.hover = options.hover;
|
|
47618
47874
|
this.canvas = document.createElement("canvas");
|
|
47619
47875
|
this.canvas.style.position = "absolute";
|
|
47620
47876
|
this.canvas.style.inset = "0";
|
|
@@ -47624,12 +47880,31 @@
|
|
|
47624
47880
|
parent.appendChild(this.canvas);
|
|
47625
47881
|
}
|
|
47626
47882
|
this.ctx = this.canvas.getContext("2d");
|
|
47883
|
+
this.onPointerMove = (event) => {
|
|
47884
|
+
const bounds = this.viewer.canvas.getBoundingClientRect();
|
|
47885
|
+
this.hoverAt = { x: event.clientX - bounds.left, y: event.clientY - bounds.top };
|
|
47886
|
+
// At most once a frame, since a pointer reports far faster than anything is drawn.
|
|
47887
|
+
if (!this.hoverFrame) {
|
|
47888
|
+
this.hoverFrame = requestAnimationFrame(() => {
|
|
47889
|
+
this.hoverFrame = 0;
|
|
47890
|
+
this.updateHover();
|
|
47891
|
+
});
|
|
47892
|
+
}
|
|
47893
|
+
};
|
|
47894
|
+
this.onPointerLeave = () => {
|
|
47895
|
+
this.hoverAt = null;
|
|
47896
|
+
this.releaseHover();
|
|
47897
|
+
};
|
|
47898
|
+
if (this.hover && this.hover.register) {
|
|
47899
|
+
this.viewer.canvas.addEventListener("pointermove", this.onPointerMove, { passive: true });
|
|
47900
|
+
this.viewer.canvas.addEventListener("pointerleave", this.onPointerLeave);
|
|
47901
|
+
}
|
|
47627
47902
|
}
|
|
47628
47903
|
SetExtent(extent) {
|
|
47629
47904
|
this.extent = extent;
|
|
47630
47905
|
}
|
|
47631
47906
|
/**
|
|
47632
|
-
* The current frame's decoded values
|
|
47907
|
+
* The current frame's decoded values.
|
|
47633
47908
|
* @param source value pixels, one texel per cell
|
|
47634
47909
|
*/
|
|
47635
47910
|
SetSource(source) {
|
|
@@ -47637,11 +47912,17 @@
|
|
|
47637
47912
|
}
|
|
47638
47913
|
/**
|
|
47639
47914
|
* The tinted pixels of the same frame, so the grid can skip cells the ramp painted nothing in.
|
|
47640
|
-
* @param painted RGBA of the presented frame, or null to fall back to coverage alone
|
|
47915
|
+
* @param painted RGBA of the presented frame, or null to fall back to the ramp or coverage alone
|
|
47641
47916
|
*/
|
|
47642
47917
|
SetPainted(painted) {
|
|
47643
47918
|
this.painted = painted;
|
|
47644
47919
|
}
|
|
47920
|
+
/**
|
|
47921
|
+
* The surface's 256 step RGBA ramp, for telling whether a cell was painted without a tinted frame.
|
|
47922
|
+
*/
|
|
47923
|
+
SetPaintedRamp(ramp) {
|
|
47924
|
+
this.paintedRamp = ramp;
|
|
47925
|
+
}
|
|
47645
47926
|
/**
|
|
47646
47927
|
* The archive's per-cell extremes, when it publishes them. Without these a label can still
|
|
47647
47928
|
* print the current reading, just not the range behind it.
|
|
@@ -47651,21 +47932,28 @@
|
|
|
47651
47932
|
this.ceiling = ceiling;
|
|
47652
47933
|
}
|
|
47653
47934
|
GetDrawnCount() {
|
|
47654
|
-
return this.
|
|
47935
|
+
return this.cellLabels.size;
|
|
47655
47936
|
}
|
|
47656
47937
|
Clear() {
|
|
47657
47938
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
47658
|
-
this.
|
|
47939
|
+
this.drawnView = null;
|
|
47659
47940
|
}
|
|
47660
47941
|
Dispose() {
|
|
47661
47942
|
this.disposed = true;
|
|
47943
|
+
this.clearCellLabels();
|
|
47944
|
+
this.releaseHover();
|
|
47945
|
+
if (this.hoverFrame) {
|
|
47946
|
+
cancelAnimationFrame(this.hoverFrame);
|
|
47947
|
+
this.hoverFrame = 0;
|
|
47948
|
+
}
|
|
47949
|
+
this.viewer.canvas.removeEventListener("pointermove", this.onPointerMove);
|
|
47950
|
+
this.viewer.canvas.removeEventListener("pointerleave", this.onPointerLeave);
|
|
47662
47951
|
if (this.canvas.parentElement) {
|
|
47663
47952
|
this.canvas.parentElement.removeChild(this.canvas);
|
|
47664
47953
|
}
|
|
47665
47954
|
}
|
|
47666
47955
|
/**
|
|
47667
|
-
* Redraws
|
|
47668
|
-
* count it considers is bounded by the view rather than by the texture.
|
|
47956
|
+
* Redraws the grid and syncs the value labels, when anything they are drawn from has changed.
|
|
47669
47957
|
*/
|
|
47670
47958
|
Render() {
|
|
47671
47959
|
var _a, _b, _c;
|
|
@@ -47673,67 +47961,296 @@
|
|
|
47673
47961
|
return;
|
|
47674
47962
|
}
|
|
47675
47963
|
const view = this.viewer.canvas;
|
|
47964
|
+
const inputs = [this.source, this.painted, this.paintedRamp, this.floor, this.ceiling,
|
|
47965
|
+
view.clientWidth, view.clientHeight, this.heightKey ? this.heightKey() : null];
|
|
47966
|
+
const camera = this.viewer.camera.viewMatrix;
|
|
47967
|
+
if (this.drawnView &&
|
|
47968
|
+
Cesium.Matrix4.equals(this.drawnView, camera) &&
|
|
47969
|
+
inputs.every((input, i) => input === this.drawnInputs[i])) {
|
|
47970
|
+
return;
|
|
47971
|
+
}
|
|
47676
47972
|
if (this.canvas.width !== view.clientWidth || this.canvas.height !== view.clientHeight) {
|
|
47677
47973
|
this.canvas.width = view.clientWidth;
|
|
47678
47974
|
this.canvas.height = view.clientHeight;
|
|
47679
47975
|
}
|
|
47680
47976
|
this.Clear();
|
|
47977
|
+
this.drawnView = Cesium.Matrix4.clone(camera, this.drawnView || undefined);
|
|
47978
|
+
this.drawnInputs = inputs;
|
|
47979
|
+
const projection = this.viewer.camera.frustum.projectionMatrix;
|
|
47980
|
+
this.viewProjection = this.viewer.scene.mode === Cesium.SceneMode.SCENE3D
|
|
47981
|
+
? Cesium.Matrix4.multiply(projection, camera, this.viewProjectionScratch)
|
|
47982
|
+
: null;
|
|
47681
47983
|
const cols = this.source.width;
|
|
47682
47984
|
const rows = this.source.height;
|
|
47683
47985
|
const window = this.visibleTexels(cols, rows);
|
|
47684
47986
|
if (!window) {
|
|
47987
|
+
this.syncCellLabels([]);
|
|
47685
47988
|
return;
|
|
47686
47989
|
}
|
|
47687
|
-
|
|
47688
|
-
|
|
47689
|
-
const
|
|
47690
|
-
const
|
|
47990
|
+
// Measured where the view is looking, since in an oblique view up close the middle of the visible
|
|
47991
|
+
// texels sits out toward the horizon, where cells are a fraction of the size.
|
|
47992
|
+
const focus = this.groundAtScreenCentre();
|
|
47993
|
+
const at = focus ? this.texelAt(focus.lon, focus.lat) : null;
|
|
47994
|
+
const focused = (Boolean(at) &&
|
|
47995
|
+
at.col >= window.col0 &&
|
|
47996
|
+
at.col <= window.col1 &&
|
|
47997
|
+
at.row >= window.row0 &&
|
|
47998
|
+
at.row <= window.row1);
|
|
47999
|
+
const midCol = focused ? at.col : (window.col0 + window.col1) / 2;
|
|
48000
|
+
const midRow = focused ? at.row : (window.row0 + window.row1) / 2;
|
|
48001
|
+
const a = this.projectCell(midCol, midRow, cols, rows);
|
|
48002
|
+
const b = this.projectCell(midCol + 1, midRow + 1, cols, rows);
|
|
47691
48003
|
if (!a || !b) {
|
|
48004
|
+
// Left as they are, since one frame where the focus will not project is not the data leaving.
|
|
47692
48005
|
return;
|
|
47693
48006
|
}
|
|
47694
48007
|
const cellPixels = Math.min(Math.abs(b.x - a.x), Math.abs(b.y - a.y));
|
|
47695
48008
|
// Ahead of the label threshold on purpose: a grid still reads at a cell size where a
|
|
47696
48009
|
// number no longer fits.
|
|
47697
48010
|
this.drawGrid(window, cols, rows, cellPixels);
|
|
47698
|
-
|
|
48011
|
+
// By area rather than the shorter side, which an oblique view foreshortens to a fraction of the other.
|
|
48012
|
+
const across = this.projectCell(midCol + 1, midRow, cols, rows);
|
|
48013
|
+
const down = this.projectCell(midCol, midRow + 1, cols, rows);
|
|
48014
|
+
const cellSize = across && down
|
|
48015
|
+
? Math.sqrt(Math.abs((across.x - a.x) * (down.y - a.y) - (across.y - a.y) * (down.x - a.x)))
|
|
48016
|
+
: cellPixels;
|
|
48017
|
+
const visibleWidth = cellSize * (window.col1 - window.col0 + 1);
|
|
48018
|
+
const visibleHeight = cellSize * (window.row1 - window.row0 + 1);
|
|
48019
|
+
if (cellSize < ((_a = this.settings.minCellPixels) !== null && _a !== void 0 ? _a : DEFAULT_MIN_CELL_PIXELS) ||
|
|
48020
|
+
Math.max(visibleWidth, visibleHeight) < MIN_VISIBLE_PIXELS) {
|
|
48021
|
+
this.syncCellLabels([]);
|
|
47699
48022
|
return;
|
|
47700
48023
|
}
|
|
47701
|
-
const visibleWidth = cellPixels * (window.col1 - window.col0 + 1);
|
|
47702
|
-
const visibleHeight = cellPixels * (window.row1 - window.row0 + 1);
|
|
47703
|
-
if (Math.max(visibleWidth, visibleHeight) < MIN_VISIBLE_PIXELS) {
|
|
47704
|
-
return;
|
|
47705
|
-
}
|
|
47706
|
-
const parts = this.settings.parts || ["value", "floor", "ceiling"];
|
|
47707
|
-
this.ctx.font = LABEL_FONT;
|
|
47708
|
-
this.ctx.textAlign = "center";
|
|
47709
48024
|
const maxLabels = (_b = this.settings.maxLabels) !== null && _b !== void 0 ? _b : DEFAULT_MAX_LABELS;
|
|
47710
48025
|
this.stride = ChooseLabelStride({
|
|
47711
|
-
cellPixels,
|
|
48026
|
+
cellPixels: cellSize,
|
|
47712
48027
|
visibleCols: window.col1 - window.col0 + 1,
|
|
47713
48028
|
visibleRows: window.row1 - window.row0 + 1,
|
|
47714
48029
|
maxLabels,
|
|
47715
|
-
|
|
47716
|
-
// one is, so changing what a label says cannot quietly start overlapping them.
|
|
47717
|
-
minSpacingPixels: Math.max((_c = this.settings.minSpacingPixels) !== null && _c !== void 0 ? _c : DEFAULT_MIN_SPACING_PIXELS, this.widestLabel(parts)),
|
|
48030
|
+
minSpacingPixels: (_c = this.settings.minSpacingPixels) !== null && _c !== void 0 ? _c : DEFAULT_MIN_SPACING_PIXELS,
|
|
47718
48031
|
held: this.stride
|
|
47719
48032
|
});
|
|
47720
|
-
const
|
|
48033
|
+
const parts = this.settings.parts || ["value", "floor", "ceiling"];
|
|
48034
|
+
this.syncCellLabels(this.labelCells(window, cols, rows, this.stride, maxLabels, parts));
|
|
48035
|
+
}
|
|
48036
|
+
/*
|
|
48037
|
+
* One labelled cell per lattice block, keyed by the block's origin so it survives frame and stride changes.
|
|
48038
|
+
*/
|
|
48039
|
+
labelCells(window, cols, rows, stride, max, parts) {
|
|
48040
|
+
const width = this.canvas.width;
|
|
48041
|
+
const height = this.canvas.height;
|
|
47721
48042
|
const firstRow = Math.floor(window.row0 / stride) * stride;
|
|
47722
48043
|
const firstCol = Math.floor(window.col0 / stride) * stride;
|
|
47723
|
-
|
|
47724
|
-
|
|
48044
|
+
const candidates = [];
|
|
48045
|
+
for (let row = firstRow; row <= window.row1; row += stride) {
|
|
48046
|
+
for (let col = firstCol; col <= window.col1; col += stride) {
|
|
47725
48047
|
const found = this.readNear(col, row, cols, rows, stride);
|
|
47726
48048
|
if (!found) {
|
|
47727
48049
|
continue;
|
|
47728
48050
|
}
|
|
47729
|
-
const point = this.
|
|
47730
|
-
|
|
47731
|
-
|
|
48051
|
+
const point = this.projectCell(found.col, found.row, cols, rows);
|
|
48052
|
+
// A margin past the edge, so a label does not drop the moment its anchor does.
|
|
48053
|
+
const margin = LABEL_EDGE_MARGIN_PIXELS;
|
|
48054
|
+
if (!point ||
|
|
48055
|
+
point.x < -margin ||
|
|
48056
|
+
point.y < -margin ||
|
|
48057
|
+
point.x > width + margin ||
|
|
48058
|
+
point.y > height + margin) {
|
|
48059
|
+
continue;
|
|
48060
|
+
}
|
|
48061
|
+
const text = this.labelLines(found.reading, parts).join("\n");
|
|
48062
|
+
if (!text) {
|
|
47732
48063
|
continue;
|
|
47733
48064
|
}
|
|
47734
|
-
|
|
48065
|
+
const key = col + "," + row;
|
|
48066
|
+
const distance = Math.hypot(point.x - width / 2, point.y - height / 2);
|
|
48067
|
+
candidates.push({
|
|
48068
|
+
key,
|
|
48069
|
+
lon: this.lonAt(found.col, cols),
|
|
48070
|
+
lat: this.latAt(found.row, rows),
|
|
48071
|
+
text,
|
|
48072
|
+
// Labels already up outrank new ones, so the capped set does not shuffle as the view pans.
|
|
48073
|
+
rank: this.cellLabels.has(key) ? distance * LABEL_KEEP_BIAS : distance
|
|
48074
|
+
});
|
|
47735
48075
|
}
|
|
47736
48076
|
}
|
|
48077
|
+
if (candidates.length > max) {
|
|
48078
|
+
candidates.sort((x, y) => x.rank - y.rank);
|
|
48079
|
+
candidates.length = max;
|
|
48080
|
+
}
|
|
48081
|
+
return candidates;
|
|
48082
|
+
}
|
|
48083
|
+
/*
|
|
48084
|
+
* Keeps one scene label per wanted cell, reusing those that are still wanted.
|
|
48085
|
+
*/
|
|
48086
|
+
syncCellLabels(wanted) {
|
|
48087
|
+
var _a;
|
|
48088
|
+
const keep = new Set(wanted.map((w) => w.key));
|
|
48089
|
+
const now = Date.now();
|
|
48090
|
+
let lingering = false;
|
|
48091
|
+
for (const [key, entry] of this.cellLabels) {
|
|
48092
|
+
if (keep.has(key)) {
|
|
48093
|
+
entry.missingSince = undefined;
|
|
48094
|
+
continue;
|
|
48095
|
+
}
|
|
48096
|
+
entry.missingSince = (_a = entry.missingSince) !== null && _a !== void 0 ? _a : now;
|
|
48097
|
+
if (now - entry.missingSince >= LABEL_LINGER_MS) {
|
|
48098
|
+
entry.label.Dispose();
|
|
48099
|
+
this.cellLabels.delete(key);
|
|
48100
|
+
}
|
|
48101
|
+
else {
|
|
48102
|
+
lingering = true;
|
|
48103
|
+
}
|
|
48104
|
+
}
|
|
48105
|
+
// A still camera draws nothing more, so a lingering label is given its own later look.
|
|
48106
|
+
if (lingering && !this.lingerTimer) {
|
|
48107
|
+
this.lingerTimer = setTimeout(() => {
|
|
48108
|
+
this.lingerTimer = null;
|
|
48109
|
+
if (!this.disposed) {
|
|
48110
|
+
this.drawnView = null;
|
|
48111
|
+
this.viewer.scene.requestRender();
|
|
48112
|
+
}
|
|
48113
|
+
}, LABEL_LINGER_MS);
|
|
48114
|
+
}
|
|
48115
|
+
for (const want of wanted) {
|
|
48116
|
+
const pos3d = Cesium.Cartesian3.fromDegrees(want.lon, want.lat, this.surfaceHeight(want.lon, want.lat));
|
|
48117
|
+
const options = {
|
|
48118
|
+
presentation: "text",
|
|
48119
|
+
text: want.text,
|
|
48120
|
+
leader: true,
|
|
48121
|
+
selectOnClick: false
|
|
48122
|
+
};
|
|
48123
|
+
const existing = this.cellLabels.get(want.key);
|
|
48124
|
+
if (existing) {
|
|
48125
|
+
if (!Cesium.Cartesian3.equalsEpsilon(existing.label.pos3d, pos3d, 0, 0.01)) {
|
|
48126
|
+
existing.label.pos3d = pos3d;
|
|
48127
|
+
}
|
|
48128
|
+
if (existing.text !== want.text) {
|
|
48129
|
+
existing.label.Configure(options);
|
|
48130
|
+
existing.text = want.text;
|
|
48131
|
+
}
|
|
48132
|
+
continue;
|
|
48133
|
+
}
|
|
48134
|
+
const id = (this.hover ? this.hover.entityId : "texture-value") + "#cell:" + want.key;
|
|
48135
|
+
const label = new exports.EntityLabel.Label({
|
|
48136
|
+
rego: {
|
|
48137
|
+
entityId: id,
|
|
48138
|
+
menuItemId: this.hover ? this.hover.menuItemId : undefined,
|
|
48139
|
+
name: want.text,
|
|
48140
|
+
priority: 0,
|
|
48141
|
+
// No graphic, since a point or billboard visual pulls a label back to itself on rebuild.
|
|
48142
|
+
visual: new Cesium.Entity({ position: pos3d })
|
|
48143
|
+
},
|
|
48144
|
+
viewer: this.viewer,
|
|
48145
|
+
pos3d,
|
|
48146
|
+
options,
|
|
48147
|
+
persistent: true,
|
|
48148
|
+
visible: () => !this.disposed
|
|
48149
|
+
});
|
|
48150
|
+
this.cellLabels.set(want.key, { label, text: want.text });
|
|
48151
|
+
}
|
|
48152
|
+
}
|
|
48153
|
+
clearCellLabels() {
|
|
48154
|
+
if (this.lingerTimer) {
|
|
48155
|
+
clearTimeout(this.lingerTimer);
|
|
48156
|
+
this.lingerTimer = null;
|
|
48157
|
+
}
|
|
48158
|
+
for (const entry of this.cellLabels.values()) {
|
|
48159
|
+
entry.label.Dispose();
|
|
48160
|
+
}
|
|
48161
|
+
this.cellLabels.clear();
|
|
48162
|
+
}
|
|
48163
|
+
/*
|
|
48164
|
+
* Shows the cell under the pointer through the register's hover label, unless something else holds it.
|
|
48165
|
+
*/
|
|
48166
|
+
updateHover() {
|
|
48167
|
+
const hover = this.hover;
|
|
48168
|
+
if (this.disposed || !hover || !hover.register || !this.source || !this.hoverAt) {
|
|
48169
|
+
return;
|
|
48170
|
+
}
|
|
48171
|
+
const register = hover.register;
|
|
48172
|
+
const target = register.GetLabelHoverTarget();
|
|
48173
|
+
const ours = Boolean(target) && register.IsLabelHovered(hover.entityId, hover.menuItemId);
|
|
48174
|
+
if (target && !ours) {
|
|
48175
|
+
this.hovering = false;
|
|
48176
|
+
return;
|
|
48177
|
+
}
|
|
48178
|
+
const under = this.readingUnder(this.hoverAt.x, this.hoverAt.y);
|
|
48179
|
+
if (!under) {
|
|
48180
|
+
this.releaseHover();
|
|
48181
|
+
return;
|
|
48182
|
+
}
|
|
48183
|
+
register.SetLabelHover({
|
|
48184
|
+
entityId: hover.entityId,
|
|
48185
|
+
menuItemId: hover.menuItemId,
|
|
48186
|
+
position: { x: this.hoverAt.x, y: this.hoverAt.y },
|
|
48187
|
+
text: under.text,
|
|
48188
|
+
pos3d: under.pos3d
|
|
48189
|
+
});
|
|
48190
|
+
this.hovering = true;
|
|
48191
|
+
}
|
|
48192
|
+
releaseHover() {
|
|
48193
|
+
const hover = this.hover;
|
|
48194
|
+
if (!this.hovering || !hover || !hover.register) {
|
|
48195
|
+
return;
|
|
48196
|
+
}
|
|
48197
|
+
this.hovering = false;
|
|
48198
|
+
if (hover.register.IsLabelHovered(hover.entityId, hover.menuItemId)) {
|
|
48199
|
+
hover.register.ClearLabelHover();
|
|
48200
|
+
}
|
|
48201
|
+
}
|
|
48202
|
+
/*
|
|
48203
|
+
* The text for the painted cell under a window position and its centre on the sheet, or null.
|
|
48204
|
+
*/
|
|
48205
|
+
readingUnder(x, y) {
|
|
48206
|
+
const at = this.cellUnder(x, y);
|
|
48207
|
+
if (!at) {
|
|
48208
|
+
return null;
|
|
48209
|
+
}
|
|
48210
|
+
const cols = this.source.width;
|
|
48211
|
+
const found = this.readAt(at.col, at.row, cols);
|
|
48212
|
+
if (!found || !this.paintedAt(at.col, at.row, cols)) {
|
|
48213
|
+
return null;
|
|
48214
|
+
}
|
|
48215
|
+
const text = this.labelLines(found, this.settings.parts || ["value", "floor", "ceiling"]).join("\n");
|
|
48216
|
+
if (!text) {
|
|
48217
|
+
return null;
|
|
48218
|
+
}
|
|
48219
|
+
const lon = this.lonAt(at.col, cols);
|
|
48220
|
+
const lat = this.latAt(at.row, this.source.height);
|
|
48221
|
+
return { text, pos3d: Cesium.Cartesian3.fromDegrees(lon, lat, this.surfaceHeight(lon, lat)) };
|
|
48222
|
+
}
|
|
48223
|
+
/*
|
|
48224
|
+
* The cell under a window position, found on the sheet rather than on the ground beneath it.
|
|
48225
|
+
*/
|
|
48226
|
+
cellUnder(x, y) {
|
|
48227
|
+
const scene = this.viewer.scene;
|
|
48228
|
+
const camera = this.viewer.camera;
|
|
48229
|
+
const screen = new Cesium.Cartesian2(x, y);
|
|
48230
|
+
const ray = camera.getPickRay(screen);
|
|
48231
|
+
if (!ray) {
|
|
48232
|
+
return null;
|
|
48233
|
+
}
|
|
48234
|
+
let hit = (scene.globe && scene.globe.pick(ray, scene)) || camera.pickEllipsoid(screen);
|
|
48235
|
+
if (!hit) {
|
|
48236
|
+
return null;
|
|
48237
|
+
}
|
|
48238
|
+
const radii = Cesium.Ellipsoid.WGS84.radii;
|
|
48239
|
+
for (let i = 0; i < HOVER_REFINE_STEPS && this.heightAt; i++) {
|
|
48240
|
+
const carto = Cesium.Cartographic.fromCartesian(hit);
|
|
48241
|
+
const height = this.heightAt(Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude));
|
|
48242
|
+
if (height == null) {
|
|
48243
|
+
break;
|
|
48244
|
+
}
|
|
48245
|
+
const inflated = new Cesium.Ellipsoid(radii.x + height, radii.y + height, radii.z + height);
|
|
48246
|
+
const interval = Cesium.IntersectionTests.rayEllipsoid(ray, inflated);
|
|
48247
|
+
if (!interval) {
|
|
48248
|
+
break;
|
|
48249
|
+
}
|
|
48250
|
+
hit = Cesium.Ray.getPoint(ray, interval.start);
|
|
48251
|
+
}
|
|
48252
|
+
const carto = Cesium.Cartographic.fromCartesian(hit);
|
|
48253
|
+
return this.texelAt(Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude));
|
|
47737
48254
|
}
|
|
47738
48255
|
/*
|
|
47739
48256
|
* Outlines each covered cell, from a projected lattice of its corners.
|
|
@@ -47761,44 +48278,25 @@
|
|
|
47761
48278
|
if (wide * high > MAX_GRID_CELLS) {
|
|
47762
48279
|
return;
|
|
47763
48280
|
}
|
|
47764
|
-
const lattice = new
|
|
47765
|
-
|
|
47766
|
-
|
|
47767
|
-
|
|
47768
|
-
|
|
47769
|
-
|
|
47770
|
-
|
|
47771
|
-
}
|
|
47772
|
-
}
|
|
47773
|
-
const path = new Path2D();
|
|
47774
|
-
let drew = false;
|
|
48281
|
+
const lattice = new Lattice(wide + 1, high + 1, (c, r) => {
|
|
48282
|
+
// Corners sit half a texel out from the centres lonAt/latAt return.
|
|
48283
|
+
const lon = this.lonAt(window.col0 + c - 0.5, cols);
|
|
48284
|
+
const lat = this.latAt(window.row0 + r - 0.5, rows);
|
|
48285
|
+
return this.projectInto(lon, lat);
|
|
48286
|
+
});
|
|
48287
|
+
const covered = new Uint8Array(wide * high);
|
|
47775
48288
|
for (let r = 0; r < high; r++) {
|
|
47776
48289
|
for (let c = 0; c < wide; c++) {
|
|
47777
|
-
|
|
47778
|
-
continue;
|
|
47779
|
-
}
|
|
47780
|
-
const tl = lattice[r * (wide + 1) + c];
|
|
47781
|
-
const tr = lattice[r * (wide + 1) + c + 1];
|
|
47782
|
-
const bl = lattice[(r + 1) * (wide + 1) + c];
|
|
47783
|
-
const br = lattice[(r + 1) * (wide + 1) + c + 1];
|
|
47784
|
-
if (!tl || !tr || !bl || !br) {
|
|
47785
|
-
continue;
|
|
47786
|
-
}
|
|
47787
|
-
path.moveTo(tl.x, tl.y);
|
|
47788
|
-
path.lineTo(tr.x, tr.y);
|
|
47789
|
-
path.lineTo(br.x, br.y);
|
|
47790
|
-
path.lineTo(bl.x, bl.y);
|
|
47791
|
-
path.closePath();
|
|
47792
|
-
drew = true;
|
|
48290
|
+
covered[r * wide + c] = this.isCovered(window.col0 + c, window.row0 + r, cols) ? 1 : 0;
|
|
47793
48291
|
}
|
|
47794
48292
|
}
|
|
47795
|
-
|
|
48293
|
+
const path = new Path2D();
|
|
48294
|
+
if (!lattice.Trace(path, covered, wide, high)) {
|
|
47796
48295
|
return;
|
|
47797
48296
|
}
|
|
47798
48297
|
this.ctx.strokeStyle = color;
|
|
47799
48298
|
this.ctx.lineWidth = (_b = this.settings.gridWidthPixels) !== null && _b !== void 0 ? _b : DEFAULT_GRID_WIDTH_PIXELS;
|
|
47800
48299
|
this.ctx.stroke(path);
|
|
47801
|
-
+" alpha" + this.ctx.globalAlpha;
|
|
47802
48300
|
}
|
|
47803
48301
|
/*
|
|
47804
48302
|
* Restricts drawing to the polygon, so nothing is ruled over ground it does not cover.
|
|
@@ -47820,33 +48318,11 @@
|
|
|
47820
48318
|
path.closePath();
|
|
47821
48319
|
this.ctx.clip(path);
|
|
47822
48320
|
}
|
|
47823
|
-
/*
|
|
47824
|
-
* The cell of whichever tile covers a point, as screen corners.
|
|
47825
|
-
*/
|
|
47826
|
-
tileCellOutline(lon, lat) {
|
|
47827
|
-
for (const tile of this.tiles) {
|
|
47828
|
-
if (lon < tile.West || lon > tile.East || lat < tile.South || lat > tile.North) {
|
|
47829
|
-
continue;
|
|
47830
|
-
}
|
|
47831
|
-
const c = (Math.min(tile.ResolutionX - 1, Math.max(0, Math.floor((lon - tile.West) / (tile.East - tile.West) * tile.ResolutionX))));
|
|
47832
|
-
const r = (Math.min(tile.ResolutionY - 1, Math.max(0, Math.floor((tile.North - lat) / (tile.North - tile.South) * tile.ResolutionY))));
|
|
47833
|
-
const w0 = tile.West + (c / tile.ResolutionX) * (tile.East - tile.West);
|
|
47834
|
-
const w1 = tile.West + ((c + 1) / tile.ResolutionX) * (tile.East - tile.West);
|
|
47835
|
-
const n0 = tile.North - (r / tile.ResolutionY) * (tile.North - tile.South);
|
|
47836
|
-
const n1 = tile.North - ((r + 1) / tile.ResolutionY) * (tile.North - tile.South);
|
|
47837
|
-
const corners = [
|
|
47838
|
-
this.project(w0, n0), this.project(w1, n0),
|
|
47839
|
-
this.project(w1, n1), this.project(w0, n1)
|
|
47840
|
-
];
|
|
47841
|
-
return corners.every((q) => q) ? corners : null;
|
|
47842
|
-
}
|
|
47843
|
-
return null;
|
|
47844
|
-
}
|
|
47845
48321
|
/*
|
|
47846
48322
|
* Outlines each tile's own cells, so cell size still shows where the mesh is refined.
|
|
47847
48323
|
*/
|
|
47848
48324
|
drawTileGrid(color) {
|
|
47849
|
-
var _a, _b
|
|
48325
|
+
var _a, _b;
|
|
47850
48326
|
const view = this.viewer.camera.computeViewRectangle(Cesium.Ellipsoid.WGS84);
|
|
47851
48327
|
if (!view) {
|
|
47852
48328
|
return;
|
|
@@ -47875,7 +48351,7 @@
|
|
|
47875
48351
|
coarse.lineTo(tr.x, tr.y);
|
|
47876
48352
|
coarse.lineTo(br.x, br.y);
|
|
47877
48353
|
coarse.lineTo(bl.x, bl.y);
|
|
47878
|
-
coarse.
|
|
48354
|
+
coarse.lineTo(tl.x, tl.y);
|
|
47879
48355
|
drewCoarse = true;
|
|
47880
48356
|
};
|
|
47881
48357
|
// Planned for every visible tile BEFORE any is drawn.
|
|
@@ -47936,31 +48412,29 @@
|
|
|
47936
48412
|
// moves, and so each step up is a subset of the one below it.
|
|
47937
48413
|
const firstR = Math.floor(r0 / step) * step;
|
|
47938
48414
|
const firstC = Math.floor(c0 / step) * step;
|
|
47939
|
-
|
|
47940
|
-
|
|
47941
|
-
|
|
47942
|
-
|
|
47943
|
-
|
|
47944
|
-
|
|
47945
|
-
|
|
47946
|
-
|
|
47947
|
-
|
|
47948
|
-
|
|
47949
|
-
|
|
47950
|
-
|
|
47951
|
-
|
|
47952
|
-
|
|
47953
|
-
|
|
47954
|
-
|
|
47955
|
-
|
|
47956
|
-
|
|
47957
|
-
|
|
47958
|
-
|
|
47959
|
-
|
|
47960
|
-
|
|
47961
|
-
|
|
47962
|
-
drew = true;
|
|
47963
|
-
}
|
|
48415
|
+
const wide = Math.ceil((c1 - firstC) / step);
|
|
48416
|
+
const high = Math.ceil((r1 - firstR) / step);
|
|
48417
|
+
const lonOf = (c) => tile.West
|
|
48418
|
+
+ (Math.min(firstC + c * step, tile.ResolutionX) / tile.ResolutionX) * (tile.East - tile.West);
|
|
48419
|
+
const latOf = (r) => tile.North
|
|
48420
|
+
- (Math.min(firstR + r * step, tile.ResolutionY) / tile.ResolutionY) * (tile.North - tile.South);
|
|
48421
|
+
const covered = new Uint8Array(wide * high);
|
|
48422
|
+
let any = false;
|
|
48423
|
+
for (let r = 0; r < high; r++) {
|
|
48424
|
+
for (let c = 0; c < wide; c++) {
|
|
48425
|
+
if (this.coveredAt((lonOf(c) + lonOf(c + 1)) / 2, (latOf(r) + latOf(r + 1)) / 2)) {
|
|
48426
|
+
covered[r * wide + c] = 1;
|
|
48427
|
+
any = true;
|
|
48428
|
+
}
|
|
48429
|
+
}
|
|
48430
|
+
}
|
|
48431
|
+
if (!any) {
|
|
48432
|
+
continue;
|
|
48433
|
+
}
|
|
48434
|
+
// Projected on demand, since a sparse raster leaves most corners belonging to no cell.
|
|
48435
|
+
const lattice = new Lattice(wide + 1, high + 1, (c, r) => this.projectInto(lonOf(c), latOf(r)));
|
|
48436
|
+
if (lattice.Trace(path, covered, wide, high)) {
|
|
48437
|
+
drew = true;
|
|
47964
48438
|
}
|
|
47965
48439
|
}
|
|
47966
48440
|
const gridWidth = (_b = this.settings.gridWidthPixels) !== null && _b !== void 0 ? _b : DEFAULT_GRID_WIDTH_PIXELS;
|
|
@@ -47978,7 +48452,7 @@
|
|
|
47978
48452
|
return;
|
|
47979
48453
|
}
|
|
47980
48454
|
this.ctx.strokeStyle = color;
|
|
47981
|
-
this.ctx.lineWidth =
|
|
48455
|
+
this.ctx.lineWidth = gridWidth;
|
|
47982
48456
|
this.ctx.stroke(path);
|
|
47983
48457
|
}
|
|
47984
48458
|
/*
|
|
@@ -47998,7 +48472,7 @@
|
|
|
47998
48472
|
};
|
|
47999
48473
|
}
|
|
48000
48474
|
/*
|
|
48001
|
-
* Whether the composited raster holds data at a lon/lat.
|
|
48475
|
+
* Whether the composited raster holds painted data at a lon/lat.
|
|
48002
48476
|
*/
|
|
48003
48477
|
coveredAt(lon, lat) {
|
|
48004
48478
|
if (!this.source) {
|
|
@@ -48011,60 +48485,20 @@
|
|
|
48011
48485
|
if (col < 0 || row < 0 || col >= cols || row >= rows) {
|
|
48012
48486
|
return false;
|
|
48013
48487
|
}
|
|
48014
|
-
const at = (row * cols + col) * 4 + 3;
|
|
48015
48488
|
// Painted, not merely covered: a dry cell holds a reading the ramp deliberately hides, and
|
|
48016
48489
|
// ruling a grid over it draws detail where the picture shows none.
|
|
48017
|
-
if (this.painted
|
|
48018
|
-
return this.
|
|
48490
|
+
if (this.painted || this.paintedRamp) {
|
|
48491
|
+
return this.paintedAt(col, row, cols);
|
|
48019
48492
|
}
|
|
48020
|
-
return this.source.data[
|
|
48493
|
+
return this.source.data[(row * cols + col) * 4 + 3] >= 128;
|
|
48021
48494
|
}
|
|
48022
48495
|
isCovered(col, row, cols) {
|
|
48023
48496
|
return Boolean(this.source && this.source.data[(row * cols + col) * 4 + 3] >= 128);
|
|
48024
48497
|
}
|
|
48025
48498
|
/*
|
|
48026
|
-
*
|
|
48027
|
-
*/
|
|
48028
|
-
widestLabel(parts) {
|
|
48029
|
-
const sample = [];
|
|
48030
|
-
if (parts.indexOf("value") >= 0) {
|
|
48031
|
-
sample.push(valueLine("-000.00", LabelUnits(this.metadata)));
|
|
48032
|
-
}
|
|
48033
|
-
const range = [];
|
|
48034
|
-
const showsRange = !exports.TextureFrameSeriesAnimator.IsTransformed(this.metadata);
|
|
48035
|
-
if (showsRange && parts.indexOf("floor") >= 0) {
|
|
48036
|
-
range.push("min: -000.00");
|
|
48037
|
-
}
|
|
48038
|
-
if (showsRange && parts.indexOf("ceiling") >= 0) {
|
|
48039
|
-
range.push("max: -000.00");
|
|
48040
|
-
}
|
|
48041
|
-
if (range.length) {
|
|
48042
|
-
sample.push(range.join(" - "));
|
|
48043
|
-
}
|
|
48044
|
-
if (!sample.length) {
|
|
48045
|
-
return 0;
|
|
48046
|
-
}
|
|
48047
|
-
const widest = Math.max(...sample.map((line) => this.ctx.measureText(line).width));
|
|
48048
|
-
return widest + LABEL_GAP_PIXELS;
|
|
48049
|
-
}
|
|
48050
|
-
/*
|
|
48051
|
-
* The four screen corners of one cell, or null if any of them will not project.
|
|
48499
|
+
* What a label says about one reading, a line at a time.
|
|
48052
48500
|
*/
|
|
48053
|
-
|
|
48054
|
-
// On a pyramid the composited texel is not a cell. Outlining it would draw a second,
|
|
48055
|
-
// uniform lattice over the tile grid, misaligned with it everywhere.
|
|
48056
|
-
if (this.tiles && this.tiles.length) {
|
|
48057
|
-
return this.tileCellOutline(this.lonAt(col, cols), this.latAt(row, rows));
|
|
48058
|
-
}
|
|
48059
|
-
const corners = [
|
|
48060
|
-
this.project(this.lonAt(col - 0.5, cols), this.latAt(row - 0.5, rows)),
|
|
48061
|
-
this.project(this.lonAt(col + 0.5, cols), this.latAt(row - 0.5, rows)),
|
|
48062
|
-
this.project(this.lonAt(col + 0.5, cols), this.latAt(row + 0.5, rows)),
|
|
48063
|
-
this.project(this.lonAt(col - 0.5, cols), this.latAt(row + 0.5, rows))
|
|
48064
|
-
];
|
|
48065
|
-
return corners.every((c) => c) ? corners : null;
|
|
48066
|
-
}
|
|
48067
|
-
drawLabel(point, reading, parts, outline) {
|
|
48501
|
+
labelLines(reading, parts) {
|
|
48068
48502
|
var _a;
|
|
48069
48503
|
const dp = (_a = this.settings.decimals) !== null && _a !== void 0 ? _a : (Math.abs(reading.value) < 10 ? 2 : 1);
|
|
48070
48504
|
const lines = [];
|
|
@@ -48083,32 +48517,7 @@
|
|
|
48083
48517
|
if (range.length) {
|
|
48084
48518
|
lines.push(range.join(" - "));
|
|
48085
48519
|
}
|
|
48086
|
-
|
|
48087
|
-
return;
|
|
48088
|
-
}
|
|
48089
|
-
if (outline) {
|
|
48090
|
-
this.ctx.strokeStyle = LABEL_ANCHOR_COLOR;
|
|
48091
|
-
this.ctx.lineWidth = 1.5;
|
|
48092
|
-
this.ctx.beginPath();
|
|
48093
|
-
this.ctx.moveTo(outline[0].x, outline[0].y);
|
|
48094
|
-
for (let i = 1; i < outline.length; i++) {
|
|
48095
|
-
this.ctx.lineTo(outline[i].x, outline[i].y);
|
|
48096
|
-
}
|
|
48097
|
-
this.ctx.closePath();
|
|
48098
|
-
this.ctx.stroke();
|
|
48099
|
-
}
|
|
48100
|
-
const width = Math.max(...lines.map((l) => this.ctx.measureText(l).width)) + 10;
|
|
48101
|
-
const height = lines.length * LINE_HEIGHT + 6;
|
|
48102
|
-
const top = point.y - height / 2;
|
|
48103
|
-
this.ctx.fillStyle = "rgba(16,16,14,0.72)";
|
|
48104
|
-
this.ctx.beginPath();
|
|
48105
|
-
this.ctx.roundRect(point.x - width / 2, top, width, height, LABEL_RADIUS);
|
|
48106
|
-
this.ctx.fill();
|
|
48107
|
-
lines.forEach((line, i) => {
|
|
48108
|
-
this.ctx.fillStyle = i === 0 ? "#ffffff" : "#a8a89f";
|
|
48109
|
-
this.ctx.fillText(line, point.x, top + LINE_HEIGHT * (i + 1) - 1);
|
|
48110
|
-
});
|
|
48111
|
-
this.drawn++;
|
|
48520
|
+
return lines;
|
|
48112
48521
|
}
|
|
48113
48522
|
/*
|
|
48114
48523
|
* The first cell with a reading at or near a lattice point, searched within its own block.
|
|
@@ -48135,11 +48544,24 @@
|
|
|
48135
48544
|
return null;
|
|
48136
48545
|
}
|
|
48137
48546
|
paintedAt(col, row, cols) {
|
|
48138
|
-
|
|
48139
|
-
|
|
48547
|
+
const at = (row * cols + col) * 4;
|
|
48548
|
+
if (this.painted && this.painted.length === this.source.data.length) {
|
|
48549
|
+
return this.painted[at + 3] > 0;
|
|
48550
|
+
}
|
|
48551
|
+
if (this.paintedRamp) {
|
|
48552
|
+
const data = this.source.data;
|
|
48553
|
+
if (data[at + 3] === 0) {
|
|
48554
|
+
return false;
|
|
48555
|
+
}
|
|
48556
|
+
// The 8 bit grey the ramp is indexed on, which is what a tinted frame would have used.
|
|
48557
|
+
const grey = exports.TextureFrameSeriesAnimator.IsPackedValue(this.metadata)
|
|
48558
|
+
? Math.round((data[at] * 256 + data[at + 1]) / 65535 * 255)
|
|
48559
|
+
: data[at];
|
|
48560
|
+
const steps = this.paintedRamp.length / 4;
|
|
48561
|
+
const index = Math.min(steps - 1, Math.round(grey / 255 * (steps - 1)));
|
|
48562
|
+
return this.paintedRamp[index * 4 + 3] > 0;
|
|
48140
48563
|
}
|
|
48141
|
-
|
|
48142
|
-
return this.painted[at] > 0;
|
|
48564
|
+
return true;
|
|
48143
48565
|
}
|
|
48144
48566
|
// Decodes one cell, skipping anything the texture marks as having no data.
|
|
48145
48567
|
readAt(col, row, cols) {
|
|
@@ -48155,6 +48577,16 @@
|
|
|
48155
48577
|
sourceMap: this.sourceMap
|
|
48156
48578
|
});
|
|
48157
48579
|
}
|
|
48580
|
+
/*
|
|
48581
|
+
* The texel holding a lon/lat, or null outside the raster.
|
|
48582
|
+
*/
|
|
48583
|
+
texelAt(lon, lat) {
|
|
48584
|
+
const cols = this.source.width;
|
|
48585
|
+
const rows = this.source.height;
|
|
48586
|
+
const col = Math.floor((lon - this.extent.West) / (this.extent.East - this.extent.West) * cols);
|
|
48587
|
+
const row = Math.floor((this.extent.North - lat) / (this.extent.North - this.extent.South) * rows);
|
|
48588
|
+
return col < 0 || row < 0 || col >= cols || row >= rows ? null : { col, row };
|
|
48589
|
+
}
|
|
48158
48590
|
lonAt(col, cols) {
|
|
48159
48591
|
return this.extent.West + ((col + 0.5) / cols) * (this.extent.East - this.extent.West);
|
|
48160
48592
|
}
|
|
@@ -48183,15 +48615,102 @@
|
|
|
48183
48615
|
row1: Math.min(rows - 1, Math.ceil((this.extent.North - south) / spanLat * rows) + 1)
|
|
48184
48616
|
};
|
|
48185
48617
|
}
|
|
48618
|
+
// Metres the sheet sits at over a point, or the ellipsoid when nothing says otherwise.
|
|
48619
|
+
surfaceHeight(lon, lat) {
|
|
48620
|
+
const height = this.heightAt ? this.heightAt(lon, lat) : null;
|
|
48621
|
+
return height == null || !isFinite(height) ? 0 : height;
|
|
48622
|
+
}
|
|
48623
|
+
projectCell(col, row, cols, rows) {
|
|
48624
|
+
return this.project(this.lonAt(col, cols), this.latAt(row, rows));
|
|
48625
|
+
}
|
|
48626
|
+
// Projected at the sheet's own height, since up close the ellipsoid is metres from where it is drawn.
|
|
48186
48627
|
project(lon, lat) {
|
|
48187
|
-
|
|
48628
|
+
return this.projectInto(lon, lat) ? { x: this.scratchWindow.x, y: this.scratchWindow.y } : null;
|
|
48629
|
+
}
|
|
48630
|
+
/*
|
|
48631
|
+
* Projects into the shared scratch window position, returning whether it landed anywhere.
|
|
48632
|
+
*/
|
|
48633
|
+
projectInto(lon, lat) {
|
|
48634
|
+
const world = Cesium.Cartesian3.fromDegrees(lon, lat, this.surfaceHeight(lon, lat), Cesium.Ellipsoid.WGS84, this.scratchWorld);
|
|
48635
|
+
// One matrix multiply in 3D, where the general path rederives the viewport and frustum per point.
|
|
48636
|
+
if (this.viewProjection) {
|
|
48637
|
+
const clip = Cesium.Matrix4.multiplyByVector(this.viewProjection, Cesium.Cartesian4.fromElements(world.x, world.y, world.z, 1, this.scratchClip), this.scratchClip);
|
|
48638
|
+
if (!(clip.w > 0)) {
|
|
48639
|
+
return null;
|
|
48640
|
+
}
|
|
48641
|
+
const view = this.viewer.canvas;
|
|
48642
|
+
this.scratchWindow.x = (clip.x / clip.w * 0.5 + 0.5) * view.clientWidth;
|
|
48643
|
+
this.scratchWindow.y = (0.5 - clip.y / clip.w * 0.5) * view.clientHeight;
|
|
48644
|
+
return this.scratchWindow;
|
|
48645
|
+
}
|
|
48188
48646
|
const transforms = Cesium.SceneTransforms;
|
|
48189
48647
|
// Renamed in newer Cesium, and this library runs on whatever the host app supplies.
|
|
48190
48648
|
const fn = transforms.worldToWindowCoordinates || transforms.wgs84ToWindowCoordinates;
|
|
48191
|
-
return fn ? fn.call(transforms, this.viewer.scene, world) : null;
|
|
48649
|
+
return fn ? fn.call(transforms, this.viewer.scene, world, this.scratchWindow) || null : null;
|
|
48192
48650
|
}
|
|
48193
48651
|
}
|
|
48194
48652
|
TextureValueLabels.Labels = Labels;
|
|
48653
|
+
/*
|
|
48654
|
+
* A grid of window positions projected at most once each, and the cell edges traced through them.
|
|
48655
|
+
*/
|
|
48656
|
+
class Lattice {
|
|
48657
|
+
constructor(wide, high, projectAt) {
|
|
48658
|
+
this.wide = wide;
|
|
48659
|
+
this.projectAt = projectAt;
|
|
48660
|
+
this.xs = new Float64Array(wide * high);
|
|
48661
|
+
this.ys = new Float64Array(wide * high);
|
|
48662
|
+
this.state = new Uint8Array(wide * high);
|
|
48663
|
+
}
|
|
48664
|
+
at(c, r) {
|
|
48665
|
+
const i = r * this.wide + c;
|
|
48666
|
+
if (this.state[i] === 0) {
|
|
48667
|
+
const p = this.projectAt(c, r);
|
|
48668
|
+
if (p) {
|
|
48669
|
+
this.xs[i] = p.x;
|
|
48670
|
+
this.ys[i] = p.y;
|
|
48671
|
+
this.state[i] = 1;
|
|
48672
|
+
}
|
|
48673
|
+
else {
|
|
48674
|
+
this.state[i] = 2;
|
|
48675
|
+
}
|
|
48676
|
+
}
|
|
48677
|
+
return this.state[i] === 1 ? i : -1;
|
|
48678
|
+
}
|
|
48679
|
+
edge(path, a, b) {
|
|
48680
|
+
if (a < 0 || b < 0) {
|
|
48681
|
+
return false;
|
|
48682
|
+
}
|
|
48683
|
+
path.moveTo(this.xs[a], this.ys[a]);
|
|
48684
|
+
path.lineTo(this.xs[b], this.ys[b]);
|
|
48685
|
+
return true;
|
|
48686
|
+
}
|
|
48687
|
+
/*
|
|
48688
|
+
* Adds each covered cell's edges to a path, a shared edge once, and says whether any were added.
|
|
48689
|
+
*/
|
|
48690
|
+
Trace(path, covered, cellsWide, cellsHigh) {
|
|
48691
|
+
let drew = false;
|
|
48692
|
+
for (let r = 0; r < cellsHigh; r++) {
|
|
48693
|
+
for (let c = 0; c < cellsWide; c++) {
|
|
48694
|
+
if (!covered[r * cellsWide + c]) {
|
|
48695
|
+
continue;
|
|
48696
|
+
}
|
|
48697
|
+
const tl = this.at(c, r);
|
|
48698
|
+
const tr = this.at(c + 1, r);
|
|
48699
|
+
const bl = this.at(c, r + 1);
|
|
48700
|
+
if (!(r > 0 && covered[(r - 1) * cellsWide + c])) {
|
|
48701
|
+
drew = this.edge(path, tl, tr) || drew;
|
|
48702
|
+
}
|
|
48703
|
+
if (!(c > 0 && covered[r * cellsWide + c - 1])) {
|
|
48704
|
+
drew = this.edge(path, tl, bl) || drew;
|
|
48705
|
+
}
|
|
48706
|
+
const br = this.at(c + 1, r + 1);
|
|
48707
|
+
drew = this.edge(path, tr, br) || drew;
|
|
48708
|
+
drew = this.edge(path, bl, br) || drew;
|
|
48709
|
+
}
|
|
48710
|
+
}
|
|
48711
|
+
return drew;
|
|
48712
|
+
}
|
|
48713
|
+
}
|
|
48195
48714
|
/*
|
|
48196
48715
|
* The value line. Units go on it alone: repeating them on the range underneath doubles the widest
|
|
48197
48716
|
* label to say nothing, and the two lines are plainly the same quantity.
|
|
@@ -48287,6 +48806,9 @@ in vec2 st;
|
|
|
48287
48806
|
in float skirt;
|
|
48288
48807
|
|
|
48289
48808
|
uniform sampler2D u_valueTexture;
|
|
48809
|
+
// The frame being faded away from, and how far the fade has got. The same texture at 1 when settled.
|
|
48810
|
+
uniform sampler2D u_valueTextureFrom;
|
|
48811
|
+
uniform float u_valueFade;
|
|
48290
48812
|
uniform sampler2D u_groundTexture;
|
|
48291
48813
|
uniform float u_valuePacked;
|
|
48292
48814
|
uniform float u_valueMin;
|
|
@@ -48407,18 +48929,22 @@ float decodeValue(vec4 texel) {
|
|
|
48407
48929
|
}
|
|
48408
48930
|
|
|
48409
48931
|
void main() {
|
|
48410
|
-
vec4
|
|
48932
|
+
vec4 texelTo = texture(u_valueTexture, st);
|
|
48933
|
+
vec4 texelFrom = texture(u_valueTextureFrom, st);
|
|
48411
48934
|
v_st = st;
|
|
48935
|
+
// Decoded before mixing, since blending a packed pair byte by byte reads as some other value
|
|
48936
|
+
// halfway through the fade.
|
|
48937
|
+
float raw = mix(decodeValue(texelFrom), decodeValue(texelTo), u_valueFade);
|
|
48938
|
+
float alpha = mix(texelFrom.a, texelTo.a, u_valueFade);
|
|
48412
48939
|
|
|
48413
48940
|
// Inside a region the scene knows is water, a texel with no value still has to paint, or the
|
|
48414
48941
|
// hole the region exists to close is still a hole.
|
|
48415
48942
|
// The value it paints is the shallowest the ramp is not transparent at.
|
|
48416
48943
|
float inMask = u_maskWeight * texture(u_maskTexture, st).a;
|
|
48417
|
-
float hasData = step(u_coverageCutoffVertex,
|
|
48418
|
-
float raw = decodeValue(texel);
|
|
48944
|
+
float hasData = step(u_coverageCutoffVertex, alpha);
|
|
48419
48945
|
float filled = mix(u_maskFillValue, max(raw, u_maskFillValue), hasData);
|
|
48420
48946
|
v_value = mix(raw, filled, inMask * u_maskFill);
|
|
48421
|
-
v_coverage = max(
|
|
48947
|
+
v_coverage = max(alpha, inMask * u_maskFill);
|
|
48422
48948
|
|
|
48423
48949
|
// Exaggeration stretches the span above the archive's minimum rather than the height itself. On an
|
|
48424
48950
|
// elevation the height is measured from the ellipsoid, tens of metres from any of the data, so
|
|
@@ -48461,6 +48987,8 @@ void main() {
|
|
|
48461
48987
|
`;
|
|
48462
48988
|
const FRAGMENT_SHADER_GLSL300 = `
|
|
48463
48989
|
uniform sampler2D u_valueTexture;
|
|
48990
|
+
uniform sampler2D u_valueTextureFrom;
|
|
48991
|
+
uniform float u_valueFade;
|
|
48464
48992
|
uniform sampler2D u_rampTexture;
|
|
48465
48993
|
uniform float u_coverageCutoff;
|
|
48466
48994
|
uniform vec2 u_texelSize;
|
|
@@ -48497,12 +49025,15 @@ in vec2 v_waveMetres;
|
|
|
48497
49025
|
* Scaled per channel for the same reason the vertex shader's decode is: reassembling the pair reaches
|
|
48498
49026
|
* 65535, which mediump does not have to represent, and this is the shader that may not get highp.
|
|
48499
49027
|
*/
|
|
48500
|
-
float
|
|
48501
|
-
vec4 texel = texture(u_valueTexture, uv);
|
|
49028
|
+
float decodeValue(vec4 texel) {
|
|
48502
49029
|
float packed16 = texel.r * (255.0 * 256.0 / 65535.0) + texel.g * (255.0 / 65535.0);
|
|
48503
49030
|
return mix(texel.r, packed16, u_valuePacked);
|
|
48504
49031
|
}
|
|
48505
49032
|
|
|
49033
|
+
float valueAt(vec2 uv) {
|
|
49034
|
+
return mix(decodeValue(texture(u_valueTextureFrom, uv)), decodeValue(texture(u_valueTexture, uv)), u_valueFade);
|
|
49035
|
+
}
|
|
49036
|
+
|
|
48506
49037
|
//#water
|
|
48507
49038
|
/*
|
|
48508
49039
|
* Slope of the ripple field at a point, in metres of rise per metre travelled.
|
|
@@ -48949,6 +49480,13 @@ void main() {
|
|
|
48949
49480
|
this.rampTexture = null;
|
|
48950
49481
|
this.texture = null;
|
|
48951
49482
|
this.textureDirty = true;
|
|
49483
|
+
// Fed from a fading source: the frame being left, and which pixels each texture holds.
|
|
49484
|
+
this.fade = null;
|
|
49485
|
+
this.textureFrom = null;
|
|
49486
|
+
this.uploadedTo = null;
|
|
49487
|
+
this.uploadedFrom = null;
|
|
49488
|
+
// Size of whichever value raster is bound, canvas or pixels.
|
|
49489
|
+
this.sourceDims = null;
|
|
48952
49490
|
this.shaderProgram = null;
|
|
48953
49491
|
this.shaderProgramWater = false;
|
|
48954
49492
|
this.renderState = null;
|
|
@@ -49119,6 +49657,65 @@ void main() {
|
|
|
49119
49657
|
this.maskGroundTexture = null;
|
|
49120
49658
|
}
|
|
49121
49659
|
}
|
|
49660
|
+
/**
|
|
49661
|
+
* Metres above the ellipsoid the sheet sits at over a point, or null unless a fading source is bound.
|
|
49662
|
+
*/
|
|
49663
|
+
HeightAt(lon, lat) {
|
|
49664
|
+
const pixels = this.fade ? this.fade.to : null;
|
|
49665
|
+
const dims = this.sourceDims;
|
|
49666
|
+
if (!pixels || !dims) {
|
|
49667
|
+
return null;
|
|
49668
|
+
}
|
|
49669
|
+
const fx = (lon - this.extent.West) / (this.extent.East - this.extent.West);
|
|
49670
|
+
const fy = (this.extent.North - lat) / (this.extent.North - this.extent.South);
|
|
49671
|
+
if (!(fx >= 0 && fx <= 1 && fy >= 0 && fy <= 1)) {
|
|
49672
|
+
return null;
|
|
49673
|
+
}
|
|
49674
|
+
const col = Math.min(dims.width - 1, Math.floor(fx * dims.width));
|
|
49675
|
+
const row = Math.min(dims.height - 1, Math.floor(fy * dims.height));
|
|
49676
|
+
const at = (row * dims.width + col) * 4;
|
|
49677
|
+
const coverage = pixels[at + 3] / 255;
|
|
49678
|
+
const normalised = this.packedValue
|
|
49679
|
+
? (pixels[at] * 256 + pixels[at + 1]) / 65535
|
|
49680
|
+
: pixels[at] / 255;
|
|
49681
|
+
const metres = this.valueMin + normalised * (this.valueMax - this.valueMin) * this.exaggeration;
|
|
49682
|
+
const following = this.followGround && Boolean(this.groundPixels);
|
|
49683
|
+
const ground = following ? this.groundAt(fx, fy) : 0;
|
|
49684
|
+
return this.baseHeight + ground + metres * (following ? coverage : 1);
|
|
49685
|
+
}
|
|
49686
|
+
/**
|
|
49687
|
+
* Changes whenever HeightAt would answer differently for the same frame.
|
|
49688
|
+
*/
|
|
49689
|
+
GetPlacementKey() {
|
|
49690
|
+
return [this.groundPixels ? 1 : 0, this.baseHeight, this.exaggeration, this.followGround ? 1 : 0].join("|");
|
|
49691
|
+
}
|
|
49692
|
+
/**
|
|
49693
|
+
* The ramp the sheet is painted with, as 256 RGBA steps across the value range.
|
|
49694
|
+
*/
|
|
49695
|
+
GetRampPixels() {
|
|
49696
|
+
return this.rampPixels;
|
|
49697
|
+
}
|
|
49698
|
+
/*
|
|
49699
|
+
* The packed ground raster read the way sampleGround reads it, bilinear between texel centres.
|
|
49700
|
+
*/
|
|
49701
|
+
groundAt(u, v) {
|
|
49702
|
+
const size = GROUND_SAMPLES_PER_SIDE;
|
|
49703
|
+
const pixels = this.groundPixels;
|
|
49704
|
+
const read = (x, y) => {
|
|
49705
|
+
const i = (Math.min(size - 1, Math.max(0, y)) * size + Math.min(size - 1, Math.max(0, x))) * 4;
|
|
49706
|
+
const packed = (pixels[i] << 16) + (pixels[i + 1] << 8) + pixels[i + 2];
|
|
49707
|
+
return GROUND_MIN_METRES + (packed / 16777215) * (GROUND_MAX_METRES - GROUND_MIN_METRES);
|
|
49708
|
+
};
|
|
49709
|
+
const x = u * size - 0.5;
|
|
49710
|
+
const y = v * size - 0.5;
|
|
49711
|
+
const x0 = Math.floor(x);
|
|
49712
|
+
const y0 = Math.floor(y);
|
|
49713
|
+
const fx = x - x0;
|
|
49714
|
+
const fy = y - y0;
|
|
49715
|
+
const top = read(x0, y0) + (read(x0 + 1, y0) - read(x0, y0)) * fx;
|
|
49716
|
+
const bottom = read(x0, y0 + 1) + (read(x0 + 1, y0 + 1) - read(x0, y0 + 1)) * fx;
|
|
49717
|
+
return top + (bottom - top) * fy;
|
|
49718
|
+
}
|
|
49122
49719
|
/**
|
|
49123
49720
|
* Feeds the surface from an animated frame series.
|
|
49124
49721
|
* Height and colour advance with the clock without the caller having to push each frame in.
|
|
@@ -49137,23 +49734,39 @@ void main() {
|
|
|
49137
49734
|
return;
|
|
49138
49735
|
}
|
|
49139
49736
|
const context = frameState.context;
|
|
49140
|
-
|
|
49141
|
-
|
|
49142
|
-
|
|
49143
|
-
|
|
49144
|
-
|
|
49145
|
-
}
|
|
49146
|
-
|
|
49147
|
-
|
|
49148
|
-
|
|
49149
|
-
|
|
49150
|
-
|
|
49737
|
+
const fade = this.valueSource && this.valueSource.GetValueFade
|
|
49738
|
+
? this.valueSource.GetValueFade()
|
|
49739
|
+
: null;
|
|
49740
|
+
if (fade) {
|
|
49741
|
+
this.fade = fade;
|
|
49742
|
+
this.sourceDims = { width: fade.width, height: fade.height };
|
|
49743
|
+
}
|
|
49744
|
+
else {
|
|
49745
|
+
this.fade = null;
|
|
49746
|
+
if (this.valueSource) {
|
|
49747
|
+
const canvas = this.valueSource.GetValueCanvas();
|
|
49748
|
+
if (!canvas) {
|
|
49749
|
+
// No frame decoded yet, so there is nothing to displace against.
|
|
49750
|
+
return;
|
|
49751
|
+
}
|
|
49752
|
+
const version = this.valueSource.GetPresentVersion();
|
|
49753
|
+
if (canvas !== this.source || version !== this.lastPresentVersion) {
|
|
49754
|
+
this.source = canvas;
|
|
49755
|
+
this.lastPresentVersion = version;
|
|
49756
|
+
this.textureDirty = true;
|
|
49757
|
+
}
|
|
49151
49758
|
}
|
|
49759
|
+
this.sourceDims = this.source ? { width: this.source.width, height: this.source.height } : null;
|
|
49152
49760
|
}
|
|
49153
|
-
if (!this.
|
|
49761
|
+
if (!this.sourceDims || !this.sourceDims.width || !this.sourceDims.height) {
|
|
49154
49762
|
return;
|
|
49155
49763
|
}
|
|
49156
|
-
this.
|
|
49764
|
+
if (this.fade) {
|
|
49765
|
+
this.syncFadeTextures(context);
|
|
49766
|
+
}
|
|
49767
|
+
else {
|
|
49768
|
+
this.syncTexture(context);
|
|
49769
|
+
}
|
|
49157
49770
|
this.syncRampTexture(context);
|
|
49158
49771
|
this.requestGround();
|
|
49159
49772
|
this.syncGroundTexture(context);
|
|
@@ -49201,6 +49814,10 @@ void main() {
|
|
|
49201
49814
|
this.texture.destroy();
|
|
49202
49815
|
this.texture = null;
|
|
49203
49816
|
}
|
|
49817
|
+
if (this.textureFrom) {
|
|
49818
|
+
this.textureFrom.destroy();
|
|
49819
|
+
this.textureFrom = null;
|
|
49820
|
+
}
|
|
49204
49821
|
if (this.rampTexture) {
|
|
49205
49822
|
this.rampTexture.destroy();
|
|
49206
49823
|
this.rampTexture = null;
|
|
@@ -49468,7 +50085,7 @@ void main() {
|
|
|
49468
50085
|
const southWest = Cesium.Cartesian3.fromDegrees(this.extent.West, this.extent.South, 0);
|
|
49469
50086
|
const southEast = Cesium.Cartesian3.fromDegrees(this.extent.East, this.extent.South, 0);
|
|
49470
50087
|
const width = Cesium.Cartesian3.distance(southWest, southEast);
|
|
49471
|
-
return Math.max(1, width / Math.max(1, this.
|
|
50088
|
+
return Math.max(1, width / Math.max(1, this.sourceDims ? this.sourceDims.width : 1));
|
|
49472
50089
|
}
|
|
49473
50090
|
syncRampTexture(context) {
|
|
49474
50091
|
if (this.rampTexture) {
|
|
@@ -49489,6 +50106,67 @@ void main() {
|
|
|
49489
50106
|
})
|
|
49490
50107
|
});
|
|
49491
50108
|
}
|
|
50109
|
+
valueSampler() {
|
|
50110
|
+
// Nearest for a packed value, because filtering would blend the two bytes of the
|
|
50111
|
+
// packing rather than the values they encode, the same trap sampleGround avoids.
|
|
50112
|
+
const minification = this.packedValue
|
|
50113
|
+
? Cesium.TextureMinificationFilter.NEAREST
|
|
50114
|
+
: Cesium.TextureMinificationFilter.LINEAR;
|
|
50115
|
+
const magnification = this.packedValue
|
|
50116
|
+
? Cesium.TextureMagnificationFilter.NEAREST
|
|
50117
|
+
: Cesium.TextureMagnificationFilter.LINEAR;
|
|
50118
|
+
return new Cesium.Sampler({
|
|
50119
|
+
wrapS: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
50120
|
+
wrapT: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
50121
|
+
minificationFilter: minification,
|
|
50122
|
+
magnificationFilter: magnification
|
|
50123
|
+
});
|
|
50124
|
+
}
|
|
50125
|
+
/*
|
|
50126
|
+
* Uploads value pixels into a texture, reusing it when the size still fits.
|
|
50127
|
+
*/
|
|
50128
|
+
uploadPixels(context, texture, pixels, width, height) {
|
|
50129
|
+
const view = new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength);
|
|
50130
|
+
const source = { width, height, arrayBufferView: view };
|
|
50131
|
+
if (texture && texture.width === width && texture.height === height) {
|
|
50132
|
+
texture.copyFrom({ source });
|
|
50133
|
+
return texture;
|
|
50134
|
+
}
|
|
50135
|
+
if (texture) {
|
|
50136
|
+
texture.destroy();
|
|
50137
|
+
}
|
|
50138
|
+
return new Cesium.Texture({
|
|
50139
|
+
context,
|
|
50140
|
+
width,
|
|
50141
|
+
height,
|
|
50142
|
+
pixelFormat: Cesium.PixelFormat.RGBA,
|
|
50143
|
+
pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE,
|
|
50144
|
+
source,
|
|
50145
|
+
// Row 0 is the archive's north edge, and has to stay at the top, as the canvas path does.
|
|
50146
|
+
flipY: false,
|
|
50147
|
+
sampler: this.valueSampler()
|
|
50148
|
+
});
|
|
50149
|
+
}
|
|
50150
|
+
/*
|
|
50151
|
+
* Keeps a texture each side of the fade, swapping them on a new frame so only that one is uploaded.
|
|
50152
|
+
*/
|
|
50153
|
+
syncFadeTextures(context) {
|
|
50154
|
+
const fade = this.fade;
|
|
50155
|
+
if (fade.to !== this.uploadedTo) {
|
|
50156
|
+
if (fade.from && fade.from === this.uploadedTo && this.texture) {
|
|
50157
|
+
const previous = this.textureFrom;
|
|
50158
|
+
this.textureFrom = this.texture;
|
|
50159
|
+
this.uploadedFrom = this.uploadedTo;
|
|
50160
|
+
this.texture = previous;
|
|
50161
|
+
}
|
|
50162
|
+
this.texture = this.uploadPixels(context, this.texture, fade.to, fade.width, fade.height);
|
|
50163
|
+
this.uploadedTo = fade.to;
|
|
50164
|
+
}
|
|
50165
|
+
if (fade.from && fade.from !== this.uploadedFrom) {
|
|
50166
|
+
this.textureFrom = this.uploadPixels(context, this.textureFrom, fade.from, fade.width, fade.height);
|
|
50167
|
+
this.uploadedFrom = fade.from;
|
|
50168
|
+
}
|
|
50169
|
+
}
|
|
49492
50170
|
syncTexture(context) {
|
|
49493
50171
|
const sizeChanged = this.texture
|
|
49494
50172
|
&& (this.texture.width !== this.source.width || this.texture.height !== this.source.height);
|
|
@@ -49496,27 +50174,15 @@ void main() {
|
|
|
49496
50174
|
if (this.texture) {
|
|
49497
50175
|
this.texture.destroy();
|
|
49498
50176
|
}
|
|
49499
|
-
// Nearest for a packed value, because filtering would blend the two bytes of the
|
|
49500
|
-
// packing rather than the values they encode, the same trap sampleGround avoids.
|
|
49501
|
-
const minification = this.packedValue
|
|
49502
|
-
? Cesium.TextureMinificationFilter.NEAREST
|
|
49503
|
-
: Cesium.TextureMinificationFilter.LINEAR;
|
|
49504
|
-
const magnification = this.packedValue
|
|
49505
|
-
? Cesium.TextureMagnificationFilter.NEAREST
|
|
49506
|
-
: Cesium.TextureMagnificationFilter.LINEAR;
|
|
49507
50177
|
this.texture = new Cesium.Texture({
|
|
49508
50178
|
context,
|
|
49509
50179
|
source: this.source,
|
|
49510
50180
|
// Cesium flips canvas rows on upload by default, which would put the archive's
|
|
49511
50181
|
// row 0 (its north edge) at the south edge and mirror the whole surface.
|
|
49512
50182
|
flipY: false,
|
|
49513
|
-
sampler:
|
|
49514
|
-
wrapS: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
49515
|
-
wrapT: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
49516
|
-
minificationFilter: minification,
|
|
49517
|
-
magnificationFilter: magnification
|
|
49518
|
-
})
|
|
50183
|
+
sampler: this.valueSampler()
|
|
49519
50184
|
});
|
|
50185
|
+
this.uploadedTo = null;
|
|
49520
50186
|
this.textureDirty = false;
|
|
49521
50187
|
return;
|
|
49522
50188
|
}
|
|
@@ -49579,7 +50245,7 @@ void main() {
|
|
|
49579
50245
|
* carry, so the ladder is capped one step above the texel count rather than run to its top.
|
|
49580
50246
|
*/
|
|
49581
50247
|
tierCeiling() {
|
|
49582
|
-
const source = this.
|
|
50248
|
+
const source = this.sourceDims;
|
|
49583
50249
|
if (!source || !source.width || !source.height) {
|
|
49584
50250
|
return TIERS[TIERS.length - 1];
|
|
49585
50251
|
}
|
|
@@ -49657,6 +50323,10 @@ void main() {
|
|
|
49657
50323
|
owner: this,
|
|
49658
50324
|
uniformMap: {
|
|
49659
50325
|
u_valueTexture: () => self.texture,
|
|
50326
|
+
u_valueTextureFrom: () => (self.fade && self.fade.from && self.textureFrom
|
|
50327
|
+
? self.textureFrom
|
|
50328
|
+
: self.texture),
|
|
50329
|
+
u_valueFade: () => (self.fade && self.fade.from ? self.fade.mix : 1),
|
|
49660
50330
|
u_groundTexture: () => self.groundTexture,
|
|
49661
50331
|
u_valuePacked: () => (self.packedValue ? 1 : 0),
|
|
49662
50332
|
u_groundWeight: () => (self.followGround && self.groundPixels ? 1 : 0),
|
|
@@ -49694,7 +50364,7 @@ void main() {
|
|
|
49694
50364
|
: 0,
|
|
49695
50365
|
u_coverageCutoff: () => COVERAGE_CUTOFF,
|
|
49696
50366
|
u_rampTexture: () => self.rampTexture,
|
|
49697
|
-
u_texelSize: () => new Cesium.Cartesian2(1 / self.
|
|
50367
|
+
u_texelSize: () => new Cesium.Cartesian2(1 / self.sourceDims.width, 1 / self.sourceDims.height),
|
|
49698
50368
|
u_waterOn: () => (self.water ? 1 : 0),
|
|
49699
50369
|
u_waterWavelength: () => (self.water && self.water.wavelength)
|
|
49700
50370
|
|| DEFAULT_WATER_WAVELENGTH_METRES,
|
|
@@ -50065,6 +50735,10 @@ void main() {
|
|
|
50065
50735
|
syncTextureValueLabels({
|
|
50066
50736
|
cEntity,
|
|
50067
50737
|
viewer: params.viewer,
|
|
50738
|
+
surface: drawsDisplacedSurface ? cEntity[DISPLACED_SURFACE_KEY] : null,
|
|
50739
|
+
hover: params.visualRegister
|
|
50740
|
+
? { register: params.visualRegister, entityId: String(entity.Bruce.ID), menuItemId: params.menuItemId }
|
|
50741
|
+
: undefined,
|
|
50068
50742
|
settings: overlaySettings(style, entity, params.tags),
|
|
50069
50743
|
extent: labelArchive
|
|
50070
50744
|
? (drawsDisplacedSurface ? archiveExtent(labelArchive.metadata, posses) : drapeExtent)
|
|
@@ -50371,6 +51045,7 @@ void main() {
|
|
|
50371
51045
|
// rendered: null
|
|
50372
51046
|
rendered: (_c = params.rendered) === null || _c === void 0 ? void 0 : _c.get(entity.Bruce.ID),
|
|
50373
51047
|
menuItemId: params.menuItemId,
|
|
51048
|
+
visualRegister: params.visualRegister,
|
|
50374
51049
|
onSeriesDiscovered: params.onSeriesDiscovered
|
|
50375
51050
|
});
|
|
50376
51051
|
}
|
|
@@ -50808,8 +51483,8 @@ void main() {
|
|
|
50808
51483
|
*/
|
|
50809
51484
|
const LABEL_GRID_COLOR = "rgba(255,255,255,0.55)";
|
|
50810
51485
|
const LABEL_GRID_WIDTH_PIXELS = 1;
|
|
50811
|
-
const LABEL_MIN_SPACING_PIXELS =
|
|
50812
|
-
const LABEL_MAX_LABELS =
|
|
51486
|
+
const LABEL_MIN_SPACING_PIXELS = 140;
|
|
51487
|
+
const LABEL_MAX_LABELS = 60;
|
|
50813
51488
|
function overlaySettings(style, entity, tags) {
|
|
50814
51489
|
if (!wantsLabels(style)) {
|
|
50815
51490
|
return undefined;
|
|
@@ -50829,6 +51504,7 @@ void main() {
|
|
|
50829
51504
|
*/
|
|
50830
51505
|
function syncTextureValueLabels(params) {
|
|
50831
51506
|
const { cEntity, viewer, settings, extent, animator, metadata } = params;
|
|
51507
|
+
const surface = params.surface && !params.surface.isDestroyed() ? params.surface : null;
|
|
50832
51508
|
const existing = cEntity[TEXTURE_VALUE_LABELS_KEY];
|
|
50833
51509
|
// Offline has no extent to rebuild the overlay from, so an existing one is left running.
|
|
50834
51510
|
if (params.offline && existing) {
|
|
@@ -50847,8 +51523,13 @@ void main() {
|
|
|
50847
51523
|
West: tile.West, East: tile.East, South: tile.South, North: tile.North,
|
|
50848
51524
|
ResolutionX: tile.ResolutionX, ResolutionY: tile.ResolutionY
|
|
50849
51525
|
})),
|
|
50850
|
-
clipRing: params.clipRing
|
|
51526
|
+
clipRing: params.clipRing,
|
|
51527
|
+
heightAt: surface ? (lon, lat) => surface.HeightAt(lon, lat) : undefined,
|
|
51528
|
+
heightKey: surface ? () => surface.GetPlacementKey() : undefined,
|
|
51529
|
+
hover: params.hover
|
|
50851
51530
|
});
|
|
51531
|
+
// A surface keeps no tinted frame, so whether a cell was painted is read off its ramp.
|
|
51532
|
+
labels.SetPaintedRamp(surface ? surface.GetRampPixels() : null);
|
|
50852
51533
|
let lastVersion = -1;
|
|
50853
51534
|
let appliedFloor = null;
|
|
50854
51535
|
let appliedCeiling = null;
|
|
@@ -50860,15 +51541,15 @@ void main() {
|
|
|
50860
51541
|
if (animator.IsDisposed()) {
|
|
50861
51542
|
return;
|
|
50862
51543
|
}
|
|
50863
|
-
const canvas = animator.GetValueCanvas();
|
|
50864
|
-
if (!canvas) {
|
|
50865
|
-
return;
|
|
50866
|
-
}
|
|
50867
51544
|
const version = animator.GetPresentVersion();
|
|
50868
51545
|
if (version !== lastVersion) {
|
|
51546
|
+
// Read from the frame cache, since reading the value canvas back copies a whole frame.
|
|
51547
|
+
const raster = animator.GetValueRaster();
|
|
51548
|
+
if (!raster) {
|
|
51549
|
+
return;
|
|
51550
|
+
}
|
|
50869
51551
|
lastVersion = version;
|
|
50870
|
-
|
|
50871
|
-
labels.SetSource(ctx.getImageData(0, 0, canvas.width, canvas.height));
|
|
51552
|
+
labels.SetSource(raster);
|
|
50872
51553
|
labels.SetPainted(animator.GetDisplayedPixels());
|
|
50873
51554
|
}
|
|
50874
51555
|
// Tracked by reference rather than latched on the first one to arrive: a tiled archive
|
|
@@ -52592,7 +53273,7 @@ void main() {
|
|
|
52592
53273
|
StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
|
|
52593
53274
|
})(exports.StyleUtils || (exports.StyleUtils = {}));
|
|
52594
53275
|
|
|
52595
|
-
const VERSION = "7.
|
|
53276
|
+
const VERSION = "7.6.0";
|
|
52596
53277
|
/**
|
|
52597
53278
|
* Updates the environment instance used by bruce-cesium to one specified.
|
|
52598
53279
|
* This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
|