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.es5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Cesium from 'cesium';
|
|
2
|
-
import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, ShaderSource, Cartesian3, Math as Math$1, Ellipsoid, Transforms, Matrix4, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, Cartesian2, Cartesian4, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass,
|
|
3
|
-
import { Cartes, LRUCache, ProjectViewTile, ENVIRONMENT, Calculator, ClientFile, Entity as Entity$1, EntityLod, EntityTag, EntityType, ObjectUtils, Style, UrlUtils, Carto, Geometry as Geometry$1, ClientFileValueMap, Color as Color$1,
|
|
2
|
+
import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, ConstantPositionProperty, HeightReference, DistanceDisplayCondition, ShaderSource, Cartesian3, Math as Math$1, Ellipsoid, Transforms, Matrix4, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, Cartesian2, Cartesian4, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, PixelDatatype, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, ColorBlendMode, HeadingPitchRoll, ShadowMode, ClassificationType, Model, JulianDate, Quaternion, ArcType, CornerType, ImageMaterialProperty, PolygonHierarchy, PolylineGraphics, Rectangle, HorizontalOrigin, VerticalOrigin, SceneMode, IntersectionTests, Ray, SceneTransforms, FeatureDetection, Matrix3, NearFarScalar, KmlDataSource, GeoJsonDataSource, Cesium3DTileStyle, HeadingPitchRange, Ion, IonResource, Cesium3DTileColorBlendMode, Cesium3DTileset, OrthographicFrustum, EasingFunction, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, Intersect, PolygonPipeline, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, CesiumInspector, ClockRange, DynamicEnvironmentMapManager, GeometryInstance, CustomDataSource, ScreenSpaceEventHandler, ScreenSpaceEventType, Fullscreen, CzmlDataSource } from 'cesium';
|
|
3
|
+
import { Cartes, LRUCache, ProjectViewTile, ENVIRONMENT, Calculator, ClientFile, Entity as Entity$1, EntityLod, EntityTag, EntityType, ObjectUtils, Style, UrlUtils, Carto, Geometry as Geometry$1, Bounds, ClientFileValueMap, Color as Color$1, Api, MenuItem, ProjectView, ProjectViewBookmark, Tileset, ZoomControl, BruceEvent, EntityCoords, DataLab, DelayQueue, AccountConcept, BruceApi, EntityRelation, RecordChangeFeed, EntitySource, Camera, ProgramKey, ProjectViewLegacyTile, Account, AccountLimits, Session, ProjectViewBookmarkGroup, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, EntityRelationType, EntityHistoricData } from 'bruce-models';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Ensures a number is returned from a given value.
|
|
@@ -7311,7 +7311,8 @@ var EntityLabel;
|
|
|
7311
7311
|
this.groupId = groupId;
|
|
7312
7312
|
this.additionalTexts = this.options.text != null ? [] : null;
|
|
7313
7313
|
this.photo = null;
|
|
7314
|
-
|
|
7314
|
+
// A label given its position keeps it, or the rebuild goes looking for the entity's own.
|
|
7315
|
+
this.createLabel(this.prePositioned ? this._pos3d : undefined);
|
|
7315
7316
|
}
|
|
7316
7317
|
/**
|
|
7317
7318
|
* Creates cesium entity for the label if one does not exist.
|
|
@@ -7903,6 +7904,22 @@ var EntityLabel;
|
|
|
7903
7904
|
radius: Math.max(width, height) / 2
|
|
7904
7905
|
};
|
|
7905
7906
|
};
|
|
7907
|
+
/*
|
|
7908
|
+
* The on-screen centre and half-size of a model too small for its box, so the box is ringed like a glyph.
|
|
7909
|
+
*/
|
|
7910
|
+
const measureModel = (anchor) => {
|
|
7911
|
+
const hull = layer.modelBoxes.Hull(this.id);
|
|
7912
|
+
if (!(hull === null || hull === void 0 ? void 0 : hull.length)) {
|
|
7913
|
+
return null;
|
|
7914
|
+
}
|
|
7915
|
+
const xs = hull.map(point => point.x), ys = hull.map(point => point.y);
|
|
7916
|
+
const left = Math.min(...xs), top = Math.min(...ys);
|
|
7917
|
+
const width = Math.max(...xs) - left, height = Math.max(...ys) - top;
|
|
7918
|
+
if (Math.max(width, height) >= MODEL_BOX_MIN_SIZE) {
|
|
7919
|
+
return null;
|
|
7920
|
+
}
|
|
7921
|
+
return { x: left + width / 2 - anchor.x, y: top + height / 2 - anchor.y, radius: Math.max(width, height) / 2 };
|
|
7922
|
+
};
|
|
7906
7923
|
// Only CAD tilesets and model Entities are assembly parts with a geometry box; anything else stays a point.
|
|
7907
7924
|
const boxable = Boolean(normalLeader && ((_r = this.rego) === null || _r === void 0 ? void 0 : _r.entityId) && ((visual instanceof Cesium3DTileFeature && this.rego.tilesetType === Tileset.EType.Cad) ||
|
|
7908
7925
|
(visual instanceof Entity && Boolean(visual.model))));
|
|
@@ -7961,7 +7978,7 @@ var EntityLabel;
|
|
|
7961
7978
|
}
|
|
7962
7979
|
let cursorMotion = this.cursorMotion;
|
|
7963
7980
|
const project = () => {
|
|
7964
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
|
|
7981
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
7965
7982
|
if (this.disposed || !this.viewer || this.viewer.isDestroyed() || ((_a = this.visible) === null || _a === void 0 ? void 0 : _a.call(this)) === false ||
|
|
7966
7983
|
(presentation === "icon" && this.options.iconScale === 0)) {
|
|
7967
7984
|
return null;
|
|
@@ -8004,7 +8021,8 @@ var EntityLabel;
|
|
|
8004
8021
|
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);
|
|
8005
8022
|
// Eased between the ring and where the stem meets the box, so gaining or losing a box never jumps.
|
|
8006
8023
|
const boxTarget = this.boxMorph.anchor;
|
|
8007
|
-
const
|
|
8024
|
+
const model = ((_g = this.modelBox) === null || _g === void 0 ? void 0 : _g.corners) ? measureModel(anchor) : null;
|
|
8025
|
+
const ringPoint = { x: anchor.x + (model || pixelOffset).x, y: anchor.y + (model || pixelOffset).y };
|
|
8008
8026
|
const boxAnchor = boxBlend > 0 && boxTarget ? {
|
|
8009
8027
|
x: ringPoint.x + (boxTarget.x - ringPoint.x) * boxBlend,
|
|
8010
8028
|
y: ringPoint.y + (boxTarget.y - ringPoint.y) * boxBlend
|
|
@@ -8015,13 +8033,13 @@ var EntityLabel;
|
|
|
8015
8033
|
return null;
|
|
8016
8034
|
if (onScreen && !this.imageAsked && this.groupId && !HasIcon(this.options) && !this.options.photo) {
|
|
8017
8035
|
this.imageAsked = true;
|
|
8018
|
-
(
|
|
8036
|
+
(_h = this.visualRegister) === null || _h === void 0 ? void 0 : _h.RequestLabelImage((_j = this.rego) === null || _j === void 0 ? void 0 : _j.entityId);
|
|
8019
8037
|
}
|
|
8020
8038
|
const transientPivot = (this.Transient && !this.Persistent ?
|
|
8021
|
-
(this.hoverFade.to === 0 ? this.retainedHoverPosition : (
|
|
8039
|
+
(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);
|
|
8022
8040
|
const hover = (presentation !== "icon" ?
|
|
8023
8041
|
(this.Transient && !this.Persistent && this.hoverFade.to === 0 ? this.retainedHoverPosition :
|
|
8024
|
-
(
|
|
8042
|
+
(_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);
|
|
8025
8043
|
const reused = !this.Transient || this.Persistent;
|
|
8026
8044
|
const now = Date.now();
|
|
8027
8045
|
const animateCursor = () => {
|
|
@@ -8045,13 +8063,13 @@ var EntityLabel;
|
|
|
8045
8063
|
const cursorAmount = cursorMotion.amount;
|
|
8046
8064
|
leader = normalLeader || cursorAmount > 0;
|
|
8047
8065
|
// An icon keeps its callout centred on the glyph rather than following the cursor across it.
|
|
8048
|
-
const icon = boxAnchor ? null : measureIcon();
|
|
8066
|
+
const icon = boxAnchor ? null : measureIcon() || model;
|
|
8049
8067
|
const iconRing = icon ? Math.max(ICON_RING_MIN, icon.radius + ICON_RING_PADDING) : 0;
|
|
8050
8068
|
// A boxed model is pinned the same way, with its stem leaving the top of the box outline.
|
|
8051
8069
|
const pinned = Boolean(icon || boxAnchor);
|
|
8052
8070
|
const pivot = pinned ? undefined : transientPivot;
|
|
8053
|
-
let x = boxAnchor ? boxAnchor.x : ((
|
|
8054
|
-
let y = boxAnchor ? boxAnchor.y : ((
|
|
8071
|
+
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;
|
|
8072
|
+
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;
|
|
8055
8073
|
if (cursorAmount > 0 && !pinned) {
|
|
8056
8074
|
// A leader-less label starts centred on its anchor; preserve that box as it lifts away.
|
|
8057
8075
|
if (!normalLeader) {
|
|
@@ -8106,7 +8124,7 @@ var EntityLabel;
|
|
|
8106
8124
|
const width = ele.offsetWidth / coordinates.scaleX;
|
|
8107
8125
|
const height = ele.offsetHeight / coordinates.scaleY;
|
|
8108
8126
|
let clusterBounds;
|
|
8109
|
-
if ((
|
|
8127
|
+
if ((_t = this.options.cluster) === null || _t === void 0 ? void 0 : _t.enabled) {
|
|
8110
8128
|
const textWidth = (sourceText === null || sourceText === void 0 ? void 0 : sourceText.offsetWidth) || 0;
|
|
8111
8129
|
const textHeight = (sourceText === null || sourceText === void 0 ? void 0 : sourceText.offsetHeight) || 0;
|
|
8112
8130
|
// A photo is never part of the footprint, so its arrival cannot change which cluster a label joins.
|
|
@@ -8140,7 +8158,7 @@ var EntityLabel;
|
|
|
8140
8158
|
if (!leader && (bounds.x + bounds.width < 0 || bounds.y + bounds.height < 0 || bounds.x > this.viewer.canvas.clientWidth || bounds.y > this.viewer.canvas.clientHeight)) {
|
|
8141
8159
|
return null;
|
|
8142
8160
|
}
|
|
8143
|
-
const state = (
|
|
8161
|
+
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 });
|
|
8144
8162
|
const opacity = this.prePositioned || hoverOnly || MAX_DISTANCE === Infinity ? 1 : 1 - distance / MAX_DISTANCE;
|
|
8145
8163
|
return {
|
|
8146
8164
|
canFlip: leader && presentation !== "icon", hasLeader: leader,
|
|
@@ -8148,8 +8166,8 @@ var EntityLabel;
|
|
|
8148
8166
|
transient: this.Transient || Boolean(hover) || cursorAmount > 0,
|
|
8149
8167
|
extendedGeometry: visual instanceof Entity && Boolean(visual.polygon || visual.polyline || visual.corridor),
|
|
8150
8168
|
viewportWidth: this.viewer.canvas.clientWidth,
|
|
8151
|
-
id: this.id, entityKey: ((
|
|
8152
|
-
entityId: (
|
|
8169
|
+
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,
|
|
8170
|
+
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,
|
|
8153
8171
|
iconRadius: icon === null || icon === void 0 ? void 0 : icon.radius, modelBox: boxed || undefined, boxBlend,
|
|
8154
8172
|
// Priority shows the hovered member individually without dragging its cluster along.
|
|
8155
8173
|
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
|
|
@@ -13811,7 +13829,9 @@ var VisualsRegister;
|
|
|
13811
13829
|
entityId: params.entityId,
|
|
13812
13830
|
menuItemId: params.menuItemId,
|
|
13813
13831
|
position: params.position ? { ...params.position } : undefined,
|
|
13814
|
-
createIfMissing: params.createIfMissing !== false
|
|
13832
|
+
createIfMissing: params.createIfMissing !== false,
|
|
13833
|
+
text: params.text,
|
|
13834
|
+
pos3d: params.pos3d ? Cartesian3.clone(params.pos3d) : undefined
|
|
13815
13835
|
};
|
|
13816
13836
|
const doc = this.viewer.container.ownerDocument;
|
|
13817
13837
|
// Only a label following the cursor should go when the cursor leaves the canvas.
|
|
@@ -13827,6 +13847,13 @@ var VisualsRegister;
|
|
|
13827
13847
|
this.SyncLabelHover();
|
|
13828
13848
|
this.viewer.scene.requestRender();
|
|
13829
13849
|
}
|
|
13850
|
+
/*
|
|
13851
|
+
* Which Entity holds the label hover, if any, so a caller can leave another's hover alone.
|
|
13852
|
+
*/
|
|
13853
|
+
GetLabelHoverTarget() {
|
|
13854
|
+
const hover = this.labelHover;
|
|
13855
|
+
return hover ? { entityId: hover.entityId, menuItemId: hover.menuItemId } : undefined;
|
|
13856
|
+
}
|
|
13830
13857
|
/*
|
|
13831
13858
|
* Whether the label hover is on this Entity, with or without a cursor position.
|
|
13832
13859
|
*/
|
|
@@ -13839,7 +13866,7 @@ var VisualsRegister;
|
|
|
13839
13866
|
return (hover === null || hover === void 0 ? void 0 : hover.entityId) === entityId && (hover.menuItemId == null || hover.menuItemId === menuItemId) ? hover.position : undefined;
|
|
13840
13867
|
}
|
|
13841
13868
|
SyncLabelHover() {
|
|
13842
|
-
var _a, _b, _c;
|
|
13869
|
+
var _a, _b, _c, _d;
|
|
13843
13870
|
const hover = this.labelHover;
|
|
13844
13871
|
if (!hover || this.disposed) {
|
|
13845
13872
|
return;
|
|
@@ -13871,12 +13898,24 @@ var VisualsRegister;
|
|
|
13871
13898
|
this.ReleaseHoverLabel(true);
|
|
13872
13899
|
return;
|
|
13873
13900
|
}
|
|
13901
|
+
const hoverOptions = {
|
|
13902
|
+
presentation: "text",
|
|
13903
|
+
text: (_b = hover.text) !== null && _b !== void 0 ? _b : group === null || group === void 0 ? void 0 : group.options.text,
|
|
13904
|
+
selectOnClick: false
|
|
13905
|
+
};
|
|
13874
13906
|
if (this.hoverLabel && !this.hoverLabel.Disposed) {
|
|
13907
|
+
// Rebuilds only when the text changed, as it does when the pointer crosses a cell.
|
|
13908
|
+
if (hover.pos3d && !Cartesian3.equals(hover.pos3d, this.hoverLabel.pos3d)) {
|
|
13909
|
+
this.hoverLabel.pos3d = hover.pos3d;
|
|
13910
|
+
}
|
|
13911
|
+
if (hover.text != null) {
|
|
13912
|
+
this.hoverLabel.Configure(hoverOptions);
|
|
13913
|
+
}
|
|
13875
13914
|
this.hoverLabel.SetHoverVisible(true);
|
|
13876
13915
|
return;
|
|
13877
13916
|
}
|
|
13878
13917
|
// Query-only icon groups have coordinates but no registered scene visual to attach to.
|
|
13879
|
-
if (!rego && group && [group.item.longitude, group.item.latitude, (
|
|
13918
|
+
if (!rego && group && [group.item.longitude, group.item.latitude, (_c = group.item.altitude) !== null && _c !== void 0 ? _c : 0].every(Number.isFinite)) {
|
|
13880
13919
|
rego = {
|
|
13881
13920
|
entityId: hover.entityId,
|
|
13882
13921
|
menuItemId: hover.menuItemId,
|
|
@@ -13898,15 +13937,12 @@ var VisualsRegister;
|
|
|
13898
13937
|
rego,
|
|
13899
13938
|
viewer: this.viewer,
|
|
13900
13939
|
visualRegister: this,
|
|
13901
|
-
api: (
|
|
13940
|
+
api: (_d = this.ApiGetters) === null || _d === void 0 ? void 0 : _d.GetBruceApi(),
|
|
13902
13941
|
persistent: false,
|
|
13903
13942
|
transient: true,
|
|
13904
13943
|
hoverRingState: this.hoverRingState,
|
|
13905
|
-
|
|
13906
|
-
|
|
13907
|
-
text: group === null || group === void 0 ? void 0 : group.options.text,
|
|
13908
|
-
selectOnClick: false
|
|
13909
|
-
},
|
|
13944
|
+
pos3d: hover.pos3d,
|
|
13945
|
+
options: hoverOptions,
|
|
13910
13946
|
visible: () => {
|
|
13911
13947
|
var _a;
|
|
13912
13948
|
const current = this.GetState(hover);
|
|
@@ -46823,6 +46859,10 @@ var TextureFrameSeriesAnimator;
|
|
|
46823
46859
|
TextureFrameSeriesAnimator.AppearanceSignature = AppearanceSignature;
|
|
46824
46860
|
const MAX_COMPOSITE_TEXELS = 2048;
|
|
46825
46861
|
const VALUE_DILATE_PASSES = 2;
|
|
46862
|
+
// Decoded frame bytes kept at once. A 2048 square composite is 16 MB, so a long series cannot all stay.
|
|
46863
|
+
const MAX_CACHED_FRAME_BYTES = 256 * 1024 * 1024;
|
|
46864
|
+
// Fetched bytes kept for decoding again, a small fraction of what they decode to.
|
|
46865
|
+
const MAX_RAW_FRAME_BYTES = 96 * 1024 * 1024;
|
|
46826
46866
|
/*
|
|
46827
46867
|
* Rewrites a packed 16 bit value as the 8 bit grey the colour ramps index on.
|
|
46828
46868
|
*
|
|
@@ -46843,19 +46883,33 @@ var TextureFrameSeriesAnimator;
|
|
|
46843
46883
|
/*
|
|
46844
46884
|
* Bleeds covered values outward into uncovered texels, leaving alpha untouched.
|
|
46845
46885
|
*/
|
|
46846
|
-
function
|
|
46886
|
+
function DilateValues(value, width, height, passes, packed) {
|
|
46847
46887
|
const texels = width * height;
|
|
46848
|
-
|
|
46888
|
+
// 255 for a texel that had data to begin with, otherwise the 1 based pass that filled it, so a pass
|
|
46889
|
+
// reads only texels filled before it and can work in place.
|
|
46890
|
+
const stamp = new Uint8Array(texels);
|
|
46849
46891
|
for (let p = 0; p < texels; p++) {
|
|
46850
|
-
|
|
46892
|
+
stamp[p] = value[p * 4 + 3] !== 0 ? 255 : 0;
|
|
46851
46893
|
}
|
|
46894
|
+
// Whether a texel or a horizontal neighbour had data, so empty space is skipped in three reads.
|
|
46895
|
+
const across = new Uint8Array(texels);
|
|
46852
46896
|
for (let pass = 0; pass < passes; pass++) {
|
|
46853
|
-
const
|
|
46854
|
-
const
|
|
46897
|
+
const current = pass + 1;
|
|
46898
|
+
const had = (p) => stamp[p] !== 0 && stamp[p] !== current;
|
|
46899
|
+
for (let y = 0; y < height; y++) {
|
|
46900
|
+
const row = y * width;
|
|
46901
|
+
for (let x = 0; x < width; x++) {
|
|
46902
|
+
const p = row + x;
|
|
46903
|
+
across[p] = (had(p) || (x > 0 && had(p - 1)) || (x + 1 < width && had(p + 1))) ? 1 : 0;
|
|
46904
|
+
}
|
|
46905
|
+
}
|
|
46855
46906
|
for (let y = 0; y < height; y++) {
|
|
46856
46907
|
for (let x = 0; x < width; x++) {
|
|
46857
46908
|
const p = y * width + x;
|
|
46858
|
-
if (
|
|
46909
|
+
if (stamp[p] !== 0) {
|
|
46910
|
+
continue;
|
|
46911
|
+
}
|
|
46912
|
+
if (!across[p] && !(y > 0 && across[p - width]) && !(y + 1 < height && across[p + width])) {
|
|
46859
46913
|
continue;
|
|
46860
46914
|
}
|
|
46861
46915
|
let sum = 0;
|
|
@@ -46864,13 +46918,13 @@ var TextureFrameSeriesAnimator;
|
|
|
46864
46918
|
for (let dx = -1; dx <= 1; dx++) {
|
|
46865
46919
|
const nx = x + dx;
|
|
46866
46920
|
const ny = y + dy;
|
|
46867
|
-
if (nx < 0 || ny < 0 || nx >= width || ny >= height || !
|
|
46921
|
+
if (nx < 0 || ny < 0 || nx >= width || ny >= height || !had(ny * width + nx)) {
|
|
46868
46922
|
continue;
|
|
46869
46923
|
}
|
|
46870
46924
|
const at = (ny * width + nx) * 4;
|
|
46871
46925
|
// Averaged as a value rather than per byte: averaging a packed low byte
|
|
46872
46926
|
// on its own wraps at every 256th step and speckles the bled edge.
|
|
46873
|
-
sum += packed ?
|
|
46927
|
+
sum += packed ? value[at] * 256 + value[at + 1] : value[at];
|
|
46874
46928
|
hits++;
|
|
46875
46929
|
}
|
|
46876
46930
|
}
|
|
@@ -46886,18 +46940,25 @@ var TextureFrameSeriesAnimator;
|
|
|
46886
46940
|
value[p * 4 + 1] = v;
|
|
46887
46941
|
value[p * 4 + 2] = v;
|
|
46888
46942
|
}
|
|
46889
|
-
|
|
46943
|
+
stamp[p] = current;
|
|
46890
46944
|
}
|
|
46891
46945
|
}
|
|
46892
46946
|
}
|
|
46893
46947
|
}
|
|
46894
46948
|
}
|
|
46949
|
+
TextureFrameSeriesAnimator.DilateValues = DilateValues;
|
|
46950
|
+
/*
|
|
46951
|
+
* Bytes one cached frame holds, counting a buffer shared between colour and value once.
|
|
46952
|
+
*/
|
|
46953
|
+
function frameBytes(pixels, value) {
|
|
46954
|
+
return (pixels ? pixels.byteLength : 0) + (value && value !== pixels ? value.byteLength : 0);
|
|
46955
|
+
}
|
|
46895
46956
|
const DEFAULT_CROSSFADE_MS = 900;
|
|
46896
46957
|
const DEFAULT_LOW_COLOR = { red: 255, green: 255, blue: 255, alpha: 0.12 };
|
|
46897
46958
|
const DEFAULT_HIGH_COLOR = { red: 21, green: 96, blue: 196, alpha: 0.92 };
|
|
46898
46959
|
class Animator {
|
|
46899
46960
|
constructor(options) {
|
|
46900
|
-
var _a, _b, _c;
|
|
46961
|
+
var _a, _b, _c, _d;
|
|
46901
46962
|
this.removeOnTick = null;
|
|
46902
46963
|
this.removeCrossfadeTick = null;
|
|
46903
46964
|
this.disposed = false;
|
|
@@ -46928,6 +46989,15 @@ var TextureFrameSeriesAnimator;
|
|
|
46928
46989
|
this.extremesLoad = null;
|
|
46929
46990
|
this.floorPixels = null;
|
|
46930
46991
|
this.ceilingPixels = null;
|
|
46992
|
+
// How far through the fade from the previous value frame to the presented one, 1 when settled.
|
|
46993
|
+
this.fadeMix = 1;
|
|
46994
|
+
// Where the current fade started from, above zero when it took over from one still running.
|
|
46995
|
+
this.fadeMixStart = 0;
|
|
46996
|
+
// Version the value canvas was last written at, since in value only mode it is written on demand.
|
|
46997
|
+
this.valueCanvasVersion = -1;
|
|
46998
|
+
this.rawBytes = 0;
|
|
46999
|
+
this.cachedBytes = 0;
|
|
47000
|
+
this.lastFrameBytes = 0;
|
|
46931
47001
|
if (!options.entity.polygon) {
|
|
46932
47002
|
throw new Error("TextureFrameSeriesAnimator requires an entity with polygon graphics.");
|
|
46933
47003
|
}
|
|
@@ -46975,6 +47045,9 @@ var TextureFrameSeriesAnimator;
|
|
|
46975
47045
|
this.maskBaseline = (_c = options.maskBaseline) !== null && _c !== void 0 ? _c : this.tiles.some((tile) => Boolean(tile.BaselineMask));
|
|
46976
47046
|
this.valueCanvas = this.produceValueCanvas ? document.createElement("canvas") : null;
|
|
46977
47047
|
this.driveMaterial = options.driveMaterial !== false;
|
|
47048
|
+
this.valueOnly = this.produceValueCanvas && !this.driveMaterial;
|
|
47049
|
+
this.rawCache = new Array(this.frames.length).fill(null);
|
|
47050
|
+
this.cacheBudget = (_d = options.cacheBytes) !== null && _d !== void 0 ? _d : MAX_CACHED_FRAME_BYTES;
|
|
46978
47051
|
this.originalMaterial = options.entity.polygon.material;
|
|
46979
47052
|
this.imageProperty = new CallbackProperty(() => this.pool[this.poolIdx], false);
|
|
46980
47053
|
if (this.driveMaterial) {
|
|
@@ -47040,7 +47113,40 @@ var TextureFrameSeriesAnimator;
|
|
|
47040
47113
|
* or null unless the instance was constructed with produceValueCanvas.
|
|
47041
47114
|
*/
|
|
47042
47115
|
GetValueCanvas() {
|
|
47043
|
-
|
|
47116
|
+
if (!this.valueDims) {
|
|
47117
|
+
return null;
|
|
47118
|
+
}
|
|
47119
|
+
if (this.valueOnly && this.valueCanvasVersion !== this.presentVersion && this.displayedValuePixels) {
|
|
47120
|
+
this.writeInto(this.valueCanvas, this.displayedValuePixels, this.valueDims);
|
|
47121
|
+
this.valueCanvasVersion = this.presentVersion;
|
|
47122
|
+
}
|
|
47123
|
+
return this.valueCanvas;
|
|
47124
|
+
}
|
|
47125
|
+
/**
|
|
47126
|
+
* The presented frame's value pixels, or null before one. Shares the cached array, so never write to it.
|
|
47127
|
+
*/
|
|
47128
|
+
GetValueRaster() {
|
|
47129
|
+
if (!this.valueDims || !this.displayedValuePixels) {
|
|
47130
|
+
return null;
|
|
47131
|
+
}
|
|
47132
|
+
return { data: this.displayedValuePixels, width: this.valueDims.width, height: this.valueDims.height };
|
|
47133
|
+
}
|
|
47134
|
+
/**
|
|
47135
|
+
* The two value frames being faded between and the mix, or null unless the material is not driven.
|
|
47136
|
+
*/
|
|
47137
|
+
GetValueFade() {
|
|
47138
|
+
if (!this.valueOnly || !this.valueDims || !this.displayedValuePixels) {
|
|
47139
|
+
return null;
|
|
47140
|
+
}
|
|
47141
|
+
const previous = this.fadeFromValuePixels;
|
|
47142
|
+
const from = previous && previous.length === this.displayedValuePixels.length ? previous : null;
|
|
47143
|
+
return {
|
|
47144
|
+
from,
|
|
47145
|
+
to: this.displayedValuePixels,
|
|
47146
|
+
width: this.valueDims.width,
|
|
47147
|
+
height: this.valueDims.height,
|
|
47148
|
+
mix: from ? this.fadeMix : 1
|
|
47149
|
+
};
|
|
47044
47150
|
}
|
|
47045
47151
|
/**
|
|
47046
47152
|
* Increments every time a frame is presented, so a consumer holding the value canvas can tell
|
|
@@ -47107,7 +47213,11 @@ var TextureFrameSeriesAnimator;
|
|
|
47107
47213
|
if (this.currentFrameIndex < 0) {
|
|
47108
47214
|
return this.frameCache[0] ? -1 : 0;
|
|
47109
47215
|
}
|
|
47110
|
-
|
|
47216
|
+
// Full once another frame will not fit, so a prefetch only displaces a frame further from the
|
|
47217
|
+
// playhead than itself and never trades the two ends of the window back and forth.
|
|
47218
|
+
const full = this.cachedBytes + this.lastFrameBytes > this.cacheBudget;
|
|
47219
|
+
const reach = full ? this.farthestCachedDistance() : n;
|
|
47220
|
+
for (let d = 0; d < Math.min(n, reach); d++) {
|
|
47111
47221
|
const forward = this.currentFrameIndex + d;
|
|
47112
47222
|
if (forward < n && !this.frameCache[forward]) {
|
|
47113
47223
|
return forward;
|
|
@@ -47119,6 +47229,68 @@ var TextureFrameSeriesAnimator;
|
|
|
47119
47229
|
}
|
|
47120
47230
|
return -1;
|
|
47121
47231
|
}
|
|
47232
|
+
farthestCachedDistance() {
|
|
47233
|
+
let farthest = 0;
|
|
47234
|
+
for (let i = 0; i < this.frameCache.length; i++) {
|
|
47235
|
+
if (this.frameCache[i]) {
|
|
47236
|
+
farthest = Math.max(farthest, Math.abs(i - this.currentFrameIndex));
|
|
47237
|
+
}
|
|
47238
|
+
}
|
|
47239
|
+
return farthest;
|
|
47240
|
+
}
|
|
47241
|
+
/*
|
|
47242
|
+
* Drops decoded frames furthest from the playhead until the cache fits its budget again.
|
|
47243
|
+
*/
|
|
47244
|
+
evictDecoded(keep) {
|
|
47245
|
+
// A tinted fade reads its target from the cache each tick, where a value fade holds the arrays.
|
|
47246
|
+
const fading = !this.valueOnly && this.fadeStartMs !== null ? this.fadeToIndex : -1;
|
|
47247
|
+
while (this.cachedBytes > this.cacheBudget) {
|
|
47248
|
+
let victim = -1;
|
|
47249
|
+
let distance = -1;
|
|
47250
|
+
for (let i = 0; i < this.frameCache.length; i++) {
|
|
47251
|
+
if (!this.frameCache[i] || i === keep || i === this.currentFrameIndex || i === fading) {
|
|
47252
|
+
continue;
|
|
47253
|
+
}
|
|
47254
|
+
const d = Math.abs(i - this.currentFrameIndex);
|
|
47255
|
+
if (d > distance) {
|
|
47256
|
+
distance = d;
|
|
47257
|
+
victim = i;
|
|
47258
|
+
}
|
|
47259
|
+
}
|
|
47260
|
+
if (victim < 0) {
|
|
47261
|
+
return;
|
|
47262
|
+
}
|
|
47263
|
+
this.cachedBytes -= frameBytes(this.frameCache[victim], this.valueCache[victim]);
|
|
47264
|
+
this.frameCache[victim] = null;
|
|
47265
|
+
this.valueCache[victim] = null;
|
|
47266
|
+
}
|
|
47267
|
+
}
|
|
47268
|
+
/*
|
|
47269
|
+
* Keeps a frame's fetched bytes unless they are large, as a tile major archive's are.
|
|
47270
|
+
*/
|
|
47271
|
+
keepRaw(idx, buffer, base) {
|
|
47272
|
+
if (buffer.byteLength > MAX_RAW_FRAME_BYTES / 16) {
|
|
47273
|
+
return;
|
|
47274
|
+
}
|
|
47275
|
+
this.rawCache[idx] = { buffer, base };
|
|
47276
|
+
this.rawBytes += buffer.byteLength;
|
|
47277
|
+
while (this.rawBytes > MAX_RAW_FRAME_BYTES) {
|
|
47278
|
+
let victim = -1;
|
|
47279
|
+
let distance = -1;
|
|
47280
|
+
for (let i = 0; i < this.rawCache.length; i++) {
|
|
47281
|
+
const d = Math.abs(i - this.currentFrameIndex);
|
|
47282
|
+
if (this.rawCache[i] && i !== idx && d > distance) {
|
|
47283
|
+
distance = d;
|
|
47284
|
+
victim = i;
|
|
47285
|
+
}
|
|
47286
|
+
}
|
|
47287
|
+
if (victim < 0) {
|
|
47288
|
+
return;
|
|
47289
|
+
}
|
|
47290
|
+
this.rawBytes -= this.rawCache[victim].buffer.byteLength;
|
|
47291
|
+
this.rawCache[victim] = null;
|
|
47292
|
+
}
|
|
47293
|
+
}
|
|
47122
47294
|
pumpFetchQueue() {
|
|
47123
47295
|
if (this.disposed || this.inFlightIndex !== -1) {
|
|
47124
47296
|
return;
|
|
@@ -47162,10 +47334,18 @@ var TextureFrameSeriesAnimator;
|
|
|
47162
47334
|
lo = Math.min(lo, at.ByteOffset);
|
|
47163
47335
|
hi = Math.max(hi, at.ByteOffset + at.ByteLength);
|
|
47164
47336
|
}
|
|
47165
|
-
|
|
47166
|
-
const
|
|
47167
|
-
if (
|
|
47168
|
-
|
|
47337
|
+
let buffer;
|
|
47338
|
+
const raw = this.rawCache[idx];
|
|
47339
|
+
if (raw && raw.base === lo) {
|
|
47340
|
+
buffer = raw.buffer;
|
|
47341
|
+
}
|
|
47342
|
+
else {
|
|
47343
|
+
const response = await fetch(this.archiveUrl, { headers: { Range: `bytes=${lo}-${hi - 1}` } });
|
|
47344
|
+
buffer = await response.arrayBuffer();
|
|
47345
|
+
if (this.disposed) {
|
|
47346
|
+
return;
|
|
47347
|
+
}
|
|
47348
|
+
this.keepRaw(idx, buffer, lo);
|
|
47169
47349
|
}
|
|
47170
47350
|
// Awaited before decoding rather than applied later, so no frame is ever cached unmasked and
|
|
47171
47351
|
// then corrected once the mask lands.
|
|
@@ -47177,9 +47357,15 @@ var TextureFrameSeriesAnimator;
|
|
|
47177
47357
|
if (this.disposed) {
|
|
47178
47358
|
return;
|
|
47179
47359
|
}
|
|
47360
|
+
if (this.frameCache[idx]) {
|
|
47361
|
+
this.cachedBytes -= frameBytes(this.frameCache[idx], this.valueCache[idx]);
|
|
47362
|
+
}
|
|
47180
47363
|
this.frameCache[idx] = decoded.pixels;
|
|
47181
47364
|
this.frameDims[idx] = { width: decoded.width, height: decoded.height };
|
|
47182
47365
|
this.valueCache[idx] = decoded.valuePixels || null;
|
|
47366
|
+
this.lastFrameBytes = frameBytes(decoded.pixels, decoded.valuePixels || null);
|
|
47367
|
+
this.cachedBytes += this.lastFrameBytes;
|
|
47368
|
+
this.evictDecoded(idx);
|
|
47183
47369
|
if (this.shouldPresent(idx, isTarget)) {
|
|
47184
47370
|
this.beginCrossfadeTo(idx);
|
|
47185
47371
|
}
|
|
@@ -47249,6 +47435,13 @@ var TextureFrameSeriesAnimator;
|
|
|
47249
47435
|
*/
|
|
47250
47436
|
tint(imageData) {
|
|
47251
47437
|
const canvas = { width: imageData.width, height: imageData.height };
|
|
47438
|
+
if (this.valueOnly) {
|
|
47439
|
+
// One array serves as both, since nothing will ever display the colour.
|
|
47440
|
+
const values = imageData.data;
|
|
47441
|
+
this.applyBaselineMask(values, undefined, canvas.width, canvas.height);
|
|
47442
|
+
DilateValues(values, canvas.width, canvas.height, VALUE_DILATE_PASSES, IsPackedValue(this.metadata));
|
|
47443
|
+
return { pixels: values, width: canvas.width, height: canvas.height, valuePixels: values };
|
|
47444
|
+
}
|
|
47252
47445
|
// Copied before tinting: the ramp is a lerp between two colours, so the value cannot be
|
|
47253
47446
|
// recovered from the tinted pixels afterwards.
|
|
47254
47447
|
const valuePixels = this.produceValueCanvas ? new Uint8ClampedArray(imageData.data) : undefined;
|
|
@@ -47269,7 +47462,7 @@ var TextureFrameSeriesAnimator;
|
|
|
47269
47462
|
}
|
|
47270
47463
|
this.applyBaselineMask(imageData.data, valuePixels, canvas.width, canvas.height);
|
|
47271
47464
|
if (valuePixels) {
|
|
47272
|
-
|
|
47465
|
+
DilateValues(valuePixels, canvas.width, canvas.height, VALUE_DILATE_PASSES, IsPackedValue(this.metadata));
|
|
47273
47466
|
}
|
|
47274
47467
|
return { pixels: imageData.data, width: canvas.width, height: canvas.height, valuePixels };
|
|
47275
47468
|
}
|
|
@@ -47527,6 +47720,10 @@ var TextureFrameSeriesAnimator;
|
|
|
47527
47720
|
}
|
|
47528
47721
|
}
|
|
47529
47722
|
beginCrossfadeTo(idx) {
|
|
47723
|
+
if (this.valueOnly) {
|
|
47724
|
+
this.beginValueFadeTo(idx);
|
|
47725
|
+
return;
|
|
47726
|
+
}
|
|
47530
47727
|
const to = this.frameCache[idx];
|
|
47531
47728
|
this.fadeFromValuePixels = this.displayedValuePixels === null && this.valueCache[idx]
|
|
47532
47729
|
? new Uint8ClampedArray(this.valueCache[idx].length)
|
|
@@ -47536,12 +47733,53 @@ var TextureFrameSeriesAnimator;
|
|
|
47536
47733
|
this.fadeToDims = this.frameDims[idx];
|
|
47537
47734
|
this.fadeStartMs = Now();
|
|
47538
47735
|
}
|
|
47736
|
+
/*
|
|
47737
|
+
* Presents a value frame at once and leaves the fade to the consumer, which blends on the GPU.
|
|
47738
|
+
*/
|
|
47739
|
+
beginValueFadeTo(idx) {
|
|
47740
|
+
const to = this.valueCache[idx];
|
|
47741
|
+
if (!to || to === this.displayedValuePixels) {
|
|
47742
|
+
return;
|
|
47743
|
+
}
|
|
47744
|
+
// A new target mid fade keeps whichever frame is mostly on screen and carries on from the current
|
|
47745
|
+
// mix, so frames landing in quick succession do not jump the sheet back.
|
|
47746
|
+
const midFade = this.fadeStartMs !== null && Boolean(this.fadeFromValuePixels);
|
|
47747
|
+
if (midFade && this.fadeMix < 0.5) {
|
|
47748
|
+
this.fadeMixStart = this.fadeMix;
|
|
47749
|
+
}
|
|
47750
|
+
else {
|
|
47751
|
+
this.fadeFromValuePixels = this.displayedValuePixels;
|
|
47752
|
+
this.fadeMixStart = 0;
|
|
47753
|
+
}
|
|
47754
|
+
this.displayedValuePixels = to;
|
|
47755
|
+
this.valueDims = this.frameDims[idx];
|
|
47756
|
+
this.fadeToIndex = idx;
|
|
47757
|
+
this.fadeMix = this.fadeFromValuePixels ? this.fadeMixStart : 1;
|
|
47758
|
+
this.fadeStartMs = this.fadeFromValuePixels ? Now() : null;
|
|
47759
|
+
this.presentVersion++;
|
|
47760
|
+
this.requestRender();
|
|
47761
|
+
}
|
|
47762
|
+
requestRender() {
|
|
47763
|
+
if (this.viewer && this.viewer.scene && this.viewer.scene.requestRenderMode) {
|
|
47764
|
+
this.viewer.scene.requestRender();
|
|
47765
|
+
}
|
|
47766
|
+
}
|
|
47539
47767
|
tickCrossfade() {
|
|
47540
47768
|
if (this.disposed || this.fadeStartMs === null) {
|
|
47541
47769
|
return;
|
|
47542
47770
|
}
|
|
47543
47771
|
const t = Math.min(1, (Now() - this.fadeStartMs) / this.crossfadeMs);
|
|
47544
47772
|
const eased = easeInOutQuad(t);
|
|
47773
|
+
if (this.valueOnly) {
|
|
47774
|
+
this.fadeMix = this.fadeMixStart + (1 - this.fadeMixStart) * eased;
|
|
47775
|
+
if (t >= 1) {
|
|
47776
|
+
this.fadeStartMs = null;
|
|
47777
|
+
this.fadeFromValuePixels = null;
|
|
47778
|
+
this.fadeMix = 1;
|
|
47779
|
+
}
|
|
47780
|
+
this.requestRender();
|
|
47781
|
+
return;
|
|
47782
|
+
}
|
|
47545
47783
|
const from = this.fadeFromPixels;
|
|
47546
47784
|
const to = this.frameCache[this.fadeToIndex];
|
|
47547
47785
|
const n = Math.min(from.length, to.length);
|
|
@@ -47581,9 +47819,7 @@ var TextureFrameSeriesAnimator;
|
|
|
47581
47819
|
this.valueDims = dims;
|
|
47582
47820
|
}
|
|
47583
47821
|
this.presentVersion++;
|
|
47584
|
-
|
|
47585
|
-
this.viewer.scene.requestRender();
|
|
47586
|
-
}
|
|
47822
|
+
this.requestRender();
|
|
47587
47823
|
}
|
|
47588
47824
|
}
|
|
47589
47825
|
TextureFrameSeriesAnimator.Animator = Animator;
|
|
@@ -47606,13 +47842,15 @@ var TextureValueLabels;
|
|
|
47606
47842
|
const MIN_VISIBLE_PIXELS = 60;
|
|
47607
47843
|
const MAX_STRIDE = 512;
|
|
47608
47844
|
const STRIDE_HYSTERESIS = 1.4;
|
|
47609
|
-
|
|
47610
|
-
const
|
|
47611
|
-
const
|
|
47612
|
-
|
|
47613
|
-
|
|
47614
|
-
|
|
47615
|
-
const
|
|
47845
|
+
// Scene labels carry a leader and are wider than a value, so they want room and not many of them.
|
|
47846
|
+
const DEFAULT_MIN_SPACING_PIXELS = 140;
|
|
47847
|
+
const DEFAULT_MAX_LABELS = 60;
|
|
47848
|
+
// How far past the edge of the view an anchor may sit before its label is dropped.
|
|
47849
|
+
const LABEL_EDGE_MARGIN_PIXELS = 120;
|
|
47850
|
+
// Distance discount for a label already up when the cap forces a choice.
|
|
47851
|
+
const LABEL_KEEP_BIAS = 0.6;
|
|
47852
|
+
// How long a label outlives its cell leaving the wanted set, so one back a frame later does not blink.
|
|
47853
|
+
const LABEL_LINGER_MS = 300;
|
|
47616
47854
|
// 8 rather than 5: below this a cell reads as texture rather than as a boundary, and it also
|
|
47617
47855
|
// bounds the worst case, since a full screen of 8 px cells fits inside MAX_GRID_CELLS.
|
|
47618
47856
|
const DEFAULT_MIN_GRID_CELL_PIXELS = 8;
|
|
@@ -47626,7 +47864,8 @@ var TextureValueLabels;
|
|
|
47626
47864
|
const TILE_OUTLINE_ALPHA = 0.45;
|
|
47627
47865
|
// Cells per grid square at the coarsest. Beyond this a tile is outlined instead.
|
|
47628
47866
|
const MAX_GRID_STRIDE = 256;
|
|
47629
|
-
|
|
47867
|
+
// Times the pointer's ground hit is moved onto the sheet's own height before reading a cell.
|
|
47868
|
+
const HOVER_REFINE_STEPS = 3;
|
|
47630
47869
|
/**
|
|
47631
47870
|
* A clip outline from a projected ring, or null when the ring cannot be drawn.
|
|
47632
47871
|
* @param projected one entry per ring vertex, null where the vertex has no window coordinate
|
|
@@ -47720,11 +47959,25 @@ var TextureValueLabels;
|
|
|
47720
47959
|
this.sourceMap = null;
|
|
47721
47960
|
this.source = null;
|
|
47722
47961
|
this.painted = null;
|
|
47962
|
+
this.paintedRamp = null;
|
|
47723
47963
|
this.floor = null;
|
|
47724
47964
|
this.ceiling = null;
|
|
47725
47965
|
this.disposed = false;
|
|
47726
|
-
this.drawn = 0;
|
|
47727
47966
|
this.stride = 0;
|
|
47967
|
+
this.cellLabels = new Map();
|
|
47968
|
+
this.lingerTimer = null;
|
|
47969
|
+
// What the last draw was made from, so a frame that changes none of it draws nothing.
|
|
47970
|
+
this.drawnView = null;
|
|
47971
|
+
this.drawnInputs = [];
|
|
47972
|
+
this.scratchWorld = new Cartesian3();
|
|
47973
|
+
this.scratchWindow = new Cartesian2();
|
|
47974
|
+
this.scratchClip = new Cartesian4();
|
|
47975
|
+
// The camera's view and projection as one matrix, rebuilt per draw, or null outside 3D.
|
|
47976
|
+
this.viewProjection = null;
|
|
47977
|
+
this.viewProjectionScratch = new Matrix4();
|
|
47978
|
+
this.hovering = false;
|
|
47979
|
+
this.hoverFrame = 0;
|
|
47980
|
+
this.hoverAt = null;
|
|
47728
47981
|
this.viewer = options.viewer;
|
|
47729
47982
|
this.extent = options.extent;
|
|
47730
47983
|
this.settings = options.settings || {};
|
|
@@ -47734,6 +47987,9 @@ var TextureValueLabels;
|
|
|
47734
47987
|
this.sourceMap = TextureFrameSeriesAnimator.SourceMap(options.metadata);
|
|
47735
47988
|
this.tiles = options.tiles;
|
|
47736
47989
|
this.clipRing = options.clipRing;
|
|
47990
|
+
this.heightAt = options.heightAt;
|
|
47991
|
+
this.heightKey = options.heightKey;
|
|
47992
|
+
this.hover = options.hover;
|
|
47737
47993
|
this.canvas = document.createElement("canvas");
|
|
47738
47994
|
this.canvas.style.position = "absolute";
|
|
47739
47995
|
this.canvas.style.inset = "0";
|
|
@@ -47743,12 +47999,31 @@ var TextureValueLabels;
|
|
|
47743
47999
|
parent.appendChild(this.canvas);
|
|
47744
48000
|
}
|
|
47745
48001
|
this.ctx = this.canvas.getContext("2d");
|
|
48002
|
+
this.onPointerMove = (event) => {
|
|
48003
|
+
const bounds = this.viewer.canvas.getBoundingClientRect();
|
|
48004
|
+
this.hoverAt = { x: event.clientX - bounds.left, y: event.clientY - bounds.top };
|
|
48005
|
+
// At most once a frame, since a pointer reports far faster than anything is drawn.
|
|
48006
|
+
if (!this.hoverFrame) {
|
|
48007
|
+
this.hoverFrame = requestAnimationFrame(() => {
|
|
48008
|
+
this.hoverFrame = 0;
|
|
48009
|
+
this.updateHover();
|
|
48010
|
+
});
|
|
48011
|
+
}
|
|
48012
|
+
};
|
|
48013
|
+
this.onPointerLeave = () => {
|
|
48014
|
+
this.hoverAt = null;
|
|
48015
|
+
this.releaseHover();
|
|
48016
|
+
};
|
|
48017
|
+
if (this.hover && this.hover.register) {
|
|
48018
|
+
this.viewer.canvas.addEventListener("pointermove", this.onPointerMove, { passive: true });
|
|
48019
|
+
this.viewer.canvas.addEventListener("pointerleave", this.onPointerLeave);
|
|
48020
|
+
}
|
|
47746
48021
|
}
|
|
47747
48022
|
SetExtent(extent) {
|
|
47748
48023
|
this.extent = extent;
|
|
47749
48024
|
}
|
|
47750
48025
|
/**
|
|
47751
|
-
* The current frame's decoded values
|
|
48026
|
+
* The current frame's decoded values.
|
|
47752
48027
|
* @param source value pixels, one texel per cell
|
|
47753
48028
|
*/
|
|
47754
48029
|
SetSource(source) {
|
|
@@ -47756,11 +48031,17 @@ var TextureValueLabels;
|
|
|
47756
48031
|
}
|
|
47757
48032
|
/**
|
|
47758
48033
|
* The tinted pixels of the same frame, so the grid can skip cells the ramp painted nothing in.
|
|
47759
|
-
* @param painted RGBA of the presented frame, or null to fall back to coverage alone
|
|
48034
|
+
* @param painted RGBA of the presented frame, or null to fall back to the ramp or coverage alone
|
|
47760
48035
|
*/
|
|
47761
48036
|
SetPainted(painted) {
|
|
47762
48037
|
this.painted = painted;
|
|
47763
48038
|
}
|
|
48039
|
+
/**
|
|
48040
|
+
* The surface's 256 step RGBA ramp, for telling whether a cell was painted without a tinted frame.
|
|
48041
|
+
*/
|
|
48042
|
+
SetPaintedRamp(ramp) {
|
|
48043
|
+
this.paintedRamp = ramp;
|
|
48044
|
+
}
|
|
47764
48045
|
/**
|
|
47765
48046
|
* The archive's per-cell extremes, when it publishes them. Without these a label can still
|
|
47766
48047
|
* print the current reading, just not the range behind it.
|
|
@@ -47770,21 +48051,28 @@ var TextureValueLabels;
|
|
|
47770
48051
|
this.ceiling = ceiling;
|
|
47771
48052
|
}
|
|
47772
48053
|
GetDrawnCount() {
|
|
47773
|
-
return this.
|
|
48054
|
+
return this.cellLabels.size;
|
|
47774
48055
|
}
|
|
47775
48056
|
Clear() {
|
|
47776
48057
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
47777
|
-
this.
|
|
48058
|
+
this.drawnView = null;
|
|
47778
48059
|
}
|
|
47779
48060
|
Dispose() {
|
|
47780
48061
|
this.disposed = true;
|
|
48062
|
+
this.clearCellLabels();
|
|
48063
|
+
this.releaseHover();
|
|
48064
|
+
if (this.hoverFrame) {
|
|
48065
|
+
cancelAnimationFrame(this.hoverFrame);
|
|
48066
|
+
this.hoverFrame = 0;
|
|
48067
|
+
}
|
|
48068
|
+
this.viewer.canvas.removeEventListener("pointermove", this.onPointerMove);
|
|
48069
|
+
this.viewer.canvas.removeEventListener("pointerleave", this.onPointerLeave);
|
|
47781
48070
|
if (this.canvas.parentElement) {
|
|
47782
48071
|
this.canvas.parentElement.removeChild(this.canvas);
|
|
47783
48072
|
}
|
|
47784
48073
|
}
|
|
47785
48074
|
/**
|
|
47786
|
-
* Redraws
|
|
47787
|
-
* count it considers is bounded by the view rather than by the texture.
|
|
48075
|
+
* Redraws the grid and syncs the value labels, when anything they are drawn from has changed.
|
|
47788
48076
|
*/
|
|
47789
48077
|
Render() {
|
|
47790
48078
|
var _a, _b, _c;
|
|
@@ -47792,67 +48080,296 @@ var TextureValueLabels;
|
|
|
47792
48080
|
return;
|
|
47793
48081
|
}
|
|
47794
48082
|
const view = this.viewer.canvas;
|
|
48083
|
+
const inputs = [this.source, this.painted, this.paintedRamp, this.floor, this.ceiling,
|
|
48084
|
+
view.clientWidth, view.clientHeight, this.heightKey ? this.heightKey() : null];
|
|
48085
|
+
const camera = this.viewer.camera.viewMatrix;
|
|
48086
|
+
if (this.drawnView &&
|
|
48087
|
+
Matrix4.equals(this.drawnView, camera) &&
|
|
48088
|
+
inputs.every((input, i) => input === this.drawnInputs[i])) {
|
|
48089
|
+
return;
|
|
48090
|
+
}
|
|
47795
48091
|
if (this.canvas.width !== view.clientWidth || this.canvas.height !== view.clientHeight) {
|
|
47796
48092
|
this.canvas.width = view.clientWidth;
|
|
47797
48093
|
this.canvas.height = view.clientHeight;
|
|
47798
48094
|
}
|
|
47799
48095
|
this.Clear();
|
|
48096
|
+
this.drawnView = Matrix4.clone(camera, this.drawnView || undefined);
|
|
48097
|
+
this.drawnInputs = inputs;
|
|
48098
|
+
const projection = this.viewer.camera.frustum.projectionMatrix;
|
|
48099
|
+
this.viewProjection = this.viewer.scene.mode === SceneMode.SCENE3D
|
|
48100
|
+
? Matrix4.multiply(projection, camera, this.viewProjectionScratch)
|
|
48101
|
+
: null;
|
|
47800
48102
|
const cols = this.source.width;
|
|
47801
48103
|
const rows = this.source.height;
|
|
47802
48104
|
const window = this.visibleTexels(cols, rows);
|
|
47803
48105
|
if (!window) {
|
|
48106
|
+
this.syncCellLabels([]);
|
|
47804
48107
|
return;
|
|
47805
48108
|
}
|
|
47806
|
-
|
|
47807
|
-
|
|
47808
|
-
const
|
|
47809
|
-
const
|
|
48109
|
+
// Measured where the view is looking, since in an oblique view up close the middle of the visible
|
|
48110
|
+
// texels sits out toward the horizon, where cells are a fraction of the size.
|
|
48111
|
+
const focus = this.groundAtScreenCentre();
|
|
48112
|
+
const at = focus ? this.texelAt(focus.lon, focus.lat) : null;
|
|
48113
|
+
const focused = (Boolean(at) &&
|
|
48114
|
+
at.col >= window.col0 &&
|
|
48115
|
+
at.col <= window.col1 &&
|
|
48116
|
+
at.row >= window.row0 &&
|
|
48117
|
+
at.row <= window.row1);
|
|
48118
|
+
const midCol = focused ? at.col : (window.col0 + window.col1) / 2;
|
|
48119
|
+
const midRow = focused ? at.row : (window.row0 + window.row1) / 2;
|
|
48120
|
+
const a = this.projectCell(midCol, midRow, cols, rows);
|
|
48121
|
+
const b = this.projectCell(midCol + 1, midRow + 1, cols, rows);
|
|
47810
48122
|
if (!a || !b) {
|
|
48123
|
+
// Left as they are, since one frame where the focus will not project is not the data leaving.
|
|
47811
48124
|
return;
|
|
47812
48125
|
}
|
|
47813
48126
|
const cellPixels = Math.min(Math.abs(b.x - a.x), Math.abs(b.y - a.y));
|
|
47814
48127
|
// Ahead of the label threshold on purpose: a grid still reads at a cell size where a
|
|
47815
48128
|
// number no longer fits.
|
|
47816
48129
|
this.drawGrid(window, cols, rows, cellPixels);
|
|
47817
|
-
|
|
48130
|
+
// By area rather than the shorter side, which an oblique view foreshortens to a fraction of the other.
|
|
48131
|
+
const across = this.projectCell(midCol + 1, midRow, cols, rows);
|
|
48132
|
+
const down = this.projectCell(midCol, midRow + 1, cols, rows);
|
|
48133
|
+
const cellSize = across && down
|
|
48134
|
+
? Math.sqrt(Math.abs((across.x - a.x) * (down.y - a.y) - (across.y - a.y) * (down.x - a.x)))
|
|
48135
|
+
: cellPixels;
|
|
48136
|
+
const visibleWidth = cellSize * (window.col1 - window.col0 + 1);
|
|
48137
|
+
const visibleHeight = cellSize * (window.row1 - window.row0 + 1);
|
|
48138
|
+
if (cellSize < ((_a = this.settings.minCellPixels) !== null && _a !== void 0 ? _a : DEFAULT_MIN_CELL_PIXELS) ||
|
|
48139
|
+
Math.max(visibleWidth, visibleHeight) < MIN_VISIBLE_PIXELS) {
|
|
48140
|
+
this.syncCellLabels([]);
|
|
47818
48141
|
return;
|
|
47819
48142
|
}
|
|
47820
|
-
const visibleWidth = cellPixels * (window.col1 - window.col0 + 1);
|
|
47821
|
-
const visibleHeight = cellPixels * (window.row1 - window.row0 + 1);
|
|
47822
|
-
if (Math.max(visibleWidth, visibleHeight) < MIN_VISIBLE_PIXELS) {
|
|
47823
|
-
return;
|
|
47824
|
-
}
|
|
47825
|
-
const parts = this.settings.parts || ["value", "floor", "ceiling"];
|
|
47826
|
-
this.ctx.font = LABEL_FONT;
|
|
47827
|
-
this.ctx.textAlign = "center";
|
|
47828
48143
|
const maxLabels = (_b = this.settings.maxLabels) !== null && _b !== void 0 ? _b : DEFAULT_MAX_LABELS;
|
|
47829
48144
|
this.stride = ChooseLabelStride({
|
|
47830
|
-
cellPixels,
|
|
48145
|
+
cellPixels: cellSize,
|
|
47831
48146
|
visibleCols: window.col1 - window.col0 + 1,
|
|
47832
48147
|
visibleRows: window.row1 - window.row0 + 1,
|
|
47833
48148
|
maxLabels,
|
|
47834
|
-
|
|
47835
|
-
// one is, so changing what a label says cannot quietly start overlapping them.
|
|
47836
|
-
minSpacingPixels: Math.max((_c = this.settings.minSpacingPixels) !== null && _c !== void 0 ? _c : DEFAULT_MIN_SPACING_PIXELS, this.widestLabel(parts)),
|
|
48149
|
+
minSpacingPixels: (_c = this.settings.minSpacingPixels) !== null && _c !== void 0 ? _c : DEFAULT_MIN_SPACING_PIXELS,
|
|
47837
48150
|
held: this.stride
|
|
47838
48151
|
});
|
|
47839
|
-
const
|
|
48152
|
+
const parts = this.settings.parts || ["value", "floor", "ceiling"];
|
|
48153
|
+
this.syncCellLabels(this.labelCells(window, cols, rows, this.stride, maxLabels, parts));
|
|
48154
|
+
}
|
|
48155
|
+
/*
|
|
48156
|
+
* One labelled cell per lattice block, keyed by the block's origin so it survives frame and stride changes.
|
|
48157
|
+
*/
|
|
48158
|
+
labelCells(window, cols, rows, stride, max, parts) {
|
|
48159
|
+
const width = this.canvas.width;
|
|
48160
|
+
const height = this.canvas.height;
|
|
47840
48161
|
const firstRow = Math.floor(window.row0 / stride) * stride;
|
|
47841
48162
|
const firstCol = Math.floor(window.col0 / stride) * stride;
|
|
47842
|
-
|
|
47843
|
-
|
|
48163
|
+
const candidates = [];
|
|
48164
|
+
for (let row = firstRow; row <= window.row1; row += stride) {
|
|
48165
|
+
for (let col = firstCol; col <= window.col1; col += stride) {
|
|
47844
48166
|
const found = this.readNear(col, row, cols, rows, stride);
|
|
47845
48167
|
if (!found) {
|
|
47846
48168
|
continue;
|
|
47847
48169
|
}
|
|
47848
|
-
const point = this.
|
|
47849
|
-
|
|
47850
|
-
|
|
48170
|
+
const point = this.projectCell(found.col, found.row, cols, rows);
|
|
48171
|
+
// A margin past the edge, so a label does not drop the moment its anchor does.
|
|
48172
|
+
const margin = LABEL_EDGE_MARGIN_PIXELS;
|
|
48173
|
+
if (!point ||
|
|
48174
|
+
point.x < -margin ||
|
|
48175
|
+
point.y < -margin ||
|
|
48176
|
+
point.x > width + margin ||
|
|
48177
|
+
point.y > height + margin) {
|
|
48178
|
+
continue;
|
|
48179
|
+
}
|
|
48180
|
+
const text = this.labelLines(found.reading, parts).join("\n");
|
|
48181
|
+
if (!text) {
|
|
47851
48182
|
continue;
|
|
47852
48183
|
}
|
|
47853
|
-
|
|
48184
|
+
const key = col + "," + row;
|
|
48185
|
+
const distance = Math.hypot(point.x - width / 2, point.y - height / 2);
|
|
48186
|
+
candidates.push({
|
|
48187
|
+
key,
|
|
48188
|
+
lon: this.lonAt(found.col, cols),
|
|
48189
|
+
lat: this.latAt(found.row, rows),
|
|
48190
|
+
text,
|
|
48191
|
+
// Labels already up outrank new ones, so the capped set does not shuffle as the view pans.
|
|
48192
|
+
rank: this.cellLabels.has(key) ? distance * LABEL_KEEP_BIAS : distance
|
|
48193
|
+
});
|
|
47854
48194
|
}
|
|
47855
48195
|
}
|
|
48196
|
+
if (candidates.length > max) {
|
|
48197
|
+
candidates.sort((x, y) => x.rank - y.rank);
|
|
48198
|
+
candidates.length = max;
|
|
48199
|
+
}
|
|
48200
|
+
return candidates;
|
|
48201
|
+
}
|
|
48202
|
+
/*
|
|
48203
|
+
* Keeps one scene label per wanted cell, reusing those that are still wanted.
|
|
48204
|
+
*/
|
|
48205
|
+
syncCellLabels(wanted) {
|
|
48206
|
+
var _a;
|
|
48207
|
+
const keep = new Set(wanted.map((w) => w.key));
|
|
48208
|
+
const now = Date.now();
|
|
48209
|
+
let lingering = false;
|
|
48210
|
+
for (const [key, entry] of this.cellLabels) {
|
|
48211
|
+
if (keep.has(key)) {
|
|
48212
|
+
entry.missingSince = undefined;
|
|
48213
|
+
continue;
|
|
48214
|
+
}
|
|
48215
|
+
entry.missingSince = (_a = entry.missingSince) !== null && _a !== void 0 ? _a : now;
|
|
48216
|
+
if (now - entry.missingSince >= LABEL_LINGER_MS) {
|
|
48217
|
+
entry.label.Dispose();
|
|
48218
|
+
this.cellLabels.delete(key);
|
|
48219
|
+
}
|
|
48220
|
+
else {
|
|
48221
|
+
lingering = true;
|
|
48222
|
+
}
|
|
48223
|
+
}
|
|
48224
|
+
// A still camera draws nothing more, so a lingering label is given its own later look.
|
|
48225
|
+
if (lingering && !this.lingerTimer) {
|
|
48226
|
+
this.lingerTimer = setTimeout(() => {
|
|
48227
|
+
this.lingerTimer = null;
|
|
48228
|
+
if (!this.disposed) {
|
|
48229
|
+
this.drawnView = null;
|
|
48230
|
+
this.viewer.scene.requestRender();
|
|
48231
|
+
}
|
|
48232
|
+
}, LABEL_LINGER_MS);
|
|
48233
|
+
}
|
|
48234
|
+
for (const want of wanted) {
|
|
48235
|
+
const pos3d = Cartesian3.fromDegrees(want.lon, want.lat, this.surfaceHeight(want.lon, want.lat));
|
|
48236
|
+
const options = {
|
|
48237
|
+
presentation: "text",
|
|
48238
|
+
text: want.text,
|
|
48239
|
+
leader: true,
|
|
48240
|
+
selectOnClick: false
|
|
48241
|
+
};
|
|
48242
|
+
const existing = this.cellLabels.get(want.key);
|
|
48243
|
+
if (existing) {
|
|
48244
|
+
if (!Cartesian3.equalsEpsilon(existing.label.pos3d, pos3d, 0, 0.01)) {
|
|
48245
|
+
existing.label.pos3d = pos3d;
|
|
48246
|
+
}
|
|
48247
|
+
if (existing.text !== want.text) {
|
|
48248
|
+
existing.label.Configure(options);
|
|
48249
|
+
existing.text = want.text;
|
|
48250
|
+
}
|
|
48251
|
+
continue;
|
|
48252
|
+
}
|
|
48253
|
+
const id = (this.hover ? this.hover.entityId : "texture-value") + "#cell:" + want.key;
|
|
48254
|
+
const label = new EntityLabel.Label({
|
|
48255
|
+
rego: {
|
|
48256
|
+
entityId: id,
|
|
48257
|
+
menuItemId: this.hover ? this.hover.menuItemId : undefined,
|
|
48258
|
+
name: want.text,
|
|
48259
|
+
priority: 0,
|
|
48260
|
+
// No graphic, since a point or billboard visual pulls a label back to itself on rebuild.
|
|
48261
|
+
visual: new Entity({ position: pos3d })
|
|
48262
|
+
},
|
|
48263
|
+
viewer: this.viewer,
|
|
48264
|
+
pos3d,
|
|
48265
|
+
options,
|
|
48266
|
+
persistent: true,
|
|
48267
|
+
visible: () => !this.disposed
|
|
48268
|
+
});
|
|
48269
|
+
this.cellLabels.set(want.key, { label, text: want.text });
|
|
48270
|
+
}
|
|
48271
|
+
}
|
|
48272
|
+
clearCellLabels() {
|
|
48273
|
+
if (this.lingerTimer) {
|
|
48274
|
+
clearTimeout(this.lingerTimer);
|
|
48275
|
+
this.lingerTimer = null;
|
|
48276
|
+
}
|
|
48277
|
+
for (const entry of this.cellLabels.values()) {
|
|
48278
|
+
entry.label.Dispose();
|
|
48279
|
+
}
|
|
48280
|
+
this.cellLabels.clear();
|
|
48281
|
+
}
|
|
48282
|
+
/*
|
|
48283
|
+
* Shows the cell under the pointer through the register's hover label, unless something else holds it.
|
|
48284
|
+
*/
|
|
48285
|
+
updateHover() {
|
|
48286
|
+
const hover = this.hover;
|
|
48287
|
+
if (this.disposed || !hover || !hover.register || !this.source || !this.hoverAt) {
|
|
48288
|
+
return;
|
|
48289
|
+
}
|
|
48290
|
+
const register = hover.register;
|
|
48291
|
+
const target = register.GetLabelHoverTarget();
|
|
48292
|
+
const ours = Boolean(target) && register.IsLabelHovered(hover.entityId, hover.menuItemId);
|
|
48293
|
+
if (target && !ours) {
|
|
48294
|
+
this.hovering = false;
|
|
48295
|
+
return;
|
|
48296
|
+
}
|
|
48297
|
+
const under = this.readingUnder(this.hoverAt.x, this.hoverAt.y);
|
|
48298
|
+
if (!under) {
|
|
48299
|
+
this.releaseHover();
|
|
48300
|
+
return;
|
|
48301
|
+
}
|
|
48302
|
+
register.SetLabelHover({
|
|
48303
|
+
entityId: hover.entityId,
|
|
48304
|
+
menuItemId: hover.menuItemId,
|
|
48305
|
+
position: { x: this.hoverAt.x, y: this.hoverAt.y },
|
|
48306
|
+
text: under.text,
|
|
48307
|
+
pos3d: under.pos3d
|
|
48308
|
+
});
|
|
48309
|
+
this.hovering = true;
|
|
48310
|
+
}
|
|
48311
|
+
releaseHover() {
|
|
48312
|
+
const hover = this.hover;
|
|
48313
|
+
if (!this.hovering || !hover || !hover.register) {
|
|
48314
|
+
return;
|
|
48315
|
+
}
|
|
48316
|
+
this.hovering = false;
|
|
48317
|
+
if (hover.register.IsLabelHovered(hover.entityId, hover.menuItemId)) {
|
|
48318
|
+
hover.register.ClearLabelHover();
|
|
48319
|
+
}
|
|
48320
|
+
}
|
|
48321
|
+
/*
|
|
48322
|
+
* The text for the painted cell under a window position and its centre on the sheet, or null.
|
|
48323
|
+
*/
|
|
48324
|
+
readingUnder(x, y) {
|
|
48325
|
+
const at = this.cellUnder(x, y);
|
|
48326
|
+
if (!at) {
|
|
48327
|
+
return null;
|
|
48328
|
+
}
|
|
48329
|
+
const cols = this.source.width;
|
|
48330
|
+
const found = this.readAt(at.col, at.row, cols);
|
|
48331
|
+
if (!found || !this.paintedAt(at.col, at.row, cols)) {
|
|
48332
|
+
return null;
|
|
48333
|
+
}
|
|
48334
|
+
const text = this.labelLines(found, this.settings.parts || ["value", "floor", "ceiling"]).join("\n");
|
|
48335
|
+
if (!text) {
|
|
48336
|
+
return null;
|
|
48337
|
+
}
|
|
48338
|
+
const lon = this.lonAt(at.col, cols);
|
|
48339
|
+
const lat = this.latAt(at.row, this.source.height);
|
|
48340
|
+
return { text, pos3d: Cartesian3.fromDegrees(lon, lat, this.surfaceHeight(lon, lat)) };
|
|
48341
|
+
}
|
|
48342
|
+
/*
|
|
48343
|
+
* The cell under a window position, found on the sheet rather than on the ground beneath it.
|
|
48344
|
+
*/
|
|
48345
|
+
cellUnder(x, y) {
|
|
48346
|
+
const scene = this.viewer.scene;
|
|
48347
|
+
const camera = this.viewer.camera;
|
|
48348
|
+
const screen = new Cartesian2(x, y);
|
|
48349
|
+
const ray = camera.getPickRay(screen);
|
|
48350
|
+
if (!ray) {
|
|
48351
|
+
return null;
|
|
48352
|
+
}
|
|
48353
|
+
let hit = (scene.globe && scene.globe.pick(ray, scene)) || camera.pickEllipsoid(screen);
|
|
48354
|
+
if (!hit) {
|
|
48355
|
+
return null;
|
|
48356
|
+
}
|
|
48357
|
+
const radii = Ellipsoid.WGS84.radii;
|
|
48358
|
+
for (let i = 0; i < HOVER_REFINE_STEPS && this.heightAt; i++) {
|
|
48359
|
+
const carto = Cartographic.fromCartesian(hit);
|
|
48360
|
+
const height = this.heightAt(Math$1.toDegrees(carto.longitude), Math$1.toDegrees(carto.latitude));
|
|
48361
|
+
if (height == null) {
|
|
48362
|
+
break;
|
|
48363
|
+
}
|
|
48364
|
+
const inflated = new Ellipsoid(radii.x + height, radii.y + height, radii.z + height);
|
|
48365
|
+
const interval = IntersectionTests.rayEllipsoid(ray, inflated);
|
|
48366
|
+
if (!interval) {
|
|
48367
|
+
break;
|
|
48368
|
+
}
|
|
48369
|
+
hit = Ray.getPoint(ray, interval.start);
|
|
48370
|
+
}
|
|
48371
|
+
const carto = Cartographic.fromCartesian(hit);
|
|
48372
|
+
return this.texelAt(Math$1.toDegrees(carto.longitude), Math$1.toDegrees(carto.latitude));
|
|
47856
48373
|
}
|
|
47857
48374
|
/*
|
|
47858
48375
|
* Outlines each covered cell, from a projected lattice of its corners.
|
|
@@ -47880,44 +48397,25 @@ var TextureValueLabels;
|
|
|
47880
48397
|
if (wide * high > MAX_GRID_CELLS) {
|
|
47881
48398
|
return;
|
|
47882
48399
|
}
|
|
47883
|
-
const lattice = new
|
|
47884
|
-
|
|
47885
|
-
|
|
47886
|
-
|
|
47887
|
-
|
|
47888
|
-
|
|
47889
|
-
|
|
47890
|
-
}
|
|
47891
|
-
}
|
|
47892
|
-
const path = new Path2D();
|
|
47893
|
-
let drew = false;
|
|
48400
|
+
const lattice = new Lattice(wide + 1, high + 1, (c, r) => {
|
|
48401
|
+
// Corners sit half a texel out from the centres lonAt/latAt return.
|
|
48402
|
+
const lon = this.lonAt(window.col0 + c - 0.5, cols);
|
|
48403
|
+
const lat = this.latAt(window.row0 + r - 0.5, rows);
|
|
48404
|
+
return this.projectInto(lon, lat);
|
|
48405
|
+
});
|
|
48406
|
+
const covered = new Uint8Array(wide * high);
|
|
47894
48407
|
for (let r = 0; r < high; r++) {
|
|
47895
48408
|
for (let c = 0; c < wide; c++) {
|
|
47896
|
-
|
|
47897
|
-
continue;
|
|
47898
|
-
}
|
|
47899
|
-
const tl = lattice[r * (wide + 1) + c];
|
|
47900
|
-
const tr = lattice[r * (wide + 1) + c + 1];
|
|
47901
|
-
const bl = lattice[(r + 1) * (wide + 1) + c];
|
|
47902
|
-
const br = lattice[(r + 1) * (wide + 1) + c + 1];
|
|
47903
|
-
if (!tl || !tr || !bl || !br) {
|
|
47904
|
-
continue;
|
|
47905
|
-
}
|
|
47906
|
-
path.moveTo(tl.x, tl.y);
|
|
47907
|
-
path.lineTo(tr.x, tr.y);
|
|
47908
|
-
path.lineTo(br.x, br.y);
|
|
47909
|
-
path.lineTo(bl.x, bl.y);
|
|
47910
|
-
path.closePath();
|
|
47911
|
-
drew = true;
|
|
48409
|
+
covered[r * wide + c] = this.isCovered(window.col0 + c, window.row0 + r, cols) ? 1 : 0;
|
|
47912
48410
|
}
|
|
47913
48411
|
}
|
|
47914
|
-
|
|
48412
|
+
const path = new Path2D();
|
|
48413
|
+
if (!lattice.Trace(path, covered, wide, high)) {
|
|
47915
48414
|
return;
|
|
47916
48415
|
}
|
|
47917
48416
|
this.ctx.strokeStyle = color;
|
|
47918
48417
|
this.ctx.lineWidth = (_b = this.settings.gridWidthPixels) !== null && _b !== void 0 ? _b : DEFAULT_GRID_WIDTH_PIXELS;
|
|
47919
48418
|
this.ctx.stroke(path);
|
|
47920
|
-
+" alpha" + this.ctx.globalAlpha;
|
|
47921
48419
|
}
|
|
47922
48420
|
/*
|
|
47923
48421
|
* Restricts drawing to the polygon, so nothing is ruled over ground it does not cover.
|
|
@@ -47939,33 +48437,11 @@ var TextureValueLabels;
|
|
|
47939
48437
|
path.closePath();
|
|
47940
48438
|
this.ctx.clip(path);
|
|
47941
48439
|
}
|
|
47942
|
-
/*
|
|
47943
|
-
* The cell of whichever tile covers a point, as screen corners.
|
|
47944
|
-
*/
|
|
47945
|
-
tileCellOutline(lon, lat) {
|
|
47946
|
-
for (const tile of this.tiles) {
|
|
47947
|
-
if (lon < tile.West || lon > tile.East || lat < tile.South || lat > tile.North) {
|
|
47948
|
-
continue;
|
|
47949
|
-
}
|
|
47950
|
-
const c = (Math.min(tile.ResolutionX - 1, Math.max(0, Math.floor((lon - tile.West) / (tile.East - tile.West) * tile.ResolutionX))));
|
|
47951
|
-
const r = (Math.min(tile.ResolutionY - 1, Math.max(0, Math.floor((tile.North - lat) / (tile.North - tile.South) * tile.ResolutionY))));
|
|
47952
|
-
const w0 = tile.West + (c / tile.ResolutionX) * (tile.East - tile.West);
|
|
47953
|
-
const w1 = tile.West + ((c + 1) / tile.ResolutionX) * (tile.East - tile.West);
|
|
47954
|
-
const n0 = tile.North - (r / tile.ResolutionY) * (tile.North - tile.South);
|
|
47955
|
-
const n1 = tile.North - ((r + 1) / tile.ResolutionY) * (tile.North - tile.South);
|
|
47956
|
-
const corners = [
|
|
47957
|
-
this.project(w0, n0), this.project(w1, n0),
|
|
47958
|
-
this.project(w1, n1), this.project(w0, n1)
|
|
47959
|
-
];
|
|
47960
|
-
return corners.every((q) => q) ? corners : null;
|
|
47961
|
-
}
|
|
47962
|
-
return null;
|
|
47963
|
-
}
|
|
47964
48440
|
/*
|
|
47965
48441
|
* Outlines each tile's own cells, so cell size still shows where the mesh is refined.
|
|
47966
48442
|
*/
|
|
47967
48443
|
drawTileGrid(color) {
|
|
47968
|
-
var _a, _b
|
|
48444
|
+
var _a, _b;
|
|
47969
48445
|
const view = this.viewer.camera.computeViewRectangle(Ellipsoid.WGS84);
|
|
47970
48446
|
if (!view) {
|
|
47971
48447
|
return;
|
|
@@ -47994,7 +48470,7 @@ var TextureValueLabels;
|
|
|
47994
48470
|
coarse.lineTo(tr.x, tr.y);
|
|
47995
48471
|
coarse.lineTo(br.x, br.y);
|
|
47996
48472
|
coarse.lineTo(bl.x, bl.y);
|
|
47997
|
-
coarse.
|
|
48473
|
+
coarse.lineTo(tl.x, tl.y);
|
|
47998
48474
|
drewCoarse = true;
|
|
47999
48475
|
};
|
|
48000
48476
|
// Planned for every visible tile BEFORE any is drawn.
|
|
@@ -48055,31 +48531,29 @@ var TextureValueLabels;
|
|
|
48055
48531
|
// moves, and so each step up is a subset of the one below it.
|
|
48056
48532
|
const firstR = Math.floor(r0 / step) * step;
|
|
48057
48533
|
const firstC = Math.floor(c0 / step) * step;
|
|
48058
|
-
|
|
48059
|
-
|
|
48060
|
-
|
|
48061
|
-
|
|
48062
|
-
|
|
48063
|
-
|
|
48064
|
-
|
|
48065
|
-
|
|
48066
|
-
|
|
48067
|
-
|
|
48068
|
-
|
|
48069
|
-
|
|
48070
|
-
|
|
48071
|
-
|
|
48072
|
-
|
|
48073
|
-
|
|
48074
|
-
|
|
48075
|
-
|
|
48076
|
-
|
|
48077
|
-
|
|
48078
|
-
|
|
48079
|
-
|
|
48080
|
-
|
|
48081
|
-
drew = true;
|
|
48082
|
-
}
|
|
48534
|
+
const wide = Math.ceil((c1 - firstC) / step);
|
|
48535
|
+
const high = Math.ceil((r1 - firstR) / step);
|
|
48536
|
+
const lonOf = (c) => tile.West
|
|
48537
|
+
+ (Math.min(firstC + c * step, tile.ResolutionX) / tile.ResolutionX) * (tile.East - tile.West);
|
|
48538
|
+
const latOf = (r) => tile.North
|
|
48539
|
+
- (Math.min(firstR + r * step, tile.ResolutionY) / tile.ResolutionY) * (tile.North - tile.South);
|
|
48540
|
+
const covered = new Uint8Array(wide * high);
|
|
48541
|
+
let any = false;
|
|
48542
|
+
for (let r = 0; r < high; r++) {
|
|
48543
|
+
for (let c = 0; c < wide; c++) {
|
|
48544
|
+
if (this.coveredAt((lonOf(c) + lonOf(c + 1)) / 2, (latOf(r) + latOf(r + 1)) / 2)) {
|
|
48545
|
+
covered[r * wide + c] = 1;
|
|
48546
|
+
any = true;
|
|
48547
|
+
}
|
|
48548
|
+
}
|
|
48549
|
+
}
|
|
48550
|
+
if (!any) {
|
|
48551
|
+
continue;
|
|
48552
|
+
}
|
|
48553
|
+
// Projected on demand, since a sparse raster leaves most corners belonging to no cell.
|
|
48554
|
+
const lattice = new Lattice(wide + 1, high + 1, (c, r) => this.projectInto(lonOf(c), latOf(r)));
|
|
48555
|
+
if (lattice.Trace(path, covered, wide, high)) {
|
|
48556
|
+
drew = true;
|
|
48083
48557
|
}
|
|
48084
48558
|
}
|
|
48085
48559
|
const gridWidth = (_b = this.settings.gridWidthPixels) !== null && _b !== void 0 ? _b : DEFAULT_GRID_WIDTH_PIXELS;
|
|
@@ -48097,7 +48571,7 @@ var TextureValueLabels;
|
|
|
48097
48571
|
return;
|
|
48098
48572
|
}
|
|
48099
48573
|
this.ctx.strokeStyle = color;
|
|
48100
|
-
this.ctx.lineWidth =
|
|
48574
|
+
this.ctx.lineWidth = gridWidth;
|
|
48101
48575
|
this.ctx.stroke(path);
|
|
48102
48576
|
}
|
|
48103
48577
|
/*
|
|
@@ -48117,7 +48591,7 @@ var TextureValueLabels;
|
|
|
48117
48591
|
};
|
|
48118
48592
|
}
|
|
48119
48593
|
/*
|
|
48120
|
-
* Whether the composited raster holds data at a lon/lat.
|
|
48594
|
+
* Whether the composited raster holds painted data at a lon/lat.
|
|
48121
48595
|
*/
|
|
48122
48596
|
coveredAt(lon, lat) {
|
|
48123
48597
|
if (!this.source) {
|
|
@@ -48130,60 +48604,20 @@ var TextureValueLabels;
|
|
|
48130
48604
|
if (col < 0 || row < 0 || col >= cols || row >= rows) {
|
|
48131
48605
|
return false;
|
|
48132
48606
|
}
|
|
48133
|
-
const at = (row * cols + col) * 4 + 3;
|
|
48134
48607
|
// Painted, not merely covered: a dry cell holds a reading the ramp deliberately hides, and
|
|
48135
48608
|
// ruling a grid over it draws detail where the picture shows none.
|
|
48136
|
-
if (this.painted
|
|
48137
|
-
return this.
|
|
48609
|
+
if (this.painted || this.paintedRamp) {
|
|
48610
|
+
return this.paintedAt(col, row, cols);
|
|
48138
48611
|
}
|
|
48139
|
-
return this.source.data[
|
|
48612
|
+
return this.source.data[(row * cols + col) * 4 + 3] >= 128;
|
|
48140
48613
|
}
|
|
48141
48614
|
isCovered(col, row, cols) {
|
|
48142
48615
|
return Boolean(this.source && this.source.data[(row * cols + col) * 4 + 3] >= 128);
|
|
48143
48616
|
}
|
|
48144
48617
|
/*
|
|
48145
|
-
*
|
|
48146
|
-
*/
|
|
48147
|
-
widestLabel(parts) {
|
|
48148
|
-
const sample = [];
|
|
48149
|
-
if (parts.indexOf("value") >= 0) {
|
|
48150
|
-
sample.push(valueLine("-000.00", LabelUnits(this.metadata)));
|
|
48151
|
-
}
|
|
48152
|
-
const range = [];
|
|
48153
|
-
const showsRange = !TextureFrameSeriesAnimator.IsTransformed(this.metadata);
|
|
48154
|
-
if (showsRange && parts.indexOf("floor") >= 0) {
|
|
48155
|
-
range.push("min: -000.00");
|
|
48156
|
-
}
|
|
48157
|
-
if (showsRange && parts.indexOf("ceiling") >= 0) {
|
|
48158
|
-
range.push("max: -000.00");
|
|
48159
|
-
}
|
|
48160
|
-
if (range.length) {
|
|
48161
|
-
sample.push(range.join(" - "));
|
|
48162
|
-
}
|
|
48163
|
-
if (!sample.length) {
|
|
48164
|
-
return 0;
|
|
48165
|
-
}
|
|
48166
|
-
const widest = Math.max(...sample.map((line) => this.ctx.measureText(line).width));
|
|
48167
|
-
return widest + LABEL_GAP_PIXELS;
|
|
48168
|
-
}
|
|
48169
|
-
/*
|
|
48170
|
-
* The four screen corners of one cell, or null if any of them will not project.
|
|
48618
|
+
* What a label says about one reading, a line at a time.
|
|
48171
48619
|
*/
|
|
48172
|
-
|
|
48173
|
-
// On a pyramid the composited texel is not a cell. Outlining it would draw a second,
|
|
48174
|
-
// uniform lattice over the tile grid, misaligned with it everywhere.
|
|
48175
|
-
if (this.tiles && this.tiles.length) {
|
|
48176
|
-
return this.tileCellOutline(this.lonAt(col, cols), this.latAt(row, rows));
|
|
48177
|
-
}
|
|
48178
|
-
const corners = [
|
|
48179
|
-
this.project(this.lonAt(col - 0.5, cols), this.latAt(row - 0.5, rows)),
|
|
48180
|
-
this.project(this.lonAt(col + 0.5, cols), this.latAt(row - 0.5, rows)),
|
|
48181
|
-
this.project(this.lonAt(col + 0.5, cols), this.latAt(row + 0.5, rows)),
|
|
48182
|
-
this.project(this.lonAt(col - 0.5, cols), this.latAt(row + 0.5, rows))
|
|
48183
|
-
];
|
|
48184
|
-
return corners.every((c) => c) ? corners : null;
|
|
48185
|
-
}
|
|
48186
|
-
drawLabel(point, reading, parts, outline) {
|
|
48620
|
+
labelLines(reading, parts) {
|
|
48187
48621
|
var _a;
|
|
48188
48622
|
const dp = (_a = this.settings.decimals) !== null && _a !== void 0 ? _a : (Math.abs(reading.value) < 10 ? 2 : 1);
|
|
48189
48623
|
const lines = [];
|
|
@@ -48202,32 +48636,7 @@ var TextureValueLabels;
|
|
|
48202
48636
|
if (range.length) {
|
|
48203
48637
|
lines.push(range.join(" - "));
|
|
48204
48638
|
}
|
|
48205
|
-
|
|
48206
|
-
return;
|
|
48207
|
-
}
|
|
48208
|
-
if (outline) {
|
|
48209
|
-
this.ctx.strokeStyle = LABEL_ANCHOR_COLOR;
|
|
48210
|
-
this.ctx.lineWidth = 1.5;
|
|
48211
|
-
this.ctx.beginPath();
|
|
48212
|
-
this.ctx.moveTo(outline[0].x, outline[0].y);
|
|
48213
|
-
for (let i = 1; i < outline.length; i++) {
|
|
48214
|
-
this.ctx.lineTo(outline[i].x, outline[i].y);
|
|
48215
|
-
}
|
|
48216
|
-
this.ctx.closePath();
|
|
48217
|
-
this.ctx.stroke();
|
|
48218
|
-
}
|
|
48219
|
-
const width = Math.max(...lines.map((l) => this.ctx.measureText(l).width)) + 10;
|
|
48220
|
-
const height = lines.length * LINE_HEIGHT + 6;
|
|
48221
|
-
const top = point.y - height / 2;
|
|
48222
|
-
this.ctx.fillStyle = "rgba(16,16,14,0.72)";
|
|
48223
|
-
this.ctx.beginPath();
|
|
48224
|
-
this.ctx.roundRect(point.x - width / 2, top, width, height, LABEL_RADIUS);
|
|
48225
|
-
this.ctx.fill();
|
|
48226
|
-
lines.forEach((line, i) => {
|
|
48227
|
-
this.ctx.fillStyle = i === 0 ? "#ffffff" : "#a8a89f";
|
|
48228
|
-
this.ctx.fillText(line, point.x, top + LINE_HEIGHT * (i + 1) - 1);
|
|
48229
|
-
});
|
|
48230
|
-
this.drawn++;
|
|
48639
|
+
return lines;
|
|
48231
48640
|
}
|
|
48232
48641
|
/*
|
|
48233
48642
|
* The first cell with a reading at or near a lattice point, searched within its own block.
|
|
@@ -48254,11 +48663,24 @@ var TextureValueLabels;
|
|
|
48254
48663
|
return null;
|
|
48255
48664
|
}
|
|
48256
48665
|
paintedAt(col, row, cols) {
|
|
48257
|
-
|
|
48258
|
-
|
|
48666
|
+
const at = (row * cols + col) * 4;
|
|
48667
|
+
if (this.painted && this.painted.length === this.source.data.length) {
|
|
48668
|
+
return this.painted[at + 3] > 0;
|
|
48669
|
+
}
|
|
48670
|
+
if (this.paintedRamp) {
|
|
48671
|
+
const data = this.source.data;
|
|
48672
|
+
if (data[at + 3] === 0) {
|
|
48673
|
+
return false;
|
|
48674
|
+
}
|
|
48675
|
+
// The 8 bit grey the ramp is indexed on, which is what a tinted frame would have used.
|
|
48676
|
+
const grey = TextureFrameSeriesAnimator.IsPackedValue(this.metadata)
|
|
48677
|
+
? Math.round((data[at] * 256 + data[at + 1]) / 65535 * 255)
|
|
48678
|
+
: data[at];
|
|
48679
|
+
const steps = this.paintedRamp.length / 4;
|
|
48680
|
+
const index = Math.min(steps - 1, Math.round(grey / 255 * (steps - 1)));
|
|
48681
|
+
return this.paintedRamp[index * 4 + 3] > 0;
|
|
48259
48682
|
}
|
|
48260
|
-
|
|
48261
|
-
return this.painted[at] > 0;
|
|
48683
|
+
return true;
|
|
48262
48684
|
}
|
|
48263
48685
|
// Decodes one cell, skipping anything the texture marks as having no data.
|
|
48264
48686
|
readAt(col, row, cols) {
|
|
@@ -48274,6 +48696,16 @@ var TextureValueLabels;
|
|
|
48274
48696
|
sourceMap: this.sourceMap
|
|
48275
48697
|
});
|
|
48276
48698
|
}
|
|
48699
|
+
/*
|
|
48700
|
+
* The texel holding a lon/lat, or null outside the raster.
|
|
48701
|
+
*/
|
|
48702
|
+
texelAt(lon, lat) {
|
|
48703
|
+
const cols = this.source.width;
|
|
48704
|
+
const rows = this.source.height;
|
|
48705
|
+
const col = Math.floor((lon - this.extent.West) / (this.extent.East - this.extent.West) * cols);
|
|
48706
|
+
const row = Math.floor((this.extent.North - lat) / (this.extent.North - this.extent.South) * rows);
|
|
48707
|
+
return col < 0 || row < 0 || col >= cols || row >= rows ? null : { col, row };
|
|
48708
|
+
}
|
|
48277
48709
|
lonAt(col, cols) {
|
|
48278
48710
|
return this.extent.West + ((col + 0.5) / cols) * (this.extent.East - this.extent.West);
|
|
48279
48711
|
}
|
|
@@ -48302,15 +48734,102 @@ var TextureValueLabels;
|
|
|
48302
48734
|
row1: Math.min(rows - 1, Math.ceil((this.extent.North - south) / spanLat * rows) + 1)
|
|
48303
48735
|
};
|
|
48304
48736
|
}
|
|
48737
|
+
// Metres the sheet sits at over a point, or the ellipsoid when nothing says otherwise.
|
|
48738
|
+
surfaceHeight(lon, lat) {
|
|
48739
|
+
const height = this.heightAt ? this.heightAt(lon, lat) : null;
|
|
48740
|
+
return height == null || !isFinite(height) ? 0 : height;
|
|
48741
|
+
}
|
|
48742
|
+
projectCell(col, row, cols, rows) {
|
|
48743
|
+
return this.project(this.lonAt(col, cols), this.latAt(row, rows));
|
|
48744
|
+
}
|
|
48745
|
+
// Projected at the sheet's own height, since up close the ellipsoid is metres from where it is drawn.
|
|
48305
48746
|
project(lon, lat) {
|
|
48306
|
-
|
|
48747
|
+
return this.projectInto(lon, lat) ? { x: this.scratchWindow.x, y: this.scratchWindow.y } : null;
|
|
48748
|
+
}
|
|
48749
|
+
/*
|
|
48750
|
+
* Projects into the shared scratch window position, returning whether it landed anywhere.
|
|
48751
|
+
*/
|
|
48752
|
+
projectInto(lon, lat) {
|
|
48753
|
+
const world = Cartesian3.fromDegrees(lon, lat, this.surfaceHeight(lon, lat), Ellipsoid.WGS84, this.scratchWorld);
|
|
48754
|
+
// One matrix multiply in 3D, where the general path rederives the viewport and frustum per point.
|
|
48755
|
+
if (this.viewProjection) {
|
|
48756
|
+
const clip = Matrix4.multiplyByVector(this.viewProjection, Cartesian4.fromElements(world.x, world.y, world.z, 1, this.scratchClip), this.scratchClip);
|
|
48757
|
+
if (!(clip.w > 0)) {
|
|
48758
|
+
return null;
|
|
48759
|
+
}
|
|
48760
|
+
const view = this.viewer.canvas;
|
|
48761
|
+
this.scratchWindow.x = (clip.x / clip.w * 0.5 + 0.5) * view.clientWidth;
|
|
48762
|
+
this.scratchWindow.y = (0.5 - clip.y / clip.w * 0.5) * view.clientHeight;
|
|
48763
|
+
return this.scratchWindow;
|
|
48764
|
+
}
|
|
48307
48765
|
const transforms = SceneTransforms;
|
|
48308
48766
|
// Renamed in newer Cesium, and this library runs on whatever the host app supplies.
|
|
48309
48767
|
const fn = transforms.worldToWindowCoordinates || transforms.wgs84ToWindowCoordinates;
|
|
48310
|
-
return fn ? fn.call(transforms, this.viewer.scene, world) : null;
|
|
48768
|
+
return fn ? fn.call(transforms, this.viewer.scene, world, this.scratchWindow) || null : null;
|
|
48311
48769
|
}
|
|
48312
48770
|
}
|
|
48313
48771
|
TextureValueLabels.Labels = Labels;
|
|
48772
|
+
/*
|
|
48773
|
+
* A grid of window positions projected at most once each, and the cell edges traced through them.
|
|
48774
|
+
*/
|
|
48775
|
+
class Lattice {
|
|
48776
|
+
constructor(wide, high, projectAt) {
|
|
48777
|
+
this.wide = wide;
|
|
48778
|
+
this.projectAt = projectAt;
|
|
48779
|
+
this.xs = new Float64Array(wide * high);
|
|
48780
|
+
this.ys = new Float64Array(wide * high);
|
|
48781
|
+
this.state = new Uint8Array(wide * high);
|
|
48782
|
+
}
|
|
48783
|
+
at(c, r) {
|
|
48784
|
+
const i = r * this.wide + c;
|
|
48785
|
+
if (this.state[i] === 0) {
|
|
48786
|
+
const p = this.projectAt(c, r);
|
|
48787
|
+
if (p) {
|
|
48788
|
+
this.xs[i] = p.x;
|
|
48789
|
+
this.ys[i] = p.y;
|
|
48790
|
+
this.state[i] = 1;
|
|
48791
|
+
}
|
|
48792
|
+
else {
|
|
48793
|
+
this.state[i] = 2;
|
|
48794
|
+
}
|
|
48795
|
+
}
|
|
48796
|
+
return this.state[i] === 1 ? i : -1;
|
|
48797
|
+
}
|
|
48798
|
+
edge(path, a, b) {
|
|
48799
|
+
if (a < 0 || b < 0) {
|
|
48800
|
+
return false;
|
|
48801
|
+
}
|
|
48802
|
+
path.moveTo(this.xs[a], this.ys[a]);
|
|
48803
|
+
path.lineTo(this.xs[b], this.ys[b]);
|
|
48804
|
+
return true;
|
|
48805
|
+
}
|
|
48806
|
+
/*
|
|
48807
|
+
* Adds each covered cell's edges to a path, a shared edge once, and says whether any were added.
|
|
48808
|
+
*/
|
|
48809
|
+
Trace(path, covered, cellsWide, cellsHigh) {
|
|
48810
|
+
let drew = false;
|
|
48811
|
+
for (let r = 0; r < cellsHigh; r++) {
|
|
48812
|
+
for (let c = 0; c < cellsWide; c++) {
|
|
48813
|
+
if (!covered[r * cellsWide + c]) {
|
|
48814
|
+
continue;
|
|
48815
|
+
}
|
|
48816
|
+
const tl = this.at(c, r);
|
|
48817
|
+
const tr = this.at(c + 1, r);
|
|
48818
|
+
const bl = this.at(c, r + 1);
|
|
48819
|
+
if (!(r > 0 && covered[(r - 1) * cellsWide + c])) {
|
|
48820
|
+
drew = this.edge(path, tl, tr) || drew;
|
|
48821
|
+
}
|
|
48822
|
+
if (!(c > 0 && covered[r * cellsWide + c - 1])) {
|
|
48823
|
+
drew = this.edge(path, tl, bl) || drew;
|
|
48824
|
+
}
|
|
48825
|
+
const br = this.at(c + 1, r + 1);
|
|
48826
|
+
drew = this.edge(path, tr, br) || drew;
|
|
48827
|
+
drew = this.edge(path, bl, br) || drew;
|
|
48828
|
+
}
|
|
48829
|
+
}
|
|
48830
|
+
return drew;
|
|
48831
|
+
}
|
|
48832
|
+
}
|
|
48314
48833
|
/*
|
|
48315
48834
|
* The value line. Units go on it alone: repeating them on the range underneath doubles the widest
|
|
48316
48835
|
* label to say nothing, and the two lines are plainly the same quantity.
|
|
@@ -48411,6 +48930,9 @@ in vec2 st;
|
|
|
48411
48930
|
in float skirt;
|
|
48412
48931
|
|
|
48413
48932
|
uniform sampler2D u_valueTexture;
|
|
48933
|
+
// The frame being faded away from, and how far the fade has got. The same texture at 1 when settled.
|
|
48934
|
+
uniform sampler2D u_valueTextureFrom;
|
|
48935
|
+
uniform float u_valueFade;
|
|
48414
48936
|
uniform sampler2D u_groundTexture;
|
|
48415
48937
|
uniform float u_valuePacked;
|
|
48416
48938
|
uniform float u_valueMin;
|
|
@@ -48531,18 +49053,22 @@ float decodeValue(vec4 texel) {
|
|
|
48531
49053
|
}
|
|
48532
49054
|
|
|
48533
49055
|
void main() {
|
|
48534
|
-
vec4
|
|
49056
|
+
vec4 texelTo = texture(u_valueTexture, st);
|
|
49057
|
+
vec4 texelFrom = texture(u_valueTextureFrom, st);
|
|
48535
49058
|
v_st = st;
|
|
49059
|
+
// Decoded before mixing, since blending a packed pair byte by byte reads as some other value
|
|
49060
|
+
// halfway through the fade.
|
|
49061
|
+
float raw = mix(decodeValue(texelFrom), decodeValue(texelTo), u_valueFade);
|
|
49062
|
+
float alpha = mix(texelFrom.a, texelTo.a, u_valueFade);
|
|
48536
49063
|
|
|
48537
49064
|
// Inside a region the scene knows is water, a texel with no value still has to paint, or the
|
|
48538
49065
|
// hole the region exists to close is still a hole.
|
|
48539
49066
|
// The value it paints is the shallowest the ramp is not transparent at.
|
|
48540
49067
|
float inMask = u_maskWeight * texture(u_maskTexture, st).a;
|
|
48541
|
-
float hasData = step(u_coverageCutoffVertex,
|
|
48542
|
-
float raw = decodeValue(texel);
|
|
49068
|
+
float hasData = step(u_coverageCutoffVertex, alpha);
|
|
48543
49069
|
float filled = mix(u_maskFillValue, max(raw, u_maskFillValue), hasData);
|
|
48544
49070
|
v_value = mix(raw, filled, inMask * u_maskFill);
|
|
48545
|
-
v_coverage = max(
|
|
49071
|
+
v_coverage = max(alpha, inMask * u_maskFill);
|
|
48546
49072
|
|
|
48547
49073
|
// Exaggeration stretches the span above the archive's minimum rather than the height itself. On an
|
|
48548
49074
|
// elevation the height is measured from the ellipsoid, tens of metres from any of the data, so
|
|
@@ -48585,6 +49111,8 @@ void main() {
|
|
|
48585
49111
|
`;
|
|
48586
49112
|
const FRAGMENT_SHADER_GLSL300 = `
|
|
48587
49113
|
uniform sampler2D u_valueTexture;
|
|
49114
|
+
uniform sampler2D u_valueTextureFrom;
|
|
49115
|
+
uniform float u_valueFade;
|
|
48588
49116
|
uniform sampler2D u_rampTexture;
|
|
48589
49117
|
uniform float u_coverageCutoff;
|
|
48590
49118
|
uniform vec2 u_texelSize;
|
|
@@ -48621,12 +49149,15 @@ in vec2 v_waveMetres;
|
|
|
48621
49149
|
* Scaled per channel for the same reason the vertex shader's decode is: reassembling the pair reaches
|
|
48622
49150
|
* 65535, which mediump does not have to represent, and this is the shader that may not get highp.
|
|
48623
49151
|
*/
|
|
48624
|
-
float
|
|
48625
|
-
vec4 texel = texture(u_valueTexture, uv);
|
|
49152
|
+
float decodeValue(vec4 texel) {
|
|
48626
49153
|
float packed16 = texel.r * (255.0 * 256.0 / 65535.0) + texel.g * (255.0 / 65535.0);
|
|
48627
49154
|
return mix(texel.r, packed16, u_valuePacked);
|
|
48628
49155
|
}
|
|
48629
49156
|
|
|
49157
|
+
float valueAt(vec2 uv) {
|
|
49158
|
+
return mix(decodeValue(texture(u_valueTextureFrom, uv)), decodeValue(texture(u_valueTexture, uv)), u_valueFade);
|
|
49159
|
+
}
|
|
49160
|
+
|
|
48630
49161
|
//#water
|
|
48631
49162
|
/*
|
|
48632
49163
|
* Slope of the ripple field at a point, in metres of rise per metre travelled.
|
|
@@ -49073,6 +49604,13 @@ void main() {
|
|
|
49073
49604
|
this.rampTexture = null;
|
|
49074
49605
|
this.texture = null;
|
|
49075
49606
|
this.textureDirty = true;
|
|
49607
|
+
// Fed from a fading source: the frame being left, and which pixels each texture holds.
|
|
49608
|
+
this.fade = null;
|
|
49609
|
+
this.textureFrom = null;
|
|
49610
|
+
this.uploadedTo = null;
|
|
49611
|
+
this.uploadedFrom = null;
|
|
49612
|
+
// Size of whichever value raster is bound, canvas or pixels.
|
|
49613
|
+
this.sourceDims = null;
|
|
49076
49614
|
this.shaderProgram = null;
|
|
49077
49615
|
this.shaderProgramWater = false;
|
|
49078
49616
|
this.renderState = null;
|
|
@@ -49243,6 +49781,65 @@ void main() {
|
|
|
49243
49781
|
this.maskGroundTexture = null;
|
|
49244
49782
|
}
|
|
49245
49783
|
}
|
|
49784
|
+
/**
|
|
49785
|
+
* Metres above the ellipsoid the sheet sits at over a point, or null unless a fading source is bound.
|
|
49786
|
+
*/
|
|
49787
|
+
HeightAt(lon, lat) {
|
|
49788
|
+
const pixels = this.fade ? this.fade.to : null;
|
|
49789
|
+
const dims = this.sourceDims;
|
|
49790
|
+
if (!pixels || !dims) {
|
|
49791
|
+
return null;
|
|
49792
|
+
}
|
|
49793
|
+
const fx = (lon - this.extent.West) / (this.extent.East - this.extent.West);
|
|
49794
|
+
const fy = (this.extent.North - lat) / (this.extent.North - this.extent.South);
|
|
49795
|
+
if (!(fx >= 0 && fx <= 1 && fy >= 0 && fy <= 1)) {
|
|
49796
|
+
return null;
|
|
49797
|
+
}
|
|
49798
|
+
const col = Math.min(dims.width - 1, Math.floor(fx * dims.width));
|
|
49799
|
+
const row = Math.min(dims.height - 1, Math.floor(fy * dims.height));
|
|
49800
|
+
const at = (row * dims.width + col) * 4;
|
|
49801
|
+
const coverage = pixels[at + 3] / 255;
|
|
49802
|
+
const normalised = this.packedValue
|
|
49803
|
+
? (pixels[at] * 256 + pixels[at + 1]) / 65535
|
|
49804
|
+
: pixels[at] / 255;
|
|
49805
|
+
const metres = this.valueMin + normalised * (this.valueMax - this.valueMin) * this.exaggeration;
|
|
49806
|
+
const following = this.followGround && Boolean(this.groundPixels);
|
|
49807
|
+
const ground = following ? this.groundAt(fx, fy) : 0;
|
|
49808
|
+
return this.baseHeight + ground + metres * (following ? coverage : 1);
|
|
49809
|
+
}
|
|
49810
|
+
/**
|
|
49811
|
+
* Changes whenever HeightAt would answer differently for the same frame.
|
|
49812
|
+
*/
|
|
49813
|
+
GetPlacementKey() {
|
|
49814
|
+
return [this.groundPixels ? 1 : 0, this.baseHeight, this.exaggeration, this.followGround ? 1 : 0].join("|");
|
|
49815
|
+
}
|
|
49816
|
+
/**
|
|
49817
|
+
* The ramp the sheet is painted with, as 256 RGBA steps across the value range.
|
|
49818
|
+
*/
|
|
49819
|
+
GetRampPixels() {
|
|
49820
|
+
return this.rampPixels;
|
|
49821
|
+
}
|
|
49822
|
+
/*
|
|
49823
|
+
* The packed ground raster read the way sampleGround reads it, bilinear between texel centres.
|
|
49824
|
+
*/
|
|
49825
|
+
groundAt(u, v) {
|
|
49826
|
+
const size = GROUND_SAMPLES_PER_SIDE;
|
|
49827
|
+
const pixels = this.groundPixels;
|
|
49828
|
+
const read = (x, y) => {
|
|
49829
|
+
const i = (Math.min(size - 1, Math.max(0, y)) * size + Math.min(size - 1, Math.max(0, x))) * 4;
|
|
49830
|
+
const packed = (pixels[i] << 16) + (pixels[i + 1] << 8) + pixels[i + 2];
|
|
49831
|
+
return GROUND_MIN_METRES + (packed / 16777215) * (GROUND_MAX_METRES - GROUND_MIN_METRES);
|
|
49832
|
+
};
|
|
49833
|
+
const x = u * size - 0.5;
|
|
49834
|
+
const y = v * size - 0.5;
|
|
49835
|
+
const x0 = Math.floor(x);
|
|
49836
|
+
const y0 = Math.floor(y);
|
|
49837
|
+
const fx = x - x0;
|
|
49838
|
+
const fy = y - y0;
|
|
49839
|
+
const top = read(x0, y0) + (read(x0 + 1, y0) - read(x0, y0)) * fx;
|
|
49840
|
+
const bottom = read(x0, y0 + 1) + (read(x0 + 1, y0 + 1) - read(x0, y0 + 1)) * fx;
|
|
49841
|
+
return top + (bottom - top) * fy;
|
|
49842
|
+
}
|
|
49246
49843
|
/**
|
|
49247
49844
|
* Feeds the surface from an animated frame series.
|
|
49248
49845
|
* Height and colour advance with the clock without the caller having to push each frame in.
|
|
@@ -49261,23 +49858,39 @@ void main() {
|
|
|
49261
49858
|
return;
|
|
49262
49859
|
}
|
|
49263
49860
|
const context = frameState.context;
|
|
49264
|
-
|
|
49265
|
-
|
|
49266
|
-
|
|
49267
|
-
|
|
49268
|
-
|
|
49269
|
-
}
|
|
49270
|
-
|
|
49271
|
-
|
|
49272
|
-
|
|
49273
|
-
|
|
49274
|
-
|
|
49861
|
+
const fade = this.valueSource && this.valueSource.GetValueFade
|
|
49862
|
+
? this.valueSource.GetValueFade()
|
|
49863
|
+
: null;
|
|
49864
|
+
if (fade) {
|
|
49865
|
+
this.fade = fade;
|
|
49866
|
+
this.sourceDims = { width: fade.width, height: fade.height };
|
|
49867
|
+
}
|
|
49868
|
+
else {
|
|
49869
|
+
this.fade = null;
|
|
49870
|
+
if (this.valueSource) {
|
|
49871
|
+
const canvas = this.valueSource.GetValueCanvas();
|
|
49872
|
+
if (!canvas) {
|
|
49873
|
+
// No frame decoded yet, so there is nothing to displace against.
|
|
49874
|
+
return;
|
|
49875
|
+
}
|
|
49876
|
+
const version = this.valueSource.GetPresentVersion();
|
|
49877
|
+
if (canvas !== this.source || version !== this.lastPresentVersion) {
|
|
49878
|
+
this.source = canvas;
|
|
49879
|
+
this.lastPresentVersion = version;
|
|
49880
|
+
this.textureDirty = true;
|
|
49881
|
+
}
|
|
49275
49882
|
}
|
|
49883
|
+
this.sourceDims = this.source ? { width: this.source.width, height: this.source.height } : null;
|
|
49276
49884
|
}
|
|
49277
|
-
if (!this.
|
|
49885
|
+
if (!this.sourceDims || !this.sourceDims.width || !this.sourceDims.height) {
|
|
49278
49886
|
return;
|
|
49279
49887
|
}
|
|
49280
|
-
this.
|
|
49888
|
+
if (this.fade) {
|
|
49889
|
+
this.syncFadeTextures(context);
|
|
49890
|
+
}
|
|
49891
|
+
else {
|
|
49892
|
+
this.syncTexture(context);
|
|
49893
|
+
}
|
|
49281
49894
|
this.syncRampTexture(context);
|
|
49282
49895
|
this.requestGround();
|
|
49283
49896
|
this.syncGroundTexture(context);
|
|
@@ -49325,6 +49938,10 @@ void main() {
|
|
|
49325
49938
|
this.texture.destroy();
|
|
49326
49939
|
this.texture = null;
|
|
49327
49940
|
}
|
|
49941
|
+
if (this.textureFrom) {
|
|
49942
|
+
this.textureFrom.destroy();
|
|
49943
|
+
this.textureFrom = null;
|
|
49944
|
+
}
|
|
49328
49945
|
if (this.rampTexture) {
|
|
49329
49946
|
this.rampTexture.destroy();
|
|
49330
49947
|
this.rampTexture = null;
|
|
@@ -49592,7 +50209,7 @@ void main() {
|
|
|
49592
50209
|
const southWest = Cartesian3.fromDegrees(this.extent.West, this.extent.South, 0);
|
|
49593
50210
|
const southEast = Cartesian3.fromDegrees(this.extent.East, this.extent.South, 0);
|
|
49594
50211
|
const width = Cartesian3.distance(southWest, southEast);
|
|
49595
|
-
return Math.max(1, width / Math.max(1, this.
|
|
50212
|
+
return Math.max(1, width / Math.max(1, this.sourceDims ? this.sourceDims.width : 1));
|
|
49596
50213
|
}
|
|
49597
50214
|
syncRampTexture(context) {
|
|
49598
50215
|
if (this.rampTexture) {
|
|
@@ -49613,6 +50230,67 @@ void main() {
|
|
|
49613
50230
|
})
|
|
49614
50231
|
});
|
|
49615
50232
|
}
|
|
50233
|
+
valueSampler() {
|
|
50234
|
+
// Nearest for a packed value, because filtering would blend the two bytes of the
|
|
50235
|
+
// packing rather than the values they encode, the same trap sampleGround avoids.
|
|
50236
|
+
const minification = this.packedValue
|
|
50237
|
+
? TextureMinificationFilter.NEAREST
|
|
50238
|
+
: TextureMinificationFilter.LINEAR;
|
|
50239
|
+
const magnification = this.packedValue
|
|
50240
|
+
? TextureMagnificationFilter.NEAREST
|
|
50241
|
+
: TextureMagnificationFilter.LINEAR;
|
|
50242
|
+
return new Sampler({
|
|
50243
|
+
wrapS: TextureWrap.CLAMP_TO_EDGE,
|
|
50244
|
+
wrapT: TextureWrap.CLAMP_TO_EDGE,
|
|
50245
|
+
minificationFilter: minification,
|
|
50246
|
+
magnificationFilter: magnification
|
|
50247
|
+
});
|
|
50248
|
+
}
|
|
50249
|
+
/*
|
|
50250
|
+
* Uploads value pixels into a texture, reusing it when the size still fits.
|
|
50251
|
+
*/
|
|
50252
|
+
uploadPixels(context, texture, pixels, width, height) {
|
|
50253
|
+
const view = new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength);
|
|
50254
|
+
const source = { width, height, arrayBufferView: view };
|
|
50255
|
+
if (texture && texture.width === width && texture.height === height) {
|
|
50256
|
+
texture.copyFrom({ source });
|
|
50257
|
+
return texture;
|
|
50258
|
+
}
|
|
50259
|
+
if (texture) {
|
|
50260
|
+
texture.destroy();
|
|
50261
|
+
}
|
|
50262
|
+
return new Texture({
|
|
50263
|
+
context,
|
|
50264
|
+
width,
|
|
50265
|
+
height,
|
|
50266
|
+
pixelFormat: PixelFormat.RGBA,
|
|
50267
|
+
pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
|
|
50268
|
+
source,
|
|
50269
|
+
// Row 0 is the archive's north edge, and has to stay at the top, as the canvas path does.
|
|
50270
|
+
flipY: false,
|
|
50271
|
+
sampler: this.valueSampler()
|
|
50272
|
+
});
|
|
50273
|
+
}
|
|
50274
|
+
/*
|
|
50275
|
+
* Keeps a texture each side of the fade, swapping them on a new frame so only that one is uploaded.
|
|
50276
|
+
*/
|
|
50277
|
+
syncFadeTextures(context) {
|
|
50278
|
+
const fade = this.fade;
|
|
50279
|
+
if (fade.to !== this.uploadedTo) {
|
|
50280
|
+
if (fade.from && fade.from === this.uploadedTo && this.texture) {
|
|
50281
|
+
const previous = this.textureFrom;
|
|
50282
|
+
this.textureFrom = this.texture;
|
|
50283
|
+
this.uploadedFrom = this.uploadedTo;
|
|
50284
|
+
this.texture = previous;
|
|
50285
|
+
}
|
|
50286
|
+
this.texture = this.uploadPixels(context, this.texture, fade.to, fade.width, fade.height);
|
|
50287
|
+
this.uploadedTo = fade.to;
|
|
50288
|
+
}
|
|
50289
|
+
if (fade.from && fade.from !== this.uploadedFrom) {
|
|
50290
|
+
this.textureFrom = this.uploadPixels(context, this.textureFrom, fade.from, fade.width, fade.height);
|
|
50291
|
+
this.uploadedFrom = fade.from;
|
|
50292
|
+
}
|
|
50293
|
+
}
|
|
49616
50294
|
syncTexture(context) {
|
|
49617
50295
|
const sizeChanged = this.texture
|
|
49618
50296
|
&& (this.texture.width !== this.source.width || this.texture.height !== this.source.height);
|
|
@@ -49620,27 +50298,15 @@ void main() {
|
|
|
49620
50298
|
if (this.texture) {
|
|
49621
50299
|
this.texture.destroy();
|
|
49622
50300
|
}
|
|
49623
|
-
// Nearest for a packed value, because filtering would blend the two bytes of the
|
|
49624
|
-
// packing rather than the values they encode, the same trap sampleGround avoids.
|
|
49625
|
-
const minification = this.packedValue
|
|
49626
|
-
? TextureMinificationFilter.NEAREST
|
|
49627
|
-
: TextureMinificationFilter.LINEAR;
|
|
49628
|
-
const magnification = this.packedValue
|
|
49629
|
-
? TextureMagnificationFilter.NEAREST
|
|
49630
|
-
: TextureMagnificationFilter.LINEAR;
|
|
49631
50301
|
this.texture = new Texture({
|
|
49632
50302
|
context,
|
|
49633
50303
|
source: this.source,
|
|
49634
50304
|
// Cesium flips canvas rows on upload by default, which would put the archive's
|
|
49635
50305
|
// row 0 (its north edge) at the south edge and mirror the whole surface.
|
|
49636
50306
|
flipY: false,
|
|
49637
|
-
sampler:
|
|
49638
|
-
wrapS: TextureWrap.CLAMP_TO_EDGE,
|
|
49639
|
-
wrapT: TextureWrap.CLAMP_TO_EDGE,
|
|
49640
|
-
minificationFilter: minification,
|
|
49641
|
-
magnificationFilter: magnification
|
|
49642
|
-
})
|
|
50307
|
+
sampler: this.valueSampler()
|
|
49643
50308
|
});
|
|
50309
|
+
this.uploadedTo = null;
|
|
49644
50310
|
this.textureDirty = false;
|
|
49645
50311
|
return;
|
|
49646
50312
|
}
|
|
@@ -49703,7 +50369,7 @@ void main() {
|
|
|
49703
50369
|
* carry, so the ladder is capped one step above the texel count rather than run to its top.
|
|
49704
50370
|
*/
|
|
49705
50371
|
tierCeiling() {
|
|
49706
|
-
const source = this.
|
|
50372
|
+
const source = this.sourceDims;
|
|
49707
50373
|
if (!source || !source.width || !source.height) {
|
|
49708
50374
|
return TIERS[TIERS.length - 1];
|
|
49709
50375
|
}
|
|
@@ -49781,6 +50447,10 @@ void main() {
|
|
|
49781
50447
|
owner: this,
|
|
49782
50448
|
uniformMap: {
|
|
49783
50449
|
u_valueTexture: () => self.texture,
|
|
50450
|
+
u_valueTextureFrom: () => (self.fade && self.fade.from && self.textureFrom
|
|
50451
|
+
? self.textureFrom
|
|
50452
|
+
: self.texture),
|
|
50453
|
+
u_valueFade: () => (self.fade && self.fade.from ? self.fade.mix : 1),
|
|
49784
50454
|
u_groundTexture: () => self.groundTexture,
|
|
49785
50455
|
u_valuePacked: () => (self.packedValue ? 1 : 0),
|
|
49786
50456
|
u_groundWeight: () => (self.followGround && self.groundPixels ? 1 : 0),
|
|
@@ -49818,7 +50488,7 @@ void main() {
|
|
|
49818
50488
|
: 0,
|
|
49819
50489
|
u_coverageCutoff: () => COVERAGE_CUTOFF,
|
|
49820
50490
|
u_rampTexture: () => self.rampTexture,
|
|
49821
|
-
u_texelSize: () => new Cartesian2(1 / self.
|
|
50491
|
+
u_texelSize: () => new Cartesian2(1 / self.sourceDims.width, 1 / self.sourceDims.height),
|
|
49822
50492
|
u_waterOn: () => (self.water ? 1 : 0),
|
|
49823
50493
|
u_waterWavelength: () => (self.water && self.water.wavelength)
|
|
49824
50494
|
|| DEFAULT_WATER_WAVELENGTH_METRES,
|
|
@@ -50190,6 +50860,10 @@ var EntityRenderEnginePolygon;
|
|
|
50190
50860
|
syncTextureValueLabels({
|
|
50191
50861
|
cEntity,
|
|
50192
50862
|
viewer: params.viewer,
|
|
50863
|
+
surface: drawsDisplacedSurface ? cEntity[DISPLACED_SURFACE_KEY] : null,
|
|
50864
|
+
hover: params.visualRegister
|
|
50865
|
+
? { register: params.visualRegister, entityId: String(entity.Bruce.ID), menuItemId: params.menuItemId }
|
|
50866
|
+
: undefined,
|
|
50193
50867
|
settings: overlaySettings(style, entity, params.tags),
|
|
50194
50868
|
extent: labelArchive
|
|
50195
50869
|
? (drawsDisplacedSurface ? archiveExtent(labelArchive.metadata, posses) : drapeExtent)
|
|
@@ -50496,6 +51170,7 @@ var EntityRenderEnginePolygon;
|
|
|
50496
51170
|
// rendered: null
|
|
50497
51171
|
rendered: (_c = params.rendered) === null || _c === void 0 ? void 0 : _c.get(entity.Bruce.ID),
|
|
50498
51172
|
menuItemId: params.menuItemId,
|
|
51173
|
+
visualRegister: params.visualRegister,
|
|
50499
51174
|
onSeriesDiscovered: params.onSeriesDiscovered
|
|
50500
51175
|
});
|
|
50501
51176
|
}
|
|
@@ -50933,8 +51608,8 @@ function wantsLabels(style) {
|
|
|
50933
51608
|
*/
|
|
50934
51609
|
const LABEL_GRID_COLOR = "rgba(255,255,255,0.55)";
|
|
50935
51610
|
const LABEL_GRID_WIDTH_PIXELS = 1;
|
|
50936
|
-
const LABEL_MIN_SPACING_PIXELS =
|
|
50937
|
-
const LABEL_MAX_LABELS =
|
|
51611
|
+
const LABEL_MIN_SPACING_PIXELS = 140;
|
|
51612
|
+
const LABEL_MAX_LABELS = 60;
|
|
50938
51613
|
function overlaySettings(style, entity, tags) {
|
|
50939
51614
|
if (!wantsLabels(style)) {
|
|
50940
51615
|
return undefined;
|
|
@@ -50954,6 +51629,7 @@ function overlaySettings(style, entity, tags) {
|
|
|
50954
51629
|
*/
|
|
50955
51630
|
function syncTextureValueLabels(params) {
|
|
50956
51631
|
const { cEntity, viewer, settings, extent, animator, metadata } = params;
|
|
51632
|
+
const surface = params.surface && !params.surface.isDestroyed() ? params.surface : null;
|
|
50957
51633
|
const existing = cEntity[TEXTURE_VALUE_LABELS_KEY];
|
|
50958
51634
|
// Offline has no extent to rebuild the overlay from, so an existing one is left running.
|
|
50959
51635
|
if (params.offline && existing) {
|
|
@@ -50972,8 +51648,13 @@ function syncTextureValueLabels(params) {
|
|
|
50972
51648
|
West: tile.West, East: tile.East, South: tile.South, North: tile.North,
|
|
50973
51649
|
ResolutionX: tile.ResolutionX, ResolutionY: tile.ResolutionY
|
|
50974
51650
|
})),
|
|
50975
|
-
clipRing: params.clipRing
|
|
51651
|
+
clipRing: params.clipRing,
|
|
51652
|
+
heightAt: surface ? (lon, lat) => surface.HeightAt(lon, lat) : undefined,
|
|
51653
|
+
heightKey: surface ? () => surface.GetPlacementKey() : undefined,
|
|
51654
|
+
hover: params.hover
|
|
50976
51655
|
});
|
|
51656
|
+
// A surface keeps no tinted frame, so whether a cell was painted is read off its ramp.
|
|
51657
|
+
labels.SetPaintedRamp(surface ? surface.GetRampPixels() : null);
|
|
50977
51658
|
let lastVersion = -1;
|
|
50978
51659
|
let appliedFloor = null;
|
|
50979
51660
|
let appliedCeiling = null;
|
|
@@ -50985,15 +51666,15 @@ function syncTextureValueLabels(params) {
|
|
|
50985
51666
|
if (animator.IsDisposed()) {
|
|
50986
51667
|
return;
|
|
50987
51668
|
}
|
|
50988
|
-
const canvas = animator.GetValueCanvas();
|
|
50989
|
-
if (!canvas) {
|
|
50990
|
-
return;
|
|
50991
|
-
}
|
|
50992
51669
|
const version = animator.GetPresentVersion();
|
|
50993
51670
|
if (version !== lastVersion) {
|
|
51671
|
+
// Read from the frame cache, since reading the value canvas back copies a whole frame.
|
|
51672
|
+
const raster = animator.GetValueRaster();
|
|
51673
|
+
if (!raster) {
|
|
51674
|
+
return;
|
|
51675
|
+
}
|
|
50994
51676
|
lastVersion = version;
|
|
50995
|
-
|
|
50996
|
-
labels.SetSource(ctx.getImageData(0, 0, canvas.width, canvas.height));
|
|
51677
|
+
labels.SetSource(raster);
|
|
50997
51678
|
labels.SetPainted(animator.GetDisplayedPixels());
|
|
50998
51679
|
}
|
|
50999
51680
|
// Tracked by reference rather than latched on the first one to arrive: a tiled archive
|
|
@@ -52732,7 +53413,7 @@ var StyleUtils;
|
|
|
52732
53413
|
StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
|
|
52733
53414
|
})(StyleUtils || (StyleUtils = {}));
|
|
52734
53415
|
|
|
52735
|
-
const VERSION = "7.
|
|
53416
|
+
const VERSION = "7.6.0";
|
|
52736
53417
|
/**
|
|
52737
53418
|
* Updates the environment instance used by bruce-cesium to one specified.
|
|
52738
53419
|
* This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
|