bruce-cesium 7.4.3 → 7.4.4
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 +328 -156
- package/dist/bruce-cesium.es5.js.map +1 -1
- package/dist/bruce-cesium.umd.js +326 -154
- package/dist/bruce-cesium.umd.js.map +1 -1
- package/dist/lib/bruce-cesium.js +1 -1
- package/dist/lib/rendering/render-managers/common/entity-label.js +325 -153
- package/dist/lib/rendering/render-managers/common/entity-label.js.map +1 -1
- package/dist/types/bruce-cesium.d.ts +1 -1
- package/dist/types/rendering/render-managers/common/entity-label.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,
|
|
3
|
-
import { Cartes, LRUCache, ProjectViewTile, ENVIRONMENT, Calculator, ClientFile, Entity as Entity$1, EntityLod, EntityTag, EntityType, ObjectUtils, Style, Carto, Geometry as Geometry$1,
|
|
2
|
+
import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, Math as Math$1, Cartesian3, JulianDate, Quaternion, Transforms, HeadingPitchRoll, HeightReference, Matrix4, ColorBlendMode, ShadowMode, ClassificationType, Model, HorizontalOrigin, VerticalOrigin, ConstantPositionProperty, ArcType, CornerType, DistanceDisplayCondition, ShaderSource, Ellipsoid, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, Cartesian2, Cartesian4, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, ImageMaterialProperty, PolygonHierarchy, PolylineGraphics, Rectangle, SceneTransforms, FeatureDetection, NearFarScalar, Matrix3, KmlDataSource, GeoJsonDataSource, SceneMode, Cesium3DTileStyle, Cesium3DTileColorBlendMode, HeadingPitchRange, Ion, IonResource, Cesium3DTileset, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, OrthographicFrustum, EasingFunction, CustomDataSource, PolygonPipeline, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, Intersect, GeometryInstance, CesiumInspector, ClockRange, DynamicEnvironmentMapManager, ScreenSpaceEventHandler, ScreenSpaceEventType, Fullscreen, CzmlDataSource } from 'cesium';
|
|
3
|
+
import { Cartes, LRUCache, ProjectViewTile, ENVIRONMENT, Calculator, ClientFile, Entity as Entity$1, EntityLod, EntityTag, EntityType, ObjectUtils, Style, Api, Carto, Geometry as Geometry$1, ClientFileValueMap, Color as Color$1, Bounds, MenuItem, ProjectView, ProjectViewBookmark, Tileset, ZoomControl, BruceEvent, EntityCoords, DataLab, DelayQueue, AccountConcept, RecordChangeFeed, BruceApi, EntityRelation, EntitySource, ProgramKey, ProjectViewLegacyTile, Camera, Account, AccountLimits, Session, UrlUtils, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, ProjectViewBookmarkGroup, EntityRelationType, EntityHistoricData } from 'bruce-models';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Ensures a number is returned from a given value.
|
|
@@ -5403,8 +5403,16 @@ var EntityLabel;
|
|
|
5403
5403
|
// so a row of labels reads as one size rather than a set of differently shaped pills.
|
|
5404
5404
|
const ICON_BACKGROUND_UNIFORM_TOLERANCE = 0.3;
|
|
5405
5405
|
const TEXT_MAX_WIDTH = 200;
|
|
5406
|
+
// Distinct icons a cluster shows, the representative's included.
|
|
5407
|
+
const ICON_STACK_LIMIT = 3;
|
|
5408
|
+
// Size of every icon in a cluster's arrangement, relative to the representative's own.
|
|
5409
|
+
const ICON_STACK_SCALE = 0.6;
|
|
5406
5410
|
// How far an angled leader runs sideways before the label starts.
|
|
5407
5411
|
const ANGLED_LEADER_RUN = 28;
|
|
5412
|
+
// Clearance between a point or billboard glyph and the ring drawn around it.
|
|
5413
|
+
const ICON_RING_PADDING = 4;
|
|
5414
|
+
// Small points still get a ring as large as the default near ring.
|
|
5415
|
+
const ICON_RING_MIN = 10;
|
|
5408
5416
|
// One per tinted icon, since an SVG mask is addressed by id across the whole document.
|
|
5409
5417
|
let tintCounter = 0;
|
|
5410
5418
|
// Key where the label instance is stored within the cesium entity.
|
|
@@ -5586,6 +5594,28 @@ var EntityLabel;
|
|
|
5586
5594
|
* Prefer declared semantics; only infers from recognisable glyphs and filename tokens, never image pixels.
|
|
5587
5595
|
*/
|
|
5588
5596
|
EntityLabel.IconPriority = GetClusterIconPriority;
|
|
5597
|
+
function iconLook(options) {
|
|
5598
|
+
return {
|
|
5599
|
+
icon: options.icon || undefined,
|
|
5600
|
+
iconUrl: options.iconUrl || undefined,
|
|
5601
|
+
iconClass: typeof options.iconClass === "string" && options.iconClass ? options.iconClass : undefined,
|
|
5602
|
+
iconColor: typeof options.iconColor === "string" && options.iconColor ? options.iconColor : undefined,
|
|
5603
|
+
iconBackground: options.iconBackground || undefined
|
|
5604
|
+
};
|
|
5605
|
+
}
|
|
5606
|
+
/*
|
|
5607
|
+
* Everything that makes two labels look alike, so labels from different groups only merge when they would read the same.
|
|
5608
|
+
*/
|
|
5609
|
+
function clusterLook(options) {
|
|
5610
|
+
var _a;
|
|
5611
|
+
const presentation = options.presentation || "text";
|
|
5612
|
+
return JSON.stringify([
|
|
5613
|
+
presentation,
|
|
5614
|
+
(_a = options.leader) !== null && _a !== void 0 ? _a : null,
|
|
5615
|
+
options.leaderStyle || "default", options.leaderColor || "",
|
|
5616
|
+
presentation === "text" ? null : iconLook(options)
|
|
5617
|
+
]);
|
|
5618
|
+
}
|
|
5589
5619
|
/*
|
|
5590
5620
|
* Resolves clustering and stable text placement before depth fading; highlighted labels receive priority.
|
|
5591
5621
|
*/
|
|
@@ -5607,7 +5637,8 @@ var EntityLabel;
|
|
|
5607
5637
|
if (!((_b = (_a = frame.options) === null || _a === void 0 ? void 0 : _a.cluster) === null || _b === void 0 ? void 0 : _b.enabled) || frame.priority > 0) {
|
|
5608
5638
|
continue;
|
|
5609
5639
|
}
|
|
5610
|
-
|
|
5640
|
+
// Groups meet inside a presentation; which of them may merge is decided pairwise below.
|
|
5641
|
+
const key = frame.options.presentation || "text";
|
|
5611
5642
|
if (!groups.has(key)) {
|
|
5612
5643
|
groups.set(key, []);
|
|
5613
5644
|
}
|
|
@@ -5619,32 +5650,54 @@ var EntityLabel;
|
|
|
5619
5650
|
frame.options.cluster.pixelRange == null ? { ...frame, x: frame.anchorX, y: frame.anchorY, width: 1, height: 1 } :
|
|
5620
5651
|
frame.clusterBounds ? { ...frame, ...frame.clusterBounds } : frame);
|
|
5621
5652
|
const index = new ScreenIndex();
|
|
5622
|
-
|
|
5653
|
+
const rank = new Map();
|
|
5654
|
+
const looks = new Map();
|
|
5655
|
+
candidates.forEach((frame, i) => {
|
|
5623
5656
|
index.Add(frame);
|
|
5624
|
-
|
|
5657
|
+
rank.set(frame.id, i);
|
|
5658
|
+
looks.set(frame.id, clusterLook(frame.options));
|
|
5659
|
+
});
|
|
5625
5660
|
const clusters = ClusterItems({
|
|
5626
5661
|
items: candidates,
|
|
5627
5662
|
id: frame => frame.id,
|
|
5628
5663
|
minSize: group[0].options.cluster.minSize,
|
|
5629
5664
|
neighbours: frame => {
|
|
5630
5665
|
var _a;
|
|
5631
|
-
|
|
5666
|
+
const nearby = index.Query(frame, (_a = frame.options.cluster.pixelRange) !== null && _a !== void 0 ? _a : (frame.options.presentation === "icon" ? 24 : 8)).sort((a, b) => rank.get(a.id) - rank.get(b.id));
|
|
5667
|
+
return compatibleMembers(frame, nearby, other => other.group || "", other => looks.get(other.id));
|
|
5632
5668
|
}
|
|
5633
5669
|
});
|
|
5634
5670
|
for (const cluster of clusters) {
|
|
5635
5671
|
let chosen = cluster.representative;
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
}
|
|
5644
|
-
}
|
|
5672
|
+
const iconic = (chosen.options.presentation || "text") !== "text";
|
|
5673
|
+
// Members in the order they would win representation; the seed is the nearest.
|
|
5674
|
+
let ordered = [chosen, ...cluster.members.filter(member => member !== chosen)];
|
|
5675
|
+
if (iconic && chosen.options.cluster.representative !== "nearest") {
|
|
5676
|
+
ordered = ordered.map((member, i) => ({ member, i, priority: EntityLabel.IconPriority(member.options) }))
|
|
5677
|
+
.sort((a, b) => b.priority - a.priority || a.i - b.i).map(entry => entry.member);
|
|
5678
|
+
chosen = ordered[0];
|
|
5645
5679
|
}
|
|
5646
5680
|
const representative = result.get(chosen.id);
|
|
5647
5681
|
representative.members = cluster.members.map(frame => frame.id);
|
|
5682
|
+
if (iconic && chosen.options.iconScale !== 0) {
|
|
5683
|
+
const seen = new Set();
|
|
5684
|
+
const icons = [];
|
|
5685
|
+
for (const member of ordered) {
|
|
5686
|
+
const look = iconLook(member.options);
|
|
5687
|
+
const key = JSON.stringify(look);
|
|
5688
|
+
if (seen.has(key)) {
|
|
5689
|
+
continue;
|
|
5690
|
+
}
|
|
5691
|
+
seen.add(key);
|
|
5692
|
+
if (member !== ordered[0])
|
|
5693
|
+
icons.push(look);
|
|
5694
|
+
if (seen.size >= ICON_STACK_LIMIT) {
|
|
5695
|
+
break;
|
|
5696
|
+
}
|
|
5697
|
+
}
|
|
5698
|
+
if (icons.length)
|
|
5699
|
+
representative.icons = icons;
|
|
5700
|
+
}
|
|
5648
5701
|
representative.entityIds = Array.from(new Set(cluster.members.map(frame => frame.entityId).filter(Boolean)));
|
|
5649
5702
|
if (chosen.options.presentation !== "icon") {
|
|
5650
5703
|
const values = new Set();
|
|
@@ -5697,6 +5750,34 @@ var EntityLabel;
|
|
|
5697
5750
|
return Array.from(result.values());
|
|
5698
5751
|
}
|
|
5699
5752
|
EntityLabel.Layout = Layout;
|
|
5753
|
+
/*
|
|
5754
|
+
* The seed plus every neighbour, in order, that shares a group or a look with each member accepted so far.
|
|
5755
|
+
*
|
|
5756
|
+
* Checking against the whole accepted set stops a group with mixed icons bridging two groups that look different.
|
|
5757
|
+
*/
|
|
5758
|
+
function compatibleMembers(seed, nearby, group, look) {
|
|
5759
|
+
const accepted = [seed];
|
|
5760
|
+
const byGroup = new Map([[group(seed), 1]]);
|
|
5761
|
+
const byLook = new Map([[look(seed), 1]]);
|
|
5762
|
+
const byBoth = new Map([[JSON.stringify([group(seed), look(seed)]), 1]]);
|
|
5763
|
+
for (const item of nearby) {
|
|
5764
|
+
if (item === seed) {
|
|
5765
|
+
continue;
|
|
5766
|
+
}
|
|
5767
|
+
const itemGroup = group(item), itemLook = look(item), both = JSON.stringify([itemGroup, itemLook]);
|
|
5768
|
+
// Accepted members in another group with another look are the ones this item may not sit beside.
|
|
5769
|
+
const conflicting = accepted.length - (byGroup.get(itemGroup) || 0) - (byLook.get(itemLook) || 0) + (byBoth.get(both) || 0);
|
|
5770
|
+
if (conflicting > 0) {
|
|
5771
|
+
continue;
|
|
5772
|
+
}
|
|
5773
|
+
accepted.push(item);
|
|
5774
|
+
byGroup.set(itemGroup, (byGroup.get(itemGroup) || 0) + 1);
|
|
5775
|
+
byLook.set(itemLook, (byLook.get(itemLook) || 0) + 1);
|
|
5776
|
+
byBoth.set(both, (byBoth.get(both) || 0) + 1);
|
|
5777
|
+
}
|
|
5778
|
+
return accepted;
|
|
5779
|
+
}
|
|
5780
|
+
EntityLabel.compatibleMembers = compatibleMembers;
|
|
5700
5781
|
const layers = new WeakMap();
|
|
5701
5782
|
const containers = new WeakMap();
|
|
5702
5783
|
/*
|
|
@@ -6094,7 +6175,7 @@ var EntityLabel;
|
|
|
6094
6175
|
* @param starterPos3d optional position to create label at. Ignored for billboard labels.
|
|
6095
6176
|
*/
|
|
6096
6177
|
async createLabel(starterPos3d) {
|
|
6097
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o
|
|
6178
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
6098
6179
|
this.removeLabel();
|
|
6099
6180
|
let pos3d = starterPos3d ? starterPos3d.clone() : null;
|
|
6100
6181
|
const counter = ++this.counter;
|
|
@@ -6416,125 +6497,28 @@ var EntityLabel;
|
|
|
6416
6497
|
let iconElement;
|
|
6417
6498
|
let iconContainer;
|
|
6418
6499
|
let fitIconBackground;
|
|
6500
|
+
let iconStack;
|
|
6501
|
+
let stackedFits = [];
|
|
6502
|
+
let displayedStack = "[]";
|
|
6419
6503
|
content.className = "entity-label-content";
|
|
6420
6504
|
content.style.cssText = "display:flex;align-items:center;gap:6px;";
|
|
6421
6505
|
if (presentation === "icon-text-above") {
|
|
6422
6506
|
content.style.flexDirection = "column-reverse";
|
|
6423
6507
|
}
|
|
6424
6508
|
if (presentation !== "text") {
|
|
6425
|
-
const
|
|
6426
|
-
iconElement = icon;
|
|
6427
|
-
|
|
6428
|
-
icon.style.cssText = `font-size:${(this.options.iconSize || 24) * ((_m = this.options.iconScale) !== null && _m !== void 0 ? _m : 1)}px;line-height:1;`;
|
|
6429
|
-
icon.style.textShadow = "0 1px 2px rgba(0, 0, 0, 0.25)";
|
|
6430
|
-
const iconColor = typeof this.options.iconColor === "string" ? this.options.iconColor : undefined;
|
|
6431
|
-
const iconBackground = this.options.iconBackground && this.options.iconScale !== 0;
|
|
6432
|
-
if (iconColor) {
|
|
6433
|
-
icon.style.color = iconColor;
|
|
6434
|
-
}
|
|
6435
|
-
if (this.options.iconUrl) {
|
|
6436
|
-
const image = document.createElement("img");
|
|
6437
|
-
image.draggable = false;
|
|
6438
|
-
image.alt = this.options.icon || "";
|
|
6439
|
-
const scale = (_o = this.options.iconScale) !== null && _o !== void 0 ? _o : 1;
|
|
6440
|
-
const size = this.options.iconSize;
|
|
6441
|
-
const svgNs = "http://www.w3.org/2000/svg";
|
|
6442
|
-
const tint = iconColor ? document.createElementNS(svgNs, "svg") : null;
|
|
6443
|
-
const painted = tint ? document.createElementNS(svgNs, "image") : null;
|
|
6444
|
-
const inked = tint ? document.createElementNS(svgNs, "rect") : null;
|
|
6445
|
-
if (tint) {
|
|
6446
|
-
const maskId = `entity-label-tint-${++tintCounter}`;
|
|
6447
|
-
const mask = document.createElementNS(svgNs, "mask");
|
|
6448
|
-
mask.setAttribute("id", maskId);
|
|
6449
|
-
mask.setAttribute("maskUnits", "userSpaceOnUse");
|
|
6450
|
-
painted.setAttribute("href", this.options.iconUrl);
|
|
6451
|
-
painted.setAttribute("preserveAspectRatio", "xMidYMid meet");
|
|
6452
|
-
mask.appendChild(painted);
|
|
6453
|
-
inked.setAttribute("fill", iconColor);
|
|
6454
|
-
inked.setAttribute("mask", `url(#${maskId})`);
|
|
6455
|
-
tint.append(mask, inked);
|
|
6456
|
-
tint.style.display = "block";
|
|
6457
|
-
icon.style.display = "inline-flex";
|
|
6458
|
-
icon.appendChild(tint);
|
|
6459
|
-
}
|
|
6460
|
-
const resize = () => {
|
|
6461
|
-
if (this.disposed || this.counter !== counter) {
|
|
6462
|
-
return;
|
|
6463
|
-
}
|
|
6464
|
-
const intrinsic = this.options.iconScale != null && size == null;
|
|
6465
|
-
const width = (intrinsic ? image.naturalWidth || 24 : size || 24) * scale;
|
|
6466
|
-
const height = (intrinsic ? image.naturalHeight || 24 : size || 24) * scale;
|
|
6467
|
-
if (tint) {
|
|
6468
|
-
tint.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
6469
|
-
for (const node of [tint, painted, inked]) {
|
|
6470
|
-
node.setAttribute("width", String(width));
|
|
6471
|
-
node.setAttribute("height", String(height));
|
|
6472
|
-
}
|
|
6473
|
-
}
|
|
6474
|
-
else {
|
|
6475
|
-
image.style.width = `${width}px`;
|
|
6476
|
-
image.style.height = `${height}px`;
|
|
6477
|
-
}
|
|
6478
|
-
this.viewer.scene.requestRender();
|
|
6479
|
-
};
|
|
6480
|
-
image.onload = resize;
|
|
6481
|
-
resize();
|
|
6482
|
-
image.src = this.options.iconUrl;
|
|
6483
|
-
if (!tint) {
|
|
6484
|
-
icon.appendChild(image);
|
|
6485
|
-
}
|
|
6486
|
-
}
|
|
6487
|
-
else if (typeof this.options.iconClass === "string" && this.options.iconClass) {
|
|
6488
|
-
icon.className = `entity-label-icon ${this.options.iconClass}`;
|
|
6489
|
-
}
|
|
6490
|
-
else {
|
|
6491
|
-
icon.textContent = this.options.icon || "●";
|
|
6492
|
-
}
|
|
6493
|
-
if (iconBackground) {
|
|
6494
|
-
const color = Color.fromCssColorString(iconColor || "white") || Color.WHITE;
|
|
6495
|
-
const luminance = 0.2126 * color.red + 0.7152 * color.green + 0.0722 * color.blue;
|
|
6496
|
-
const background = (luminance > 0.5 ? color.darken(ICON_BACKGROUND_BLEND, new Color()) :
|
|
6497
|
-
color.brighten(ICON_BACKGROUND_BLEND, new Color()));
|
|
6498
|
-
const size = (this.options.iconSize || 24) * ((_p = this.options.iconScale) !== null && _p !== void 0 ? _p : 1);
|
|
6499
|
-
icon.style.backgroundColor = background.withAlpha(ICON_BACKGROUND_ALPHA).toCssColorString();
|
|
6500
|
-
icon.style.borderRadius = "50%";
|
|
6501
|
-
icon.style.padding = `${size / 3}px`;
|
|
6502
|
-
icon.style.display = "inline-flex";
|
|
6503
|
-
icon.style.alignItems = "center";
|
|
6504
|
-
icon.style.justifyContent = "center";
|
|
6505
|
-
icon.style.minWidth = `${size}px`;
|
|
6506
|
-
icon.style.minHeight = `${size}px`;
|
|
6507
|
-
icon.style.boxSizing = "content-box";
|
|
6508
|
-
let fitted = false;
|
|
6509
|
-
fitIconBackground = () => {
|
|
6510
|
-
if (fitted) {
|
|
6511
|
-
return;
|
|
6512
|
-
}
|
|
6513
|
-
const padding = size / 3;
|
|
6514
|
-
const width = icon.offsetWidth - padding * 2;
|
|
6515
|
-
const height = icon.offsetHeight - padding * 2;
|
|
6516
|
-
if (width <= 0 || height <= 0) {
|
|
6517
|
-
return;
|
|
6518
|
-
}
|
|
6519
|
-
fitted = true;
|
|
6520
|
-
const longest = Math.max(width, height);
|
|
6521
|
-
const difference = (longest - Math.min(width, height)) / longest;
|
|
6522
|
-
if (difference > ICON_BACKGROUND_UNIFORM_TOLERANCE) {
|
|
6523
|
-
return;
|
|
6524
|
-
}
|
|
6525
|
-
icon.style.minWidth = `${longest}px`;
|
|
6526
|
-
icon.style.minHeight = `${longest}px`;
|
|
6527
|
-
this.viewer.scene.requestRender();
|
|
6528
|
-
};
|
|
6529
|
-
}
|
|
6530
|
-
if (this.options.iconScale === 0) {
|
|
6531
|
-
icon.style.display = "none";
|
|
6532
|
-
}
|
|
6509
|
+
const built = this.buildIcon(this.options, counter);
|
|
6510
|
+
const icon = iconElement = built.icon;
|
|
6511
|
+
fitIconBackground = built.fit;
|
|
6533
6512
|
makeClickable(icon);
|
|
6534
6513
|
// Keep badges outside the glyph so font classes and image masks cannot affect them.
|
|
6535
6514
|
iconContainer = document.createElement("span");
|
|
6536
|
-
iconContainer.style.cssText = "position:relative;display:inline-flex;flex:none;";
|
|
6537
|
-
|
|
6515
|
+
iconContainer.style.cssText = "position:relative;display:inline-flex;flex:none;align-items:center;";
|
|
6516
|
+
icon.style.position = "relative";
|
|
6517
|
+
iconStack = document.createElement("span");
|
|
6518
|
+
iconStack.className = "entity-label-icon-stack";
|
|
6519
|
+
iconStack.style.cssText = "display:none;gap:2px;align-items:center;justify-items:center;flex:none;";
|
|
6520
|
+
makeClickable(iconStack);
|
|
6521
|
+
iconContainer.append(iconStack, icon);
|
|
6538
6522
|
content.appendChild(iconContainer);
|
|
6539
6523
|
}
|
|
6540
6524
|
if (presentation !== "icon") {
|
|
@@ -6546,7 +6530,7 @@ var EntityLabel;
|
|
|
6546
6530
|
if (presentation === "text")
|
|
6547
6531
|
makeClickable(textElement);
|
|
6548
6532
|
content.appendChild(textElement);
|
|
6549
|
-
if ((
|
|
6533
|
+
if ((_m = this.options.cluster) === null || _m === void 0 ? void 0 : _m.enabled) {
|
|
6550
6534
|
sourceText = document.createElement("span");
|
|
6551
6535
|
sourceText.className = "entity-label-source-text";
|
|
6552
6536
|
sourceText.setAttribute("aria-hidden", "true");
|
|
@@ -6587,7 +6571,7 @@ var EntityLabel;
|
|
|
6587
6571
|
ele.appendChild(countElement);
|
|
6588
6572
|
}
|
|
6589
6573
|
ele.className = "entity-label";
|
|
6590
|
-
ele.dataset.entityId = ((
|
|
6574
|
+
ele.dataset.entityId = ((_o = this.rego) === null || _o === void 0 ? void 0 : _o.entityId) || "";
|
|
6591
6575
|
ele.dataset.presentation = presentation;
|
|
6592
6576
|
if (this.groupId) {
|
|
6593
6577
|
ele.dataset.groupId = this.groupId;
|
|
@@ -6703,6 +6687,41 @@ var EntityLabel;
|
|
|
6703
6687
|
run, side: (rightwards ? "right" : "left")
|
|
6704
6688
|
};
|
|
6705
6689
|
};
|
|
6690
|
+
/*
|
|
6691
|
+
* The on-screen centre and half-size of a point or billboard glyph, relative to its projected position.
|
|
6692
|
+
* Read every frame, since styling and selection can rescale a billboard in place.
|
|
6693
|
+
*/
|
|
6694
|
+
const measureIcon = () => {
|
|
6695
|
+
var _a;
|
|
6696
|
+
if (!normalLeader || !(visual instanceof Entity)) {
|
|
6697
|
+
return null;
|
|
6698
|
+
}
|
|
6699
|
+
if (visual.point) {
|
|
6700
|
+
const size = EnsureNumber(getValue(this.viewer, visual.point.pixelSize), 1) +
|
|
6701
|
+
EnsureNumber(getValue(this.viewer, visual.point.outlineWidth), 0) * 2;
|
|
6702
|
+
return size > 0 ? { x: 0, y: 0, radius: size / 2 } : null;
|
|
6703
|
+
}
|
|
6704
|
+
if (!visual.billboard) {
|
|
6705
|
+
return null;
|
|
6706
|
+
}
|
|
6707
|
+
const billboard = visual.billboard;
|
|
6708
|
+
const scale = EnsureNumber(getValue(this.viewer, billboard.scale), 1);
|
|
6709
|
+
// Icon billboards only record their image height; generated circles set both sides.
|
|
6710
|
+
const height = EnsureNumber((_a = getValue(this.viewer, billboard.height)) !== null && _a !== void 0 ? _a : billboard._billboardSize, 0) * scale;
|
|
6711
|
+
const width = EnsureNumber(getValue(this.viewer, billboard.width), height / scale) * scale;
|
|
6712
|
+
if (!(height > 0) || !(width > 0)) {
|
|
6713
|
+
return null;
|
|
6714
|
+
}
|
|
6715
|
+
const vertical = getValue(this.viewer, billboard.verticalOrigin);
|
|
6716
|
+
const horizontal = getValue(this.viewer, billboard.horizontalOrigin);
|
|
6717
|
+
const offset = getValue(this.viewer, billboard.pixelOffset);
|
|
6718
|
+
return {
|
|
6719
|
+
x: ((offset === null || offset === void 0 ? void 0 : offset.x) || 0) + (horizontal === HorizontalOrigin.LEFT ? width / 2 : horizontal === HorizontalOrigin.RIGHT ? -width / 2 : 0),
|
|
6720
|
+
y: ((offset === null || offset === void 0 ? void 0 : offset.y) || 0) + (vertical === VerticalOrigin.BOTTOM || vertical === VerticalOrigin.BASELINE ? -height / 2 :
|
|
6721
|
+
vertical === VerticalOrigin.TOP ? height / 2 : 0),
|
|
6722
|
+
radius: Math.max(width, height) / 2
|
|
6723
|
+
};
|
|
6724
|
+
};
|
|
6706
6725
|
let cursorMotion = this.cursorMotion;
|
|
6707
6726
|
const project = () => {
|
|
6708
6727
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
@@ -6769,9 +6788,13 @@ var EntityLabel;
|
|
|
6769
6788
|
this.cursorMotion = cursorMotion;
|
|
6770
6789
|
const cursorAmount = cursorMotion.amount;
|
|
6771
6790
|
leader = normalLeader || cursorAmount > 0;
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6791
|
+
// An icon keeps its callout centred on the glyph rather than following the cursor across it.
|
|
6792
|
+
const icon = measureIcon();
|
|
6793
|
+
const iconRing = icon ? Math.max(ICON_RING_MIN, icon.radius + ICON_RING_PADDING) : 0;
|
|
6794
|
+
const pivot = icon ? undefined : transientPivot;
|
|
6795
|
+
let x = ((_l = pivot === null || pivot === void 0 ? void 0 : pivot.x) !== null && _l !== void 0 ? _l : anchor.x) + (icon || pixelOffset).x;
|
|
6796
|
+
let y = ((_m = pivot === null || pivot === void 0 ? void 0 : pivot.y) !== null && _m !== void 0 ? _m : anchor.y) + (icon || pixelOffset).y;
|
|
6797
|
+
if (cursorAmount > 0 && !icon) {
|
|
6775
6798
|
// A leader-less label starts centred on its anchor; preserve that box as it lifts away.
|
|
6776
6799
|
if (!normalLeader) {
|
|
6777
6800
|
x -= ele.offsetWidth / layer.coordinates.scaleX / 2;
|
|
@@ -6809,9 +6832,11 @@ var EntityLabel;
|
|
|
6809
6832
|
if (cursorAmount > 0) {
|
|
6810
6833
|
leaderLength += (96 - leaderLength) * cursorAmount;
|
|
6811
6834
|
}
|
|
6835
|
+
// Measured from the ring, so the visible stem is the same length whatever the glyph's size.
|
|
6836
|
+
leaderLength += iconRing;
|
|
6812
6837
|
y = anchorY - leaderLength;
|
|
6813
6838
|
}
|
|
6814
|
-
else if (hover && !reused) {
|
|
6839
|
+
else if (hover && !reused && !icon) {
|
|
6815
6840
|
x = hover.x + pixelOffset.x;
|
|
6816
6841
|
y = hover.y + pixelOffset.y - 24;
|
|
6817
6842
|
}
|
|
@@ -6863,6 +6888,7 @@ var EntityLabel;
|
|
|
6863
6888
|
viewportWidth: this.viewer.canvas.clientWidth,
|
|
6864
6889
|
id: this.id, entityKey: ((_s = this.rego) === null || _s === void 0 ? void 0 : _s.entityId) ? JSON.stringify([this.rego.entityId, this.rego.menuItemId || "", this.Transient && !this.Persistent ? "hover" : ""]) : undefined,
|
|
6865
6890
|
entityId: (_t = this.rego) === null || _t === void 0 ? void 0 : _t.entityId, group: this.groupId, x, y, width, height, anchorX, anchorY, depth, leaderLength, leaderRun, leaderSide, distance,
|
|
6891
|
+
iconRadius: icon === null || icon === void 0 ? void 0 : icon.radius,
|
|
6866
6892
|
// Priority shows the hovered member individually without dragging its cluster along.
|
|
6867
6893
|
opacity, priority: hover || cursorAmount > 0 || (state === null || state === void 0 ? void 0 : state.highlighted) ? 1 : 0, options: this.options, text: originalText, clusterBounds
|
|
6868
6894
|
};
|
|
@@ -6870,7 +6896,7 @@ var EntityLabel;
|
|
|
6870
6896
|
let displacement = { x: 0, y: 0 };
|
|
6871
6897
|
let nudge = { x: 0, y: 0, fromX: 0, fromY: 0, targetX: 0, targetY: 0, start: 0 };
|
|
6872
6898
|
const apply = (placement) => {
|
|
6873
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
|
|
6899
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
|
|
6874
6900
|
const show = (placement === null || placement === void 0 ? void 0 : placement.visible) && placement.opacity > 0;
|
|
6875
6901
|
clickEntityIds = show ? placement.entityIds : [];
|
|
6876
6902
|
ele.style.visibility = show ? "visible" : "hidden";
|
|
@@ -6907,6 +6933,29 @@ var EntityLabel;
|
|
|
6907
6933
|
this.viewer.scene.requestRender();
|
|
6908
6934
|
}
|
|
6909
6935
|
fitIconBackground === null || fitIconBackground === void 0 ? void 0 : fitIconBackground();
|
|
6936
|
+
const stack = JSON.stringify(placement.icons || []);
|
|
6937
|
+
if (iconStack && stack !== displayedStack) {
|
|
6938
|
+
displayedStack = stack;
|
|
6939
|
+
const looks = ((_b = placement.icons) === null || _b === void 0 ? void 0 : _b.length) ? [iconLook(this.options), ...placement.icons] : [];
|
|
6940
|
+
// One small size for all, so no icon in the arrangement reads as more important than another.
|
|
6941
|
+
const size = (this.options.iconSize || 24) * ((_c = this.options.iconScale) !== null && _c !== void 0 ? _c : 1) * ICON_STACK_SCALE;
|
|
6942
|
+
const built = looks.map(look => this.buildIcon({ ...look, iconSize: size }, counter));
|
|
6943
|
+
// Two sit side by side; three make a triangle with the representative on top.
|
|
6944
|
+
iconStack.style.gridTemplateColumns = built.length === 3 ? "auto auto" : `repeat(${built.length}, auto)`;
|
|
6945
|
+
if (built.length === 3) {
|
|
6946
|
+
built[0].icon.style.gridColumn = "1 / span 2";
|
|
6947
|
+
}
|
|
6948
|
+
iconStack.replaceChildren(...built.map(({ icon }) => icon));
|
|
6949
|
+
iconStack.style.display = built.length ? "inline-grid" : "none";
|
|
6950
|
+
// The glyph stays measurable, since cluster membership is taken from its footprint.
|
|
6951
|
+
iconElement.style.visibility = built.length ? "hidden" : "";
|
|
6952
|
+
iconElement.style.position = built.length ? "absolute" : "relative";
|
|
6953
|
+
stackedFits = built.map(({ fit }) => fit).filter(Boolean);
|
|
6954
|
+
// The stack widens the label, which the next frame has to measure.
|
|
6955
|
+
this.viewer.scene.requestRender();
|
|
6956
|
+
}
|
|
6957
|
+
for (const fit of stackedFits)
|
|
6958
|
+
fit();
|
|
6910
6959
|
if (iconContainer) {
|
|
6911
6960
|
const badgeWidth = countElement.offsetWidth;
|
|
6912
6961
|
const iconWidth = iconElement.offsetWidth;
|
|
@@ -6922,36 +6971,43 @@ var EntityLabel;
|
|
|
6922
6971
|
// Summary and badge changes must fit on their first visible frame, including after resize.
|
|
6923
6972
|
const ring = this.options.anchorRing;
|
|
6924
6973
|
const pointer = (layer.pointer || (cursorMotion.target ? cursorMotion.position : undefined) || (this.Transient && !this.Persistent ?
|
|
6925
|
-
(this.hoverFade.to === 0 ? this.retainedHoverPosition : (
|
|
6926
|
-
const
|
|
6927
|
-
const
|
|
6974
|
+
(this.hoverFade.to === 0 ? this.retainedHoverPosition : (_d = this.visualRegister) === null || _d === void 0 ? void 0 : _d.GetLabelHoverPosition((_e = this.rego) === null || _e === void 0 ? void 0 : _e.entityId, (_f = this.rego) === null || _f === void 0 ? void 0 : _f.menuItemId)) : undefined));
|
|
6975
|
+
const iconRing = frame.iconRadius ? Math.max(ICON_RING_MIN, frame.iconRadius + ICON_RING_PADDING) : 0;
|
|
6976
|
+
const hovered = pointer && Math.hypot(pointer.x - frame.anchorX, pointer.y - frame.anchorY) <= Math.max((_g = ring === null || ring === void 0 ? void 0 : ring.hoverDistance) !== null && _g !== void 0 ? _g : 24, iconRing);
|
|
6977
|
+
const normalRadius = (!leader || (ring === null || ring === void 0 ? void 0 : ring.enabled) === false ? 0 : Math.max(frame.distance <= ((_h = ring === null || ring === void 0 ? void 0 : ring.nearDistance) !== null && _h !== void 0 ? _h : 100) ? ((_j = ring === null || ring === void 0 ? void 0 : ring.nearRadius) !== null && _j !== void 0 ? _j : 10) : 0, hovered ? ((_k = ring === null || ring === void 0 ? void 0 : ring.hoverRadius) !== null && _k !== void 0 ? _k : (this.Transient && !this.Persistent ? 15.4 : 22)) : 0));
|
|
6928
6978
|
const targetRadius = normalRadius + (15.4 - normalRadius) * cursorMotion.amount;
|
|
6979
|
+
// Around a glyph the ring keeps the glyph's size and only fades its stroke in, so it never covers the icon.
|
|
6980
|
+
const targetOuter = iconRing || targetRadius || 2.5;
|
|
6981
|
+
const targetInner = iconRing ? (targetRadius > 0 ? iconRing - 2 : iconRing) : Math.max(0, targetRadius - 2);
|
|
6929
6982
|
const now = Date.now();
|
|
6930
|
-
const duration = (
|
|
6983
|
+
const duration = (_l = ring === null || ring === void 0 ? void 0 : ring.transitionDuration) !== null && _l !== void 0 ? _l : 200;
|
|
6931
6984
|
const sharedRing = this.Transient && !this.Persistent && this.hoverFade.to === 1 ? this.hoverRingState : undefined;
|
|
6932
6985
|
let ringAnimation = sharedRing ? sharedRing.animation : this.ringAnimation;
|
|
6933
6986
|
if (!ringAnimation) {
|
|
6934
|
-
|
|
6987
|
+
const outer = iconRing || 2.5;
|
|
6988
|
+
const inner = iconRing;
|
|
6989
|
+
ringAnimation = { target: outer, targetInner: inner, start: now, outer, inner, fromOuter: outer, fromInner: inner };
|
|
6935
6990
|
}
|
|
6936
6991
|
const animateRing = () => {
|
|
6937
6992
|
const progress = duration > 0 ? Math.min(1, Math.max(0, (now - ringAnimation.start) / duration)) : 1;
|
|
6938
6993
|
const eased = progress * progress * (3 - 2 * progress);
|
|
6939
|
-
ringAnimation.outer = ringAnimation.fromOuter + (
|
|
6940
|
-
ringAnimation.inner = ringAnimation.fromInner + (
|
|
6994
|
+
ringAnimation.outer = ringAnimation.fromOuter + (ringAnimation.target - ringAnimation.fromOuter) * eased;
|
|
6995
|
+
ringAnimation.inner = ringAnimation.fromInner + (ringAnimation.targetInner - ringAnimation.fromInner) * eased;
|
|
6941
6996
|
return progress;
|
|
6942
6997
|
};
|
|
6943
6998
|
animateRing();
|
|
6944
|
-
if (ringAnimation.target !==
|
|
6999
|
+
if (ringAnimation.target !== targetOuter || ringAnimation.targetInner !== targetInner) {
|
|
6945
7000
|
ringAnimation = {
|
|
6946
7001
|
...ringAnimation,
|
|
6947
|
-
target:
|
|
7002
|
+
target: targetOuter,
|
|
7003
|
+
targetInner,
|
|
6948
7004
|
start: now,
|
|
6949
7005
|
fromOuter: ringAnimation.outer,
|
|
6950
7006
|
fromInner: ringAnimation.inner
|
|
6951
7007
|
};
|
|
6952
7008
|
}
|
|
6953
7009
|
// Animate the hole and stem together; no CSS transition may lag behind a moving world anchor.
|
|
6954
|
-
if (animateRing() < 1 && (ringAnimation.fromOuter !==
|
|
7010
|
+
if (animateRing() < 1 && (ringAnimation.fromOuter !== targetOuter || ringAnimation.fromInner !== targetInner)) {
|
|
6955
7011
|
this.viewer.scene.requestRender();
|
|
6956
7012
|
}
|
|
6957
7013
|
this.ringAnimation = ringAnimation;
|
|
@@ -6966,19 +7022,19 @@ var EntityLabel;
|
|
|
6966
7022
|
if (leader && angled) {
|
|
6967
7023
|
// The declutter owns the side; the fit only says which one had the room to begin with.
|
|
6968
7024
|
const run = Math.max(ANGLED_LEADER_RUN, radius + 4);
|
|
6969
|
-
const side = (
|
|
7025
|
+
const side = (_o = (_m = placement.textSide) !== null && _m !== void 0 ? _m : frame.leaderSide) !== null && _o !== void 0 ? _o : "right";
|
|
6970
7026
|
const beside = side === "left" ? frame.anchorX - run - width : frame.anchorX + run;
|
|
6971
7027
|
position.x = Math.max(0, Math.min(beside, this.viewer.canvas.clientWidth - width));
|
|
6972
7028
|
}
|
|
6973
7029
|
else if (leader && placement.textSide === "left") {
|
|
6974
7030
|
position.x = Math.max(0, Math.min(frame.anchorX - width, this.viewer.canvas.clientWidth - width));
|
|
6975
7031
|
}
|
|
6976
|
-
const gap = (
|
|
7032
|
+
const gap = (_q = (_p = this.options.pointerAvoidance) === null || _p === void 0 ? void 0 : _p.gap) !== null && _q !== void 0 ? _q : 12;
|
|
6977
7033
|
const containsPointer = (x, y, margin) => {
|
|
6978
7034
|
return pointer && pointer.x >= x - margin && pointer.x <= x + width + margin && pointer.y >= y - margin && pointer.y <= y + height + margin;
|
|
6979
7035
|
};
|
|
6980
7036
|
const passive = !selectOnClick || !this.visualRegister;
|
|
6981
|
-
if (passive && ((
|
|
7037
|
+
if (passive && ((_r = this.options.pointerAvoidance) === null || _r === void 0 ? void 0 : _r.enabled) !== false && pointer &&
|
|
6982
7038
|
(containsPointer(position.x, position.y, gap) || containsPointer(position.x + displacement.x, position.y + displacement.y, gap))) {
|
|
6983
7039
|
if (containsPointer(position.x + displacement.x, position.y + displacement.y, 0)) {
|
|
6984
7040
|
// Choose against the original box and retain the offset until the pointer leaves both boxes.
|
|
@@ -6996,7 +7052,7 @@ var EntityLabel;
|
|
|
6996
7052
|
else {
|
|
6997
7053
|
displacement = { x: 0, y: 0 };
|
|
6998
7054
|
}
|
|
6999
|
-
const nudgeDuration = (
|
|
7055
|
+
const nudgeDuration = (_t = (_s = this.options.pointerAvoidance) === null || _s === void 0 ? void 0 : _s.transitionDuration) !== null && _t !== void 0 ? _t : 200;
|
|
7000
7056
|
const animateNudge = () => {
|
|
7001
7057
|
const progress = now < nudge.start ? 0 : nudgeDuration > 0 ? Math.min(1, (now - nudge.start) / nudgeDuration) : 1;
|
|
7002
7058
|
const eased = progress * progress * (3 - 2 * progress);
|
|
@@ -7012,7 +7068,7 @@ var EntityLabel;
|
|
|
7012
7068
|
fromY: nudge.y,
|
|
7013
7069
|
targetX: displacement.x,
|
|
7014
7070
|
targetY: displacement.y,
|
|
7015
|
-
start: now + ((
|
|
7071
|
+
start: now + ((_v = (_u = this.options.pointerAvoidance) === null || _u === void 0 ? void 0 : _u.delay) !== null && _v !== void 0 ? _v : 180)
|
|
7016
7072
|
};
|
|
7017
7073
|
}
|
|
7018
7074
|
if (animateNudge() < 1 && (nudge.fromX !== nudge.targetX || nudge.fromY !== nudge.targetY)) {
|
|
@@ -7022,9 +7078,8 @@ var EntityLabel;
|
|
|
7022
7078
|
position.y += nudge.y;
|
|
7023
7079
|
ele.style.left = `${coordinates.x + position.x * coordinates.scaleX}px`;
|
|
7024
7080
|
ele.style.top = `${coordinates.y + position.y * coordinates.scaleY}px`;
|
|
7025
|
-
const clickTarget
|
|
7026
|
-
|
|
7027
|
-
clickTarget.setAttribute("aria-label", placement.members.length ? `Select ${placement.entityIds.length} entities` : `Select ${(_v = (_u = this.options.text) !== null && _u !== void 0 ? _u : text) !== null && _v !== void 0 ? _v : (_w = this.rego) === null || _w === void 0 ? void 0 : _w.entityId}`);
|
|
7081
|
+
for (const clickTarget of Array.from(content.querySelectorAll('[role="button"]'))) {
|
|
7082
|
+
clickTarget.setAttribute("aria-label", placement.members.length ? `Select ${placement.entityIds.length} entities` : `Select ${(_x = (_w = this.options.text) !== null && _w !== void 0 ? _w : text) !== null && _x !== void 0 ? _x : (_y = this.rego) === null || _y === void 0 ? void 0 : _y.entityId}`);
|
|
7028
7083
|
}
|
|
7029
7084
|
if (leader) {
|
|
7030
7085
|
const nodeRadius = ringAnimation.outer;
|
|
@@ -7064,13 +7119,13 @@ var EntityLabel;
|
|
|
7064
7119
|
// Keep the stem inside the 126-degree arc, away from antialiased endpoints.
|
|
7065
7120
|
const arcFraction = 0.35;
|
|
7066
7121
|
const rotationLimit = Math.PI * arcFraction - Math.PI / 30;
|
|
7067
|
-
const targetOffset = hovered ? Math.max(-rotationLimit, Math.min(rotationLimit, wrappedAngle)) : ((
|
|
7122
|
+
const targetOffset = hovered ? Math.max(-rotationLimit, Math.min(rotationLimit, wrappedAngle)) : ((_z = ringAnimation.arcOffset) !== null && _z !== void 0 ? _z : 0);
|
|
7068
7123
|
const targetOpen = cursorMotion.target || (hovered && (ring === null || ring === void 0 ? void 0 : ring.enabled) !== false && targetRadius > 0) ? 1 : 0;
|
|
7069
|
-
const elapsed = Math.min(32, Math.max(0, now - ((
|
|
7124
|
+
const elapsed = Math.min(32, Math.max(0, now - ((_0 = ringAnimation.arcTime) !== null && _0 !== void 0 ? _0 : now)));
|
|
7070
7125
|
const blend = duration > 0 ? 1 - Math.exp(-elapsed / (duration / 3)) : 1;
|
|
7071
7126
|
const approach = (value, target) => Math.abs(value - target) < 0.001 ? target : value + (target - value) * blend;
|
|
7072
|
-
ringAnimation.arcOffset = approach((
|
|
7073
|
-
ringAnimation.arcOpen = approach((
|
|
7127
|
+
ringAnimation.arcOffset = approach((_1 = ringAnimation.arcOffset) !== null && _1 !== void 0 ? _1 : 0, targetOffset);
|
|
7128
|
+
ringAnimation.arcOpen = approach((_2 = ringAnimation.arcOpen) !== null && _2 !== void 0 ? _2 : 0, targetOpen);
|
|
7074
7129
|
ringAnimation.arcTime = now;
|
|
7075
7130
|
if (ringAnimation.arcOffset !== targetOffset || ringAnimation.arcOpen !== targetOpen) {
|
|
7076
7131
|
this.viewer.scene.requestRender();
|
|
@@ -7123,6 +7178,123 @@ var EntityLabel;
|
|
|
7123
7178
|
}, 2500);
|
|
7124
7179
|
});
|
|
7125
7180
|
}
|
|
7181
|
+
/*
|
|
7182
|
+
* Builds one icon glyph from label options, with its background chip when asked for.
|
|
7183
|
+
*
|
|
7184
|
+
* The returned fit squares the chip off once the glyph has a measurable size.
|
|
7185
|
+
*/
|
|
7186
|
+
buildIcon(options, counter) {
|
|
7187
|
+
var _a, _b, _c;
|
|
7188
|
+
let fit;
|
|
7189
|
+
const icon = document.createElement("span");
|
|
7190
|
+
icon.className = "entity-label-icon";
|
|
7191
|
+
icon.style.cssText = `font-size:${(options.iconSize || 24) * ((_a = options.iconScale) !== null && _a !== void 0 ? _a : 1)}px;line-height:1;`;
|
|
7192
|
+
icon.style.textShadow = "0 1px 2px rgba(0, 0, 0, 0.25)";
|
|
7193
|
+
const iconColor = typeof options.iconColor === "string" ? options.iconColor : undefined;
|
|
7194
|
+
const iconBackground = options.iconBackground && options.iconScale !== 0;
|
|
7195
|
+
if (iconColor) {
|
|
7196
|
+
icon.style.color = iconColor;
|
|
7197
|
+
}
|
|
7198
|
+
if (options.iconUrl) {
|
|
7199
|
+
const image = document.createElement("img");
|
|
7200
|
+
image.draggable = false;
|
|
7201
|
+
image.alt = options.icon || "";
|
|
7202
|
+
const scale = (_b = options.iconScale) !== null && _b !== void 0 ? _b : 1;
|
|
7203
|
+
const size = options.iconSize;
|
|
7204
|
+
const svgNs = "http://www.w3.org/2000/svg";
|
|
7205
|
+
const tint = iconColor ? document.createElementNS(svgNs, "svg") : null;
|
|
7206
|
+
const painted = tint ? document.createElementNS(svgNs, "image") : null;
|
|
7207
|
+
const inked = tint ? document.createElementNS(svgNs, "rect") : null;
|
|
7208
|
+
if (tint) {
|
|
7209
|
+
const maskId = `entity-label-tint-${++tintCounter}`;
|
|
7210
|
+
const mask = document.createElementNS(svgNs, "mask");
|
|
7211
|
+
mask.setAttribute("id", maskId);
|
|
7212
|
+
mask.setAttribute("maskUnits", "userSpaceOnUse");
|
|
7213
|
+
painted.setAttribute("href", options.iconUrl);
|
|
7214
|
+
painted.setAttribute("preserveAspectRatio", "xMidYMid meet");
|
|
7215
|
+
mask.appendChild(painted);
|
|
7216
|
+
inked.setAttribute("fill", iconColor);
|
|
7217
|
+
inked.setAttribute("mask", `url(#${maskId})`);
|
|
7218
|
+
tint.append(mask, inked);
|
|
7219
|
+
tint.style.display = "block";
|
|
7220
|
+
icon.style.display = "inline-flex";
|
|
7221
|
+
icon.appendChild(tint);
|
|
7222
|
+
}
|
|
7223
|
+
const resize = () => {
|
|
7224
|
+
if (this.disposed || this.counter !== counter) {
|
|
7225
|
+
return;
|
|
7226
|
+
}
|
|
7227
|
+
const intrinsic = options.iconScale != null && size == null;
|
|
7228
|
+
const width = (intrinsic ? image.naturalWidth || 24 : size || 24) * scale;
|
|
7229
|
+
const height = (intrinsic ? image.naturalHeight || 24 : size || 24) * scale;
|
|
7230
|
+
if (tint) {
|
|
7231
|
+
tint.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
7232
|
+
for (const node of [tint, painted, inked]) {
|
|
7233
|
+
node.setAttribute("width", String(width));
|
|
7234
|
+
node.setAttribute("height", String(height));
|
|
7235
|
+
}
|
|
7236
|
+
}
|
|
7237
|
+
else {
|
|
7238
|
+
image.style.width = `${width}px`;
|
|
7239
|
+
image.style.height = `${height}px`;
|
|
7240
|
+
}
|
|
7241
|
+
this.viewer.scene.requestRender();
|
|
7242
|
+
};
|
|
7243
|
+
image.onload = resize;
|
|
7244
|
+
resize();
|
|
7245
|
+
image.src = options.iconUrl;
|
|
7246
|
+
if (!tint) {
|
|
7247
|
+
icon.appendChild(image);
|
|
7248
|
+
}
|
|
7249
|
+
}
|
|
7250
|
+
else if (typeof options.iconClass === "string" && options.iconClass) {
|
|
7251
|
+
icon.className = `entity-label-icon ${options.iconClass}`;
|
|
7252
|
+
}
|
|
7253
|
+
else {
|
|
7254
|
+
icon.textContent = options.icon || "●";
|
|
7255
|
+
}
|
|
7256
|
+
if (iconBackground) {
|
|
7257
|
+
const color = Color.fromCssColorString(iconColor || "white") || Color.WHITE;
|
|
7258
|
+
const luminance = 0.2126 * color.red + 0.7152 * color.green + 0.0722 * color.blue;
|
|
7259
|
+
const background = (luminance > 0.5 ? color.darken(ICON_BACKGROUND_BLEND, new Color()) :
|
|
7260
|
+
color.brighten(ICON_BACKGROUND_BLEND, new Color()));
|
|
7261
|
+
const size = (options.iconSize || 24) * ((_c = options.iconScale) !== null && _c !== void 0 ? _c : 1);
|
|
7262
|
+
icon.style.backgroundColor = background.withAlpha(ICON_BACKGROUND_ALPHA).toCssColorString();
|
|
7263
|
+
icon.style.borderRadius = "50%";
|
|
7264
|
+
icon.style.padding = `${size / 3}px`;
|
|
7265
|
+
icon.style.display = "inline-flex";
|
|
7266
|
+
icon.style.alignItems = "center";
|
|
7267
|
+
icon.style.justifyContent = "center";
|
|
7268
|
+
icon.style.minWidth = `${size}px`;
|
|
7269
|
+
icon.style.minHeight = `${size}px`;
|
|
7270
|
+
icon.style.boxSizing = "content-box";
|
|
7271
|
+
let fitted = false;
|
|
7272
|
+
fit = () => {
|
|
7273
|
+
if (fitted) {
|
|
7274
|
+
return;
|
|
7275
|
+
}
|
|
7276
|
+
const padding = size / 3;
|
|
7277
|
+
const width = icon.offsetWidth - padding * 2;
|
|
7278
|
+
const height = icon.offsetHeight - padding * 2;
|
|
7279
|
+
if (width <= 0 || height <= 0) {
|
|
7280
|
+
return;
|
|
7281
|
+
}
|
|
7282
|
+
fitted = true;
|
|
7283
|
+
const longest = Math.max(width, height);
|
|
7284
|
+
const difference = (longest - Math.min(width, height)) / longest;
|
|
7285
|
+
if (difference > ICON_BACKGROUND_UNIFORM_TOLERANCE) {
|
|
7286
|
+
return;
|
|
7287
|
+
}
|
|
7288
|
+
icon.style.minWidth = `${longest}px`;
|
|
7289
|
+
icon.style.minHeight = `${longest}px`;
|
|
7290
|
+
this.viewer.scene.requestRender();
|
|
7291
|
+
};
|
|
7292
|
+
}
|
|
7293
|
+
if (options.iconScale === 0) {
|
|
7294
|
+
icon.style.display = "none";
|
|
7295
|
+
}
|
|
7296
|
+
return { icon, fit };
|
|
7297
|
+
}
|
|
7126
7298
|
/**
|
|
7127
7299
|
* Removes label from the viewer and unsubscribes from events.
|
|
7128
7300
|
*/
|
|
@@ -50400,7 +50572,7 @@ var StyleUtils;
|
|
|
50400
50572
|
StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
|
|
50401
50573
|
})(StyleUtils || (StyleUtils = {}));
|
|
50402
50574
|
|
|
50403
|
-
const VERSION = "7.4.
|
|
50575
|
+
const VERSION = "7.4.4";
|
|
50404
50576
|
/**
|
|
50405
50577
|
* Updates the environment instance used by bruce-cesium to one specified.
|
|
50406
50578
|
* This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
|