@terraware/web-components 5.0.26 → 5.0.27-rc.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/components/AntSwitch/index.js +19 -22
- package/components/Autocomplete/Autocomplete.js +106 -130
- package/components/Badge/index.js +28 -29
- package/components/BusySpinner/index.js +40 -45
- package/components/Button/Button.js +41 -47
- package/components/Checkbox.js +64 -63
- package/components/Confirm/index.js +74 -83
- package/components/DatePicker/DatePicker.js +138 -141
- package/components/DialogBox/DialogBox.js +111 -118
- package/components/Divisor.js +18 -18
- package/components/Dropdown.js +66 -94
- package/components/EditableTable/index.js +211 -271
- package/components/ErrorBox/ErrorBox.js +47 -49
- package/components/FileChooser/index.js +117 -135
- package/components/FormBottomBar/index.js +75 -84
- package/components/Icon/Icon.js +12 -20
- package/components/Icon/icons/index.js +256 -256
- package/components/IconTooltip/index.js +49 -60
- package/components/Map/MapBox.js +545 -452
- package/components/Map/MapContainer.js +25 -29
- package/components/Map/MapDateSliderControl.js +64 -64
- package/components/Map/MapDrawer.js +41 -47
- package/components/Map/MapLegend.js +176 -172
- package/components/Map/MapViewStyleControl.js +59 -63
- package/components/Map/ReactMapboxWrapper.js +3 -2
- package/components/Map/index.js +161 -180
- package/components/Map/types.js +12 -17
- package/components/Map/useMaintainLayerOrder.js +16 -26
- package/components/Markdown/index.js +25 -26
- package/components/Message/Message.js +73 -76
- package/components/MultiSelect/index.js +112 -124
- package/components/Navbar/NavFooter.js +9 -13
- package/components/Navbar/NavItem.js +70 -76
- package/components/Navbar/NavSection.js +17 -19
- package/components/Navbar/Navbar.js +39 -46
- package/components/Navbar/SubNavbar.js +9 -13
- package/components/Note.js +18 -22
- package/components/OverlayModal/OverlayModal.js +56 -63
- package/components/PageForm/index.js +33 -41
- package/components/PhotoChooser/index.js +165 -173
- package/components/PhotosCarousel/PhotoSlide.js +32 -35
- package/components/PhotosCarousel/index.js +80 -85
- package/components/PhotosCarousel/usePhotosCarousel.js +69 -76
- package/components/Pill/index.js +41 -45
- package/components/PillList/index.js +17 -24
- package/components/PlacementWrapper/index.js +38 -59
- package/components/PopoverMenu/Popover.js +68 -85
- package/components/PopoverMenu/index.js +51 -56
- package/components/PopoverMultiSelect/index.js +52 -59
- package/components/ProgressCircle/ProgressCircle.js +28 -32
- package/components/RadioButton.js +17 -19
- package/components/Select/Select.js +13 -15
- package/components/Select/SelectT.js +235 -250
- package/components/Separator/index.js +14 -17
- package/components/Size.js +0 -1
- package/components/Slider/index.js +43 -56
- package/components/SummaryBox.js +70 -72
- package/components/Tabs/index.js +133 -135
- package/components/TextTruncated/index.js +117 -130
- package/components/Textfield/Textfield.js +158 -190
- package/components/Textfield/TruncatedTextArea.js +70 -84
- package/components/TimelineSlider/TimelineMarker.js +25 -30
- package/components/TimelineSlider/TimelineRail.js +8 -11
- package/components/TimelineSlider/index.js +64 -66
- package/components/TimelineSliderV2/ClusterBadge.js +25 -29
- package/components/TimelineSliderV2/TimelineDot.js +31 -35
- package/components/TimelineSliderV2/TimelineRail.js +8 -11
- package/components/TimelineSliderV2/index.js +121 -103
- package/components/TimelineSliderV2/layout.js +124 -147
- package/components/TimelineSliderV2/types.js +0 -1
- package/components/Tooltip/Tooltip.js +47 -56
- package/components/ViewPhotosDialog/index.js +28 -35
- package/components/VirtualWalkthrough/Annotation.js +163 -207
- package/components/VirtualWalkthrough/AnnotationEditPane.js +198 -184
- package/components/VirtualWalkthrough/AnnotationPanel.js +187 -199
- package/components/VirtualWalkthrough/Application.js +31 -34
- package/components/VirtualWalkthrough/AutoRotator.js +85 -223
- package/components/VirtualWalkthrough/BlockingSpinner.js +26 -28
- package/components/VirtualWalkthrough/BoundaryRing.js +30 -34
- package/components/VirtualWalkthrough/BoundaryWall.js +32 -36
- package/components/VirtualWalkthrough/CameraInfo.js +100 -89
- package/components/VirtualWalkthrough/ControlsInfoPane.js +254 -195
- package/components/VirtualWalkthrough/GradientSky.js +72 -73
- package/components/VirtualWalkthrough/SplatControls.js +209 -221
- package/components/VirtualWalkthrough/SplatCrop.js +21 -20
- package/components/VirtualWalkthrough/SplatFadeCrop.js +21 -20
- package/components/VirtualWalkthrough/SplatModel.js +65 -85
- package/components/VirtualWalkthrough/SplatRevealRain.js +41 -46
- package/components/VirtualWalkthrough/TfAnnotationManager.js +197 -379
- package/components/VirtualWalkthrough/TfXrNavigation.js +73 -162
- package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +438 -431
- package/components/VirtualWalkthrough/VrAnnotationPanel.js +18 -22
- package/components/VirtualWalkthrough/XrAnnotationInteraction.js +14 -17
- package/components/VirtualWalkthrough/XrExitButton.js +14 -17
- package/components/VirtualWalkthrough/XrGazeDwell.js +14 -17
- package/components/VirtualWalkthrough/XrPointerRay.js +14 -17
- package/components/VirtualWalkthrough/XrStartPose.js +67 -121
- package/components/VirtualWalkthrough/boundary-ring.js +87 -117
- package/components/VirtualWalkthrough/boundary-wall.js +175 -258
- package/components/VirtualWalkthrough/camera-adoption.js +18 -64
- package/components/VirtualWalkthrough/groundPlane.js +32 -47
- package/components/VirtualWalkthrough/gsplat-fade-crop-effect.js +35 -35
- package/components/VirtualWalkthrough/splatFormat.js +13 -47
- package/components/VirtualWalkthrough/useAdoptedCamera.js +23 -44
- package/components/VirtualWalkthrough/useRestartOnVr.js +26 -37
- package/components/VirtualWalkthrough/useXrRenderTuning.js +33 -54
- package/components/VirtualWalkthrough/vr-annotation-panel-layout.js +67 -99
- package/components/VirtualWalkthrough/vr-annotation-panel.js +403 -433
- package/components/VirtualWalkthrough/vr-annotation-scroll.js +10 -28
- package/components/VirtualWalkthrough/walkthrough-camera.js +204 -329
- package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.js +39 -80
- package/components/VirtualWalkthrough/xr-annotation-candidates.js +6 -18
- package/components/VirtualWalkthrough/xr-annotation-interaction.js +31 -56
- package/components/VirtualWalkthrough/xr-annotation-targeting.js +22 -38
- package/components/VirtualWalkthrough/xr-button-arming.js +7 -17
- package/components/VirtualWalkthrough/xr-button-hold.js +22 -36
- package/components/VirtualWalkthrough/xr-exit-button-mask.js +22 -36
- package/components/VirtualWalkthrough/xr-exit-button.js +148 -207
- package/components/VirtualWalkthrough/xr-face-buttons.js +19 -29
- package/components/VirtualWalkthrough/xr-gaze-dwell-state.js +23 -34
- package/components/VirtualWalkthrough/xr-gaze-dwell.js +107 -144
- package/components/VirtualWalkthrough/xr-interactive-ui.js +12 -24
- package/components/VirtualWalkthrough/xr-pointer-ray.js +13 -20
- package/components/VirtualWalkthrough/xr-progress-pie-material.js +117 -88
- package/components/VirtualWalkthrough/xr-progress-pie.js +1 -10
- package/components/VirtualWalkthrough/xr-ray-quad.js +16 -30
- package/components/VirtualWalkthrough/xr-render-tuning.js +17 -48
- package/components/VirtualWalkthrough/xr-scene-bounds.js +21 -37
- package/components/VirtualWalkthrough/xr-start-pose.js +27 -73
- package/components/VirtualWalkthrough/xr-teleport-gesture.js +16 -42
- package/components/svg/BlobbyGrayIconImage.js +41 -39
- package/components/svg/BlobbyGrayIconImport.js +41 -39
- package/components/svg/BlobbyGrayIconUploadToTheCloud.js +41 -39
- package/components/svg/BlobbyIconCloudChecked.js +202 -164
- package/components/svg/BlobbyIconExportOrUploadPhoto.js +202 -164
- package/components/svg/BlobbyIconFolder.js +202 -164
- package/components/svg/BlobbyIconGraphReport.js +202 -164
- package/components/svg/BlobbyIconHappy.js +202 -164
- package/components/svg/BlobbyIconHeartMonitor.js +202 -164
- package/components/svg/BlobbyIconLeaf.js +202 -164
- package/components/svg/BlobbyIconLibrary.js +202 -164
- package/components/svg/BlobbyIconNursery.js +204 -166
- package/components/svg/BlobbyIconOrganization.js +202 -164
- package/components/svg/BlobbyIconParchment.js +202 -164
- package/components/svg/BlobbyIconPeople.js +202 -164
- package/components/svg/BlobbyIconSeedBank.js +202 -164
- package/components/svg/BlobbyIconSeedling.js +202 -164
- package/components/svg/BlobbyIconSeeds.js +202 -164
- package/components/svg/BlobbyIconSite.js +206 -167
- package/components/svg/BlobbyIconSparkles.js +202 -164
- package/components/svg/BlobbyIconWrench.js +202 -164
- package/components/svg/Bug.js +19 -21
- package/components/svg/Calendar.js +19 -21
- package/components/svg/CaretDown.js +19 -21
- package/components/svg/CaretLeft.js +19 -21
- package/components/svg/CaretRight.js +19 -21
- package/components/svg/CaretUp.js +19 -21
- package/components/svg/ChevronDown.js +19 -21
- package/components/svg/ChevronUp.js +19 -21
- package/components/svg/Close.js +19 -21
- package/components/svg/Critical.js +19 -21
- package/components/svg/Edit.js +202 -164
- package/components/svg/Error.js +19 -21
- package/components/svg/Filter.js +19 -21
- package/components/svg/Folder.js +19 -21
- package/components/svg/Help.js +19 -21
- package/components/svg/Home.js +19 -21
- package/components/svg/IconAdd.js +19 -21
- package/components/svg/IconArrowRight.js +21 -23
- package/components/svg/IconBusinessNetwork.js +19 -21
- package/components/svg/IconCalendar.js +19 -21
- package/components/svg/IconCancel.js +19 -21
- package/components/svg/IconChargingBattery.js +19 -21
- package/components/svg/IconChecklist.js +21 -23
- package/components/svg/IconCheckmark.js +19 -21
- package/components/svg/IconCoinInHand.js +19 -21
- package/components/svg/IconColumns.js +19 -21
- package/components/svg/IconComment.js +20 -22
- package/components/svg/IconDashboard.js +19 -21
- package/components/svg/IconDataMigration.js +19 -21
- package/components/svg/IconDocument.js +21 -23
- package/components/svg/IconDownloadFromTheCloud.js +21 -23
- package/components/svg/IconEdit.js +21 -23
- package/components/svg/IconExpand.js +21 -23
- package/components/svg/IconExport.js +19 -21
- package/components/svg/IconExternalLink.js +19 -21
- package/components/svg/IconEye.js +19 -21
- package/components/svg/IconEyeOff.js +19 -21
- package/components/svg/IconFile.js +19 -21
- package/components/svg/IconFolder.js +19 -21
- package/components/svg/IconFullScreen.js +20 -22
- package/components/svg/IconFutures.js +19 -21
- package/components/svg/IconGraphReport.js +19 -21
- package/components/svg/IconGrid.js +19 -21
- package/components/svg/IconHeartMonitor.js +19 -21
- package/components/svg/IconHelp.js +21 -23
- package/components/svg/IconHistory.js +19 -21
- package/components/svg/IconImport.js +19 -21
- package/components/svg/IconIndex.js +19 -21
- package/components/svg/IconLayers.js +19 -21
- package/components/svg/IconLibrary.js +20 -22
- package/components/svg/IconList.js +19 -21
- package/components/svg/IconLivePlant.js +19 -21
- package/components/svg/IconLocations.js +19 -21
- package/components/svg/IconMail.js +19 -21
- package/components/svg/IconManager.js +19 -21
- package/components/svg/IconMap.js +19 -21
- package/components/svg/IconMarker.js +19 -21
- package/components/svg/IconMenu.js +19 -21
- package/components/svg/IconMenuHorizontal.js +19 -21
- package/components/svg/IconModule.js +19 -21
- package/components/svg/IconMyLocation.js +19 -21
- package/components/svg/IconNote.js +21 -23
- package/components/svg/IconNursery.js +21 -23
- package/components/svg/IconOrg.js +19 -21
- package/components/svg/IconParchment.js +19 -21
- package/components/svg/IconPhoto.js +19 -21
- package/components/svg/IconPlantsFilled.js +19 -21
- package/components/svg/IconRedo.js +19 -21
- package/components/svg/IconRestorationSite.js +19 -21
- package/components/svg/IconSeedBank.js +19 -21
- package/components/svg/IconSeedling.js +19 -21
- package/components/svg/IconSettings.js +19 -21
- package/components/svg/IconSlice.js +19 -21
- package/components/svg/IconSubmit.js +19 -21
- package/components/svg/IconSubtract.js +21 -23
- package/components/svg/IconSynced.js +19 -21
- package/components/svg/IconTrashCan.js +19 -21
- package/components/svg/IconTreasureMap.js +19 -21
- package/components/svg/IconUnavailable.js +19 -21
- package/components/svg/IconUndo.js +19 -21
- package/components/svg/IconVariable.js +19 -21
- package/components/svg/IconVideo.js +30 -31
- package/components/svg/IconWifi.js +19 -21
- package/components/svg/Info.js +19 -21
- package/components/svg/Key.js +19 -21
- package/components/svg/Leaf.js +19 -21
- package/components/svg/Lock.js +19 -21
- package/components/svg/Logo.js +48 -43
- package/components/svg/MenuVertical.js +19 -21
- package/components/svg/Notification.js +19 -21
- package/components/svg/Person.js +19 -21
- package/components/svg/Plus.js +19 -21
- package/components/svg/RestorationSite.js +19 -21
- package/components/svg/Search.js +19 -21
- package/components/svg/Seeds.js +19 -21
- package/components/svg/Site.js +19 -21
- package/components/svg/Sparkles.js +19 -21
- package/components/svg/Species.js +19 -21
- package/components/svg/Species2.js +202 -164
- package/components/svg/Spinner.js +54 -48
- package/components/svg/Star.js +19 -21
- package/components/svg/Success.js +19 -21
- package/components/svg/SuccessFilled.js +19 -21
- package/components/svg/TerrawareLogoDesktop.js +55 -49
- package/components/svg/TerrawareLogoMobile.js +25 -26
- package/components/svg/Touchscreen.js +19 -21
- package/components/svg/UploadCloud.js +202 -164
- package/components/svg/Warning.js +19 -21
- package/components/svg/WelcomeClipboard.js +156 -133
- package/components/svg/index.js +129 -129
- package/components/table/EnhancedTableToolbar.js +68 -74
- package/components/table/EnhancedTableToolbarV2.js +31 -41
- package/components/table/TableCellRenderer.js +196 -231
- package/components/table/TableHeader.js +71 -73
- package/components/table/TableHeaderItem.js +97 -102
- package/components/table/density.js +18 -19
- package/components/table/index.js +350 -369
- package/components/table/sort.js +27 -52
- package/components/table/types.js +0 -1
- package/components/types/index.js +0 -1
- package/hooks/cameraEntityContext.js +2 -13
- package/hooks/useBoolean.js +17 -13
- package/hooks/useCameraPosition.js +56 -56
- package/hooks/useDevicePerformance.js +17 -17
- package/hooks/useXr.js +85 -87
- package/index.js +58 -58
- package/package.json +1 -1
- package/setupTests.js +0 -4
- package/strings/export.js +111 -170
- package/strings/index.js +25 -54
- package/strings/locales.js +21 -41
- package/strings/strings-en.js +49 -51
- package/strings/strings-es.js +49 -51
- package/strings/strings-fr.js +49 -51
- package/strings/strings-gx.js +49 -51
- package/style-dictionary-dist/TerrawareTheme.js +186 -186
- package/theme.js +74 -76
- package/utils/color.js +8 -15
- package/utils/date.js +42 -108
- package/utils/index.js +4 -4
- package/utils/preventDefaultEvent.js +3 -3
- package/utils/text.js +11 -11
- package/utils/useDeviceInfo.js +42 -79
- package/virtualWalkthrough.js +23 -23
|
@@ -1,400 +1,218 @@
|
|
|
1
|
-
import { createElement } from
|
|
2
|
-
import { renderToStaticMarkup } from
|
|
3
|
-
import {
|
|
4
|
-
import { AnnotationManager } from
|
|
5
|
-
import Icon from
|
|
6
|
-
|
|
7
|
-
// Maps each annotation icon type to a @terraware/web-components icon.
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
3
|
+
import { FILTER_LINEAR, PIXELFORMAT_RGBA8, Texture } from "playcanvas";
|
|
4
|
+
import { AnnotationManager } from "playcanvas/scripts/esm/annotations.mjs";
|
|
5
|
+
import Icon from "../Icon/Icon.js";
|
|
8
6
|
const ANNOTATION_ICONS = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
text: 'iconComment',
|
|
8
|
+
image: 'iconPhoto',
|
|
9
|
+
video: 'iconVideo'
|
|
12
10
|
};
|
|
13
|
-
|
|
14
|
-
// Pixel dimensions of the (square) hotspot texture canvas at rest.
|
|
15
11
|
const HOTSPOT_TEXTURE_SIZE = 64;
|
|
16
|
-
|
|
17
|
-
// How much larger the hotspot appears while hovered.
|
|
18
12
|
const HOTSPOT_HOVER_SCALE = 1.3;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Extended AnnotationManager that adds max size clamping for annotations
|
|
22
|
-
* and fixes positioning when canvas is not at document origin.
|
|
23
|
-
*/
|
|
24
13
|
class TfAnnotationManager extends AnnotationManager {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* Maximum world-space size for annotations.
|
|
34
|
-
* This prevents annotations from growing beyond this size regardless of camera distance.
|
|
35
|
-
*
|
|
36
|
-
* @attribute
|
|
37
|
-
* @title Max World Size
|
|
38
|
-
* @type {number}
|
|
39
|
-
* @default 1.0
|
|
40
|
-
*/
|
|
41
|
-
set maxWorldSize(value) {
|
|
42
|
-
this._maxWorldSize = value;
|
|
43
|
-
}
|
|
44
|
-
get maxWorldSize() {
|
|
45
|
-
return this._maxWorldSize;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Background color for the hotspot texture canvas (the circle fill color).
|
|
50
|
-
* This is a hex color string (e.g., '#2C8658') that's drawn on the texture canvas.
|
|
51
|
-
* Note: This is different from hotspotColor and hoverColor, which are PlayCanvas Color objects
|
|
52
|
-
* used for the material's emissive property.
|
|
53
|
-
*
|
|
54
|
-
* @attribute
|
|
55
|
-
* @title Hotspot Background Color
|
|
56
|
-
* @type {string}
|
|
57
|
-
* @default '#2C8658'
|
|
58
|
-
*/
|
|
59
|
-
set hotspotBackgroundColor(value) {
|
|
60
|
-
this._hotspotBackgroundColor = value;
|
|
61
|
-
}
|
|
62
|
-
get hotspotBackgroundColor() {
|
|
63
|
-
return this._hotspotBackgroundColor;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Initialize with custom parent DOM if needed.
|
|
68
|
-
*/
|
|
69
|
-
initialize() {
|
|
70
|
-
// Find a container div with data-annotation-container attribute
|
|
71
|
-
const canvas = this.app.graphicsDevice.canvas;
|
|
72
|
-
const container = canvas.parentElement?.querySelector('[data-annotation-container]');
|
|
73
|
-
if (container) {
|
|
74
|
-
this._customParentDom = container;
|
|
75
|
-
this._parentDom = container;
|
|
14
|
+
static scriptName = 'tfAnnotationManager';
|
|
15
|
+
_maxWorldSize = 1.0;
|
|
16
|
+
_customParentDom = null;
|
|
17
|
+
_clickHandlersAttached = new Set();
|
|
18
|
+
_hotspotBackgroundColor = '#2C8658';
|
|
19
|
+
_iconImages = new Map();
|
|
20
|
+
set maxWorldSize(value) {
|
|
21
|
+
this._maxWorldSize = value;
|
|
76
22
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
super.initialize();
|
|
80
|
-
this._loadIconImages();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Renders each web-components icon to an SVG image once. When an image finishes
|
|
85
|
-
* loading, any annotations already using that icon are redrawn.
|
|
86
|
-
* @private
|
|
87
|
-
*/
|
|
88
|
-
_loadIconImages() {
|
|
89
|
-
const fillColor = this._hotspotColor?.toString() ?? '#ffffff';
|
|
90
|
-
Object.keys(ANNOTATION_ICONS).forEach(iconType => {
|
|
91
|
-
const name = ANNOTATION_ICONS[iconType];
|
|
92
|
-
const markup = renderToStaticMarkup(/*#__PURE__*/createElement(Icon, {
|
|
93
|
-
name,
|
|
94
|
-
fillColor
|
|
95
|
-
})).replace(/^<svg/, '<svg width="128" height="128"');
|
|
96
|
-
const image = new Image();
|
|
97
|
-
image.onload = () => this._refreshIcon(iconType);
|
|
98
|
-
image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`;
|
|
99
|
-
this._iconImages.set(iconType, image);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Redraws the texture for all annotations using the given icon, once its image
|
|
105
|
-
* has loaded.
|
|
106
|
-
* @private
|
|
107
|
-
*/
|
|
108
|
-
_refreshIcon(iconType) {
|
|
109
|
-
const annotationResources = this._annotationResources;
|
|
110
|
-
if (!annotationResources) {
|
|
111
|
-
return;
|
|
23
|
+
get maxWorldSize() {
|
|
24
|
+
return this._maxWorldSize;
|
|
112
25
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Update loop to attach click handlers to annotations and manage visibility.
|
|
122
|
-
*/
|
|
123
|
-
update() {
|
|
124
|
-
const annotationResources = this._annotationResources;
|
|
125
|
-
if (!annotationResources) {
|
|
126
|
-
return;
|
|
26
|
+
set hotspotBackgroundColor(value) {
|
|
27
|
+
this._hotspotBackgroundColor = value;
|
|
28
|
+
}
|
|
29
|
+
get hotspotBackgroundColor() {
|
|
30
|
+
return this._hotspotBackgroundColor;
|
|
127
31
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const callback = annotation.onClickCallback;
|
|
135
|
-
if (callback) {
|
|
136
|
-
this._clickHandlersAttached.add(annotation);
|
|
137
|
-
resources.hotspotDom.addEventListener('pointerdown', e => {
|
|
138
|
-
callback(e.clientX, e.clientY);
|
|
139
|
-
});
|
|
140
|
-
}
|
|
32
|
+
initialize() {
|
|
33
|
+
const canvas = this.app.graphicsDevice.canvas;
|
|
34
|
+
const container = canvas.parentElement?.querySelector('[data-annotation-container]');
|
|
35
|
+
if (container) {
|
|
36
|
+
this._customParentDom = container;
|
|
37
|
+
this._parentDom = container;
|
|
141
38
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
39
|
+
super.initialize();
|
|
40
|
+
this._loadIconImages();
|
|
41
|
+
}
|
|
42
|
+
_loadIconImages() {
|
|
43
|
+
const fillColor = this._hotspotColor?.toString() ?? '#ffffff';
|
|
44
|
+
Object.keys(ANNOTATION_ICONS).forEach((iconType)=>{
|
|
45
|
+
const name = ANNOTATION_ICONS[iconType];
|
|
46
|
+
const markup = renderToStaticMarkup(createElement(Icon, {
|
|
47
|
+
name,
|
|
48
|
+
fillColor
|
|
49
|
+
})).replace(/^<svg/, '<svg width="128" height="128"');
|
|
50
|
+
const image = new Image();
|
|
51
|
+
image.onload = ()=>this._refreshIcon(iconType);
|
|
52
|
+
image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`;
|
|
53
|
+
this._iconImages.set(iconType, image);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
_refreshIcon(iconType) {
|
|
57
|
+
const annotationResources = this._annotationResources;
|
|
58
|
+
if (!annotationResources) return;
|
|
59
|
+
annotationResources.forEach((_resources, annotation)=>{
|
|
60
|
+
if ((annotation.icon ?? 'text') === iconType) this._applyAnnotationIcon(annotation);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
update() {
|
|
64
|
+
const annotationResources = this._annotationResources;
|
|
65
|
+
if (!annotationResources) return;
|
|
66
|
+
annotationResources.forEach((resources, annotation)=>{
|
|
67
|
+
if (resources && resources.hotspotDom) {
|
|
68
|
+
if (!this._clickHandlersAttached.has(annotation)) {
|
|
69
|
+
const callback = annotation.onClickCallback;
|
|
70
|
+
if (callback) {
|
|
71
|
+
this._clickHandlersAttached.add(annotation);
|
|
72
|
+
resources.hotspotDom.addEventListener('pointerdown', (e)=>{
|
|
73
|
+
callback(e.clientX, e.clientY);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const isVisible = void 0 !== annotation.enabled ? annotation.enabled : true;
|
|
78
|
+
if (this._activeAnnotation === annotation && !isVisible) {
|
|
79
|
+
this._tooltipDom.style.visibility = 'hidden';
|
|
80
|
+
this._tooltipDom.style.opacity = '0';
|
|
81
|
+
this._activeAnnotation = null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
_registerAnnotation(annotation) {
|
|
87
|
+
super._registerAnnotation(annotation);
|
|
88
|
+
this._applyAnnotationIcon(annotation);
|
|
89
|
+
const resources = this._annotationResources.get(annotation);
|
|
90
|
+
if (resources) {
|
|
91
|
+
resources.baseEntity.enabled = false;
|
|
92
|
+
resources.overlayEntity.enabled = false;
|
|
151
93
|
}
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Override annotation registration to draw the media icon onto the hotspot
|
|
158
|
-
* texture once the annotation (and its icon) is available.
|
|
159
|
-
* @private
|
|
160
|
-
*/
|
|
161
|
-
_registerAnnotation(annotation) {
|
|
162
|
-
super._registerAnnotation(annotation);
|
|
163
|
-
this._applyAnnotationIcon(annotation);
|
|
164
|
-
|
|
165
|
-
// Keep the hotspot mesh hidden until the first scale pass sizes it. The annotation entity
|
|
166
|
-
// starts at unit scale, so the plane would render a metre across for the frames before
|
|
167
|
-
// _updateAnnotationRotationAndScale runs.
|
|
168
|
-
const resources = this._annotationResources.get(annotation);
|
|
169
|
-
if (resources) {
|
|
170
|
-
resources.baseEntity.enabled = false;
|
|
171
|
-
resources.overlayEntity.enabled = false;
|
|
172
94
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
95
|
+
_applyAnnotationIcon(annotation) {
|
|
96
|
+
const resources = this._annotationResources.get(annotation);
|
|
97
|
+
if (!resources) return;
|
|
98
|
+
const icon = annotation.icon ?? 'text';
|
|
99
|
+
resources.texture.destroy();
|
|
100
|
+
resources.texture = this._createHotspotTexture(annotation.label, HOTSPOT_TEXTURE_SIZE, 6, icon);
|
|
101
|
+
resources.materials.forEach((material)=>{
|
|
102
|
+
material.emissiveMap = resources.texture;
|
|
103
|
+
material.opacityMap = resources.texture;
|
|
104
|
+
material.update();
|
|
105
|
+
});
|
|
183
106
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
material.update();
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Draws the icon image onto the hotspot texture canvas, scaled to fit the
|
|
196
|
-
* circle. Does nothing if the image has not finished loading yet; the
|
|
197
|
-
* annotation is redrawn once it has (see `_refreshIcon`).
|
|
198
|
-
* @private
|
|
199
|
-
*/
|
|
200
|
-
_drawIcon(ctx, icon, size) {
|
|
201
|
-
const image = this._iconImages.get(icon);
|
|
202
|
-
if (!image || !image.complete || image.naturalWidth === 0) {
|
|
203
|
-
return;
|
|
107
|
+
_drawIcon(ctx, icon, size) {
|
|
108
|
+
const image = this._iconImages.get(icon);
|
|
109
|
+
if (!image || !image.complete || 0 === image.naturalWidth) return;
|
|
110
|
+
const iconSize = 0.5 * size;
|
|
111
|
+
const offset = (size - iconSize) / 2;
|
|
112
|
+
ctx.drawImage(image, offset, offset, iconSize, iconSize);
|
|
204
113
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
ctx.drawImage(image, offset, offset, iconSize, iconSize);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Keep the media icon drawn on the hotspot texture. The label text itself
|
|
212
|
-
* is not rendered on the hotspot, so we simply redraw with the icon.
|
|
213
|
-
* @private
|
|
214
|
-
*/
|
|
215
|
-
_onLabelChange(annotation) {
|
|
216
|
-
this._applyAnnotationIcon(annotation);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* The hotspot colors are baked into the texture in `_createHotspotTexture`,
|
|
221
|
-
* so the material emissive is kept white to let those colors render as drawn.
|
|
222
|
-
* @private
|
|
223
|
-
*/
|
|
224
|
-
_createHotspotMaterial(texture, options) {
|
|
225
|
-
const material = super._createHotspotMaterial(texture, options);
|
|
226
|
-
material.emissive.set(1, 1, 1);
|
|
227
|
-
material.update();
|
|
228
|
-
return material;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
232
|
-
_setAnnotationHover(_annotation, _hover) {
|
|
233
|
-
// No-op: the hover growth is handled in _updateAnnotationRotationAndScale
|
|
234
|
-
// (via _hoverAnnotation); the texture is identical in both states.
|
|
235
|
-
}
|
|
236
|
-
_updateAllAnnotationColors() {
|
|
237
|
-
// No-op: hotspot colors are baked into the texture (see _createHotspotMaterial).
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
241
|
-
_showTooltip(_annotation) {
|
|
242
|
-
// No-op: AnnotationPanel handles all annotation UI, this removes default behavior
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
246
|
-
_onTextChange(_annotation, _text) {
|
|
247
|
-
// No-op: AnnotationPanel handles all annotation UI, this removes default behavior
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Override position update to adjust for canvas offset and respect visibility.
|
|
252
|
-
* @private
|
|
253
|
-
*/
|
|
254
|
-
_updateAnnotationPositions(annotation, resources, screenPos) {
|
|
255
|
-
const canvas = this.app.graphicsDevice.canvas;
|
|
256
|
-
const rect = canvas.getBoundingClientRect();
|
|
257
|
-
|
|
258
|
-
// If using a custom parent (positioned container), use raw screen coordinates
|
|
259
|
-
// Otherwise, adjust for canvas position in document
|
|
260
|
-
const offsetX = this._customParentDom ? 0 : rect.left + window.scrollX;
|
|
261
|
-
const offsetY = this._customParentDom ? 0 : rect.top + window.scrollY;
|
|
262
|
-
|
|
263
|
-
// Check if annotation is visible (defaults to true if not specified)
|
|
264
|
-
const isVisible = annotation.enabled !== undefined ? annotation.enabled : true;
|
|
265
|
-
resources.hotspotDom.style.display = isVisible ? 'block' : 'none';
|
|
266
|
-
resources.hotspotDom.style.left = `${screenPos.x + offsetX}px`;
|
|
267
|
-
resources.hotspotDom.style.top = `${screenPos.y + offsetY}px`;
|
|
268
|
-
if (this._activeAnnotation === annotation) {
|
|
269
|
-
this._tooltipDom.style.display = isVisible ? 'block' : 'none';
|
|
270
|
-
this._tooltipDom.style.left = `${screenPos.x + offsetX}px`;
|
|
271
|
-
this._tooltipDom.style.top = `${screenPos.y + offsetY}px`;
|
|
114
|
+
_onLabelChange(annotation) {
|
|
115
|
+
this._applyAnnotationIcon(annotation);
|
|
272
116
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
117
|
+
_createHotspotMaterial(texture, options) {
|
|
118
|
+
const material = super._createHotspotMaterial(texture, options);
|
|
119
|
+
material.emissive.set(1, 1, 1);
|
|
120
|
+
material.update();
|
|
121
|
+
return material;
|
|
278
122
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
annotation.entity.rotateLocal(90, 0, 0);
|
|
302
|
-
const cameraPos = this._camera.getPosition();
|
|
303
|
-
const distance = annotation.entity.getPosition().distance(cameraPos);
|
|
304
|
-
const canvas = this.app.graphicsDevice.canvas;
|
|
305
|
-
const screenHeight = canvas.clientHeight;
|
|
306
|
-
const projMatrix = this._camera.camera.projectionMatrix;
|
|
307
|
-
|
|
308
|
-
// The world size that projects to _hotspotSize screen pixels at this distance.
|
|
309
|
-
const targetWorldSize = this._hotspotSize / screenHeight * (2 * distance / projMatrix.data[5]);
|
|
310
|
-
|
|
311
|
-
// Annotations hang off an unscaled parent, so local scale is world scale and maxWorldSize
|
|
312
|
-
// clamps the hotspot at a size in metres.
|
|
313
|
-
const clampedScale = Math.min(targetWorldSize, this._maxWorldSize);
|
|
314
|
-
const hovered = this._hoverAnnotation === annotation;
|
|
315
|
-
const hoverScale = hovered ? HOTSPOT_HOVER_SCALE : 1;
|
|
316
|
-
const entityScale = clampedScale * hoverScale;
|
|
317
|
-
annotation.entity.setLocalScale(entityScale, entityScale, entityScale);
|
|
318
|
-
|
|
319
|
-
// Scale the hotspot DOM element proportionally when clamped, including the
|
|
320
|
-
// hover growth so it tracks the visible circle. This is a pure screen-space
|
|
321
|
-
// size, so it uses the true _hotspotSize and the clamp ratio only.
|
|
322
|
-
const resources = this._annotationResources.get(annotation);
|
|
323
|
-
if (resources) {
|
|
324
|
-
// This branch only runs for annotations in front of the camera, and the scale above is now
|
|
325
|
-
// clamped, so it is safe to reveal the mesh (hidden on register / when behind the camera).
|
|
326
|
-
resources.baseEntity.enabled = true;
|
|
327
|
-
resources.overlayEntity.enabled = true;
|
|
123
|
+
_setAnnotationHover(_annotation, _hover) {}
|
|
124
|
+
_updateAllAnnotationColors() {}
|
|
125
|
+
_showTooltip(_annotation) {}
|
|
126
|
+
_onTextChange(_annotation, _text) {}
|
|
127
|
+
_updateAnnotationPositions(annotation, resources, screenPos) {
|
|
128
|
+
const canvas = this.app.graphicsDevice.canvas;
|
|
129
|
+
const rect = canvas.getBoundingClientRect();
|
|
130
|
+
const offsetX = this._customParentDom ? 0 : rect.left + window.scrollX;
|
|
131
|
+
const offsetY = this._customParentDom ? 0 : rect.top + window.scrollY;
|
|
132
|
+
const isVisible = void 0 !== annotation.enabled ? annotation.enabled : true;
|
|
133
|
+
resources.hotspotDom.style.display = isVisible ? 'block' : 'none';
|
|
134
|
+
resources.hotspotDom.style.left = `${screenPos.x + offsetX}px`;
|
|
135
|
+
resources.hotspotDom.style.top = `${screenPos.y + offsetY}px`;
|
|
136
|
+
if (this._activeAnnotation === annotation) {
|
|
137
|
+
this._tooltipDom.style.display = isVisible ? 'block' : 'none';
|
|
138
|
+
this._tooltipDom.style.left = `${screenPos.x + offsetX}px`;
|
|
139
|
+
this._tooltipDom.style.top = `${screenPos.y + offsetY}px`;
|
|
140
|
+
}
|
|
141
|
+
if (annotation.onScreenPositionUpdateCallback) {
|
|
142
|
+
const hotspotSize = parseFloat(resources.hotspotDom.style.width) || void 0;
|
|
143
|
+
annotation.onScreenPositionUpdateCallback(screenPos.x + offsetX, screenPos.y + offsetY, hotspotSize);
|
|
144
|
+
}
|
|
328
145
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
resources.hotspotDom.style.width = `${scaledSize}px`;
|
|
334
|
-
resources.hotspotDom.style.height = `${scaledSize}px`;
|
|
146
|
+
_hideAnnotationElements(annotation, resources) {
|
|
147
|
+
super._hideAnnotationElements(annotation, resources);
|
|
148
|
+
resources.baseEntity.enabled = false;
|
|
149
|
+
resources.overlayEntity.enabled = false;
|
|
335
150
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
151
|
+
_updateAnnotationRotationAndScale(annotation) {
|
|
152
|
+
const cameraRotation = this._camera.getRotation();
|
|
153
|
+
annotation.entity.setRotation(cameraRotation);
|
|
154
|
+
annotation.entity.rotateLocal(90, 0, 0);
|
|
155
|
+
const cameraPos = this._camera.getPosition();
|
|
156
|
+
const distance = annotation.entity.getPosition().distance(cameraPos);
|
|
157
|
+
const canvas = this.app.graphicsDevice.canvas;
|
|
158
|
+
const screenHeight = canvas.clientHeight;
|
|
159
|
+
const projMatrix = this._camera.camera.projectionMatrix;
|
|
160
|
+
const targetWorldSize = this._hotspotSize / screenHeight * (2 * distance / projMatrix.data[5]);
|
|
161
|
+
const clampedScale = Math.min(targetWorldSize, this._maxWorldSize);
|
|
162
|
+
const hovered = this._hoverAnnotation === annotation;
|
|
163
|
+
const hoverScale = hovered ? HOTSPOT_HOVER_SCALE : 1;
|
|
164
|
+
const entityScale = clampedScale * hoverScale;
|
|
165
|
+
annotation.entity.setLocalScale(entityScale, entityScale, entityScale);
|
|
166
|
+
const resources = this._annotationResources.get(annotation);
|
|
167
|
+
if (resources) {
|
|
168
|
+
resources.baseEntity.enabled = true;
|
|
169
|
+
resources.overlayEntity.enabled = true;
|
|
170
|
+
}
|
|
171
|
+
if (resources && resources.hotspotDom) {
|
|
172
|
+
const clampRatio = clampedScale / targetWorldSize;
|
|
173
|
+
const baseSize = this._hotspotSize + 5;
|
|
174
|
+
const scaledSize = baseSize * clampRatio * hoverScale;
|
|
175
|
+
resources.hotspotDom.style.width = `${scaledSize}px`;
|
|
176
|
+
resources.hotspotDom.style.height = `${scaledSize}px`;
|
|
177
|
+
}
|
|
349
178
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
179
|
+
_createHotspotTexture(label, size = HOTSPOT_TEXTURE_SIZE, borderWidth = 6, icon) {
|
|
180
|
+
const canvas = document.createElement('canvas');
|
|
181
|
+
canvas.width = size;
|
|
182
|
+
canvas.height = size;
|
|
183
|
+
const ctx = canvas.getContext('2d');
|
|
184
|
+
if (!ctx) throw new Error('Failed to get canvas 2d context');
|
|
185
|
+
ctx.fillStyle = 'white';
|
|
186
|
+
ctx.globalAlpha = 0;
|
|
187
|
+
ctx.fillRect(0, 0, size, size);
|
|
188
|
+
ctx.globalAlpha = 1.0;
|
|
189
|
+
const centerX = size / 2;
|
|
190
|
+
const centerY = size / 2;
|
|
191
|
+
const radius = size / 2 - 4;
|
|
192
|
+
const foregroundColor = this._hotspotColor?.toString() ?? '#ffffff';
|
|
193
|
+
ctx.beginPath();
|
|
194
|
+
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
|
|
195
|
+
ctx.fillStyle = this._hotspotBackgroundColor;
|
|
196
|
+
ctx.fill();
|
|
197
|
+
ctx.beginPath();
|
|
198
|
+
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
|
|
199
|
+
ctx.lineWidth = borderWidth;
|
|
200
|
+
ctx.strokeStyle = foregroundColor;
|
|
201
|
+
ctx.stroke();
|
|
202
|
+
if (icon) this._drawIcon(ctx, icon, size);
|
|
203
|
+
const imageData = ctx.getImageData(0, 0, size, size);
|
|
204
|
+
const data = imageData.data;
|
|
205
|
+
return new Texture(this.app.graphicsDevice, {
|
|
206
|
+
width: size,
|
|
207
|
+
height: size,
|
|
208
|
+
format: PIXELFORMAT_RGBA8,
|
|
209
|
+
magFilter: FILTER_LINEAR,
|
|
210
|
+
minFilter: FILTER_LINEAR,
|
|
211
|
+
mipmaps: false,
|
|
212
|
+
levels: [
|
|
213
|
+
new Uint8Array(data.buffer)
|
|
214
|
+
]
|
|
215
|
+
});
|
|
381
216
|
}
|
|
382
|
-
|
|
383
|
-
// Get pixel data
|
|
384
|
-
const imageData = ctx.getImageData(0, 0, size, size);
|
|
385
|
-
const data = imageData.data;
|
|
386
|
-
|
|
387
|
-
// Create and return the texture
|
|
388
|
-
return new Texture(this.app.graphicsDevice, {
|
|
389
|
-
width: size,
|
|
390
|
-
height: size,
|
|
391
|
-
format: PIXELFORMAT_RGBA8,
|
|
392
|
-
magFilter: FILTER_LINEAR,
|
|
393
|
-
minFilter: FILTER_LINEAR,
|
|
394
|
-
mipmaps: false,
|
|
395
|
-
levels: [new Uint8Array(data.buffer)]
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
217
|
}
|
|
399
|
-
|
|
400
218
|
export { TfAnnotationManager };
|