@tableslayer/ui 0.1.3 → 0.1.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.
Files changed (205) hide show
  1. package/package.json +2 -13
  2. package/src/lib/components/Avatar/Avatar.svelte +82 -0
  3. package/src/lib/components/Avatar/AvatarFileInput.svelte +85 -0
  4. package/src/lib/components/Avatar/AvatarPopover.svelte +34 -0
  5. package/src/lib/components/Avatar/index.ts +4 -0
  6. package/src/lib/components/Avatar/types.ts +24 -0
  7. package/src/lib/components/BrushSizeSlider/BrushSizeSlider.svelte +174 -0
  8. package/src/lib/components/BrushSizeSlider/index.ts +1 -0
  9. package/src/lib/components/Button/Button.svelte +182 -0
  10. package/src/lib/components/Button/ConfirmActionButton.svelte +98 -0
  11. package/src/lib/components/Button/IconButton.svelte +121 -0
  12. package/src/lib/components/Button/RadioButton.svelte +93 -0
  13. package/src/lib/components/Button/index.ts +5 -0
  14. package/src/lib/components/Button/types.ts +54 -0
  15. package/src/lib/components/CardFan/CardFan.svelte +165 -0
  16. package/src/lib/components/CardFan/index.ts +2 -0
  17. package/src/lib/components/CardFan/types.ts +6 -0
  18. package/src/lib/components/CodeBlock/Code.svelte +7 -0
  19. package/src/lib/components/CodeBlock/CodeBlock.svelte +102 -0
  20. package/src/lib/components/CodeBlock/index.ts +3 -0
  21. package/src/lib/components/CodeBlock/types.ts +10 -0
  22. package/src/lib/components/ColorMode/ColorMode.svelte +8 -0
  23. package/src/lib/components/ColorMode/index.ts +2 -0
  24. package/src/lib/components/ColorMode/types.ts +12 -0
  25. package/src/lib/components/ColorPicker/ColorPicker.svelte +838 -0
  26. package/src/lib/components/ColorPicker/ColorPickerSwatch.svelte +32 -0
  27. package/src/lib/components/ColorPicker/index.ts +3 -0
  28. package/src/lib/components/ColorPicker/types.ts +51 -0
  29. package/src/lib/components/ContextMenu/ContextMenu.svelte +86 -0
  30. package/src/lib/components/ContextMenu/index.ts +2 -0
  31. package/src/lib/components/ContextMenu/types.ts +15 -0
  32. package/src/lib/components/DrawingSliders/DrawingSliders.svelte +379 -0
  33. package/src/lib/components/DrawingSliders/index.ts +1 -0
  34. package/src/lib/components/Editor/Editor.svelte +825 -0
  35. package/src/lib/components/Editor/index.ts +1 -0
  36. package/src/lib/components/FogSliders/FogSliders.svelte +33 -0
  37. package/src/lib/components/FogSliders/index.ts +1 -0
  38. package/src/lib/components/Hr/Hr.svelte +15 -0
  39. package/src/lib/components/Hr/index.ts +1 -0
  40. package/src/lib/components/Icon/Icon.svelte +6 -0
  41. package/src/lib/components/Icon/index.ts +2 -0
  42. package/src/lib/components/Icon/types.ts +20 -0
  43. package/src/lib/components/Input/DualInputSlider.svelte +126 -0
  44. package/src/lib/components/Input/FileInput.svelte +176 -0
  45. package/src/lib/components/Input/FormControl.svelte +150 -0
  46. package/src/lib/components/Input/FormError.svelte +37 -0
  47. package/src/lib/components/Input/Input.svelte +56 -0
  48. package/src/lib/components/Input/InputCheckbox.svelte +99 -0
  49. package/src/lib/components/Input/InputSlider.svelte +86 -0
  50. package/src/lib/components/Input/Label.svelte +19 -0
  51. package/src/lib/components/Input/index.ts +9 -0
  52. package/src/lib/components/Input/types.ts +39 -0
  53. package/src/lib/components/Link/Link.svelte +41 -0
  54. package/src/lib/components/Link/LinkBox.svelte +20 -0
  55. package/src/lib/components/Link/LinkOverlay.svelte +23 -0
  56. package/src/lib/components/Link/index.ts +4 -0
  57. package/src/lib/components/Link/types.ts +17 -0
  58. package/src/lib/components/Loading/Loader.svelte +60 -0
  59. package/src/lib/components/Loading/Skeleton.svelte +9 -0
  60. package/src/lib/components/Loading/index.ts +2 -0
  61. package/src/lib/components/Logo/Logo.svelte +16 -0
  62. package/src/lib/components/Logo/index.ts +1 -0
  63. package/src/lib/components/MarkerTooltip/MarkerTooltip.svelte +435 -0
  64. package/src/lib/components/MarkerTooltip/index.ts +1 -0
  65. package/src/lib/components/Menu/SelectorMenu.svelte +280 -0
  66. package/src/lib/components/Menu/index.ts +2 -0
  67. package/src/lib/components/Menu/types.ts +17 -0
  68. package/src/lib/components/MyCounterButton.svelte +11 -0
  69. package/src/lib/components/Panel/index.ts +2 -0
  70. package/src/lib/components/Panel/panel.svelte +18 -0
  71. package/src/lib/components/Panel/types.ts +8 -0
  72. package/src/lib/components/PersistButton/PersistButton.svelte +100 -0
  73. package/src/lib/components/PersistButton/index.ts +1 -0
  74. package/src/lib/components/Popover/Popover.svelte +81 -0
  75. package/src/lib/components/Popover/index.ts +2 -0
  76. package/src/lib/components/Popover/types.ts +19 -0
  77. package/src/lib/components/PropsTable/PropsTable.svelte +107 -0
  78. package/src/lib/components/RadialMenu/EffectPreview.svelte +36 -0
  79. package/src/lib/components/RadialMenu/EffectPreviewScene.svelte +194 -0
  80. package/src/lib/components/RadialMenu/RadialMenu.svelte +503 -0
  81. package/src/lib/components/RadialMenu/RadialMenuItem.svelte +176 -0
  82. package/src/lib/components/RadialMenu/index.ts +2 -0
  83. package/src/lib/components/RadialMenu/types.ts +35 -0
  84. package/src/lib/components/Select/Select.svelte +342 -0
  85. package/src/lib/components/Select/index.ts +2 -0
  86. package/src/lib/components/Select/types.ts +22 -0
  87. package/src/lib/components/Spacer/Spacer.svelte +14 -0
  88. package/src/lib/components/Spacer/index.ts +2 -0
  89. package/src/lib/components/Spacer/types.ts +5 -0
  90. package/src/lib/components/Stage/components/AnnotationLayer/AnnotationLayer.svelte +445 -0
  91. package/src/lib/components/Stage/components/AnnotationLayer/AnnotationMaterial.svelte +167 -0
  92. package/src/lib/components/Stage/components/AnnotationLayer/types.ts +196 -0
  93. package/src/lib/components/Stage/components/CursorLayer/CursorLayer.svelte +148 -0
  94. package/src/lib/components/Stage/components/CursorLayer/cursor.svg +26 -0
  95. package/src/lib/components/Stage/components/CursorLayer/index.ts +2 -0
  96. package/src/lib/components/Stage/components/CursorLayer/types.ts +23 -0
  97. package/src/lib/components/Stage/components/DrawingLayer/DrawingMaterial.svelte +364 -0
  98. package/src/lib/components/Stage/components/DrawingLayer/types.ts +65 -0
  99. package/src/lib/components/Stage/components/EdgeOverlayLayer/EdgeOverlayLayer.svelte +72 -0
  100. package/src/lib/components/Stage/components/EdgeOverlayLayer/types.ts +34 -0
  101. package/src/lib/components/Stage/components/FogLayer/FogLayer.svelte +75 -0
  102. package/src/lib/components/Stage/components/FogLayer/types.ts +51 -0
  103. package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarLayer.svelte +249 -0
  104. package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarMaterial.svelte +200 -0
  105. package/src/lib/components/Stage/components/FogOfWarLayer/types.ts +116 -0
  106. package/src/lib/components/Stage/components/GridLayer/GridLayer.svelte +20 -0
  107. package/src/lib/components/Stage/components/GridLayer/GridMaterial.svelte +69 -0
  108. package/src/lib/components/Stage/components/GridLayer/types.ts +79 -0
  109. package/src/lib/components/Stage/components/LayerInput/LayerInput.svelte +300 -0
  110. package/src/lib/components/Stage/components/MapLayer/MapLayer.svelte +196 -0
  111. package/src/lib/components/Stage/components/MapLayer/dataSources/GifDataSource.ts +265 -0
  112. package/src/lib/components/Stage/components/MapLayer/dataSources/IMapDataSource.ts +55 -0
  113. package/src/lib/components/Stage/components/MapLayer/dataSources/ImageDataSource.ts +87 -0
  114. package/src/lib/components/Stage/components/MapLayer/dataSources/VideoDataSource.ts +150 -0
  115. package/src/lib/components/Stage/components/MapLayer/dataSources/dataSourceFactory.ts +48 -0
  116. package/src/lib/components/Stage/components/MapLayer/dataSources/index.ts +16 -0
  117. package/src/lib/components/Stage/components/MapLayer/types.ts +58 -0
  118. package/src/lib/components/Stage/components/MarkerLayer/MarkerLayer.svelte +398 -0
  119. package/src/lib/components/Stage/components/MarkerLayer/MarkerToken.svelte +262 -0
  120. package/src/lib/components/Stage/components/MarkerLayer/types.ts +126 -0
  121. package/src/lib/components/Stage/components/MeasurementLayer/MeasurementLayer.svelte +364 -0
  122. package/src/lib/components/Stage/components/MeasurementLayer/MeasurementManager.svelte +473 -0
  123. package/src/lib/components/Stage/components/MeasurementLayer/measurements/BaseMeasurement.ts +427 -0
  124. package/src/lib/components/Stage/components/MeasurementLayer/measurements/BeamMeasurement.ts +105 -0
  125. package/src/lib/components/Stage/components/MeasurementLayer/measurements/CircleMeasurement.ts +98 -0
  126. package/src/lib/components/Stage/components/MeasurementLayer/measurements/ConeMeasurement.ts +163 -0
  127. package/src/lib/components/Stage/components/MeasurementLayer/measurements/LineMeasurement.ts +102 -0
  128. package/src/lib/components/Stage/components/MeasurementLayer/measurements/RectangleMeasurement.ts +120 -0
  129. package/src/lib/components/Stage/components/MeasurementLayer/measurements/index.ts +7 -0
  130. package/src/lib/components/Stage/components/MeasurementLayer/types.ts +94 -0
  131. package/src/lib/components/Stage/components/MeasurementLayer/utils/canvasDrawing.ts +357 -0
  132. package/src/lib/components/Stage/components/MeasurementLayer/utils/distanceCalculations.ts +170 -0
  133. package/src/lib/components/Stage/components/ParticleSystem/ParticleSystem.svelte +220 -0
  134. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/ash.png +0 -0
  135. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/leaves.png +0 -0
  136. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/rain.png +0 -0
  137. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/snow.png +0 -0
  138. package/src/lib/components/Stage/components/ParticleSystem/rng.js +20 -0
  139. package/src/lib/components/Stage/components/ParticleSystem/types.ts +95 -0
  140. package/src/lib/components/Stage/components/PerformanceDebugger/PerformanceDebugger.svelte +144 -0
  141. package/src/lib/components/Stage/components/PerformanceDebugger/index.ts +1 -0
  142. package/src/lib/components/Stage/components/PerformanceOverlay/PerformanceOverlay.svelte +208 -0
  143. package/src/lib/components/Stage/components/PerformanceOverlay/index.ts +1 -0
  144. package/src/lib/components/Stage/components/PointerInputManager/PointerInputManager.svelte +201 -0
  145. package/src/lib/components/Stage/components/Scene/Scene.svelte +651 -0
  146. package/src/lib/components/Stage/components/Scene/luts.ts +24 -0
  147. package/src/lib/components/Stage/components/Scene/types.ts +225 -0
  148. package/src/lib/components/Stage/components/Stage/Stage.svelte +332 -0
  149. package/src/lib/components/Stage/components/Stage/types.ts +136 -0
  150. package/src/lib/components/Stage/components/WeatherLayer/WeatherLayer.svelte +135 -0
  151. package/src/lib/components/Stage/components/WeatherLayer/presets/AshPreset.ts +71 -0
  152. package/src/lib/components/Stage/components/WeatherLayer/presets/LeavesPreset.ts +70 -0
  153. package/src/lib/components/Stage/components/WeatherLayer/presets/RainPreset.ts +68 -0
  154. package/src/lib/components/Stage/components/WeatherLayer/presets/SnowPreset.ts +70 -0
  155. package/src/lib/components/Stage/components/WeatherLayer/presets/index.ts +6 -0
  156. package/src/lib/components/Stage/components/WeatherLayer/types.ts +35 -0
  157. package/src/lib/components/Stage/helpers/clippingPlaneStore.svelte.ts +28 -0
  158. package/src/lib/components/Stage/helpers/debugState.svelte.ts +18 -0
  159. package/src/lib/components/Stage/helpers/grid.ts +548 -0
  160. package/src/lib/components/Stage/helpers/lazyBrush.ts +171 -0
  161. package/src/lib/components/Stage/helpers/performanceMetrics.svelte.ts +220 -0
  162. package/src/lib/components/Stage/helpers/utils.ts +21 -0
  163. package/src/lib/components/Stage/index.ts +49 -0
  164. package/src/lib/components/Stage/shaders/AnnotationEffects.frag +1070 -0
  165. package/src/lib/components/Stage/shaders/Annotations.frag +29 -0
  166. package/src/lib/components/Stage/shaders/Drawing.frag +83 -0
  167. package/src/lib/components/Stage/shaders/Drawing.vert +5 -0
  168. package/src/lib/components/Stage/shaders/Fog.frag +147 -0
  169. package/src/lib/components/Stage/shaders/FractalNoise.frag +96 -0
  170. package/src/lib/components/Stage/shaders/GridShader.frag +174 -0
  171. package/src/lib/components/Stage/shaders/Overlay.frag +23 -0
  172. package/src/lib/components/Stage/shaders/Overlay.vert +0 -0
  173. package/src/lib/components/Stage/shaders/Particles.frag +27 -0
  174. package/src/lib/components/Stage/shaders/Particles.vert +51 -0
  175. package/src/lib/components/Stage/shaders/ToolOutline.frag +59 -0
  176. package/src/lib/components/Stage/shaders/default.vert +8 -0
  177. package/src/lib/components/Stage/types.ts +4 -0
  178. package/src/lib/components/Table/Table.svelte +16 -0
  179. package/src/lib/components/Table/Td.svelte +17 -0
  180. package/src/lib/components/Table/Th.svelte +18 -0
  181. package/src/lib/components/Table/index.ts +4 -0
  182. package/src/lib/components/Table/types.ts +14 -0
  183. package/src/lib/components/Text/Text.svelte +23 -0
  184. package/src/lib/components/Text/index.ts +2 -0
  185. package/src/lib/components/Text/types.ts +12 -0
  186. package/src/lib/components/Title/Title.svelte +54 -0
  187. package/src/lib/components/Title/index.ts +2 -0
  188. package/src/lib/components/Title/types.ts +9 -0
  189. package/src/lib/components/Toast/Toast.svelte +155 -0
  190. package/src/lib/components/Toast/index.ts +5 -0
  191. package/src/lib/components/Toast/toastCookie.ts +24 -0
  192. package/src/lib/components/Toast/types.ts +6 -0
  193. package/src/lib/components/ToolTip/ToolTip.svelte +70 -0
  194. package/src/lib/components/ToolTip/index.ts +2 -0
  195. package/src/lib/components/ToolTip/types.ts +14 -0
  196. package/src/lib/components/index.ts +32 -0
  197. package/src/lib/components/types.ts +0 -0
  198. package/src/lib/index.ts +2 -0
  199. package/src/lib/styles/globals.css +108 -0
  200. package/src/lib/styles/normalize.css +9 -0
  201. package/src/lib/styles/reset.css +133 -0
  202. package/src/lib/styles/utilities.css +179 -0
  203. package/src/lib/styles/vars.css +1103 -0
  204. package/src/lib/types/awareness.ts +17 -0
  205. package/src/lib/utils/rle.ts +217 -0
@@ -0,0 +1,473 @@
1
+ <script lang="ts">
2
+ import * as THREE from 'three';
3
+ import { T, useTask } from '@threlte/core';
4
+ import { onDestroy } from 'svelte';
5
+ import { MeasurementType, type MeasurementLayerProps } from './types';
6
+ import type { IMeasurement } from './measurements/BaseMeasurement';
7
+ import { LineMeasurement } from './measurements/LineMeasurement';
8
+ import { CircleMeasurement } from './measurements/CircleMeasurement';
9
+ import { RectangleMeasurement } from './measurements/RectangleMeasurement';
10
+ import { BeamMeasurement } from './measurements/BeamMeasurement';
11
+ import { ConeMeasurement } from './measurements/ConeMeasurement';
12
+ import { drawCircle } from './utils/canvasDrawing';
13
+ import type { DisplayProps } from '../Stage/types';
14
+ import type { GridLayerProps } from '../GridLayer/types';
15
+ import { SceneLayer, SceneLayerOrder } from '../Scene/types';
16
+
17
+ interface Props {
18
+ props: MeasurementLayerProps;
19
+ visible: boolean;
20
+ displayProps: DisplayProps;
21
+ gridProps: GridLayerProps;
22
+ sceneRotation?: number;
23
+ onFadeComplete?: () => void;
24
+ }
25
+
26
+ const { props, visible, displayProps, gridProps, sceneRotation = 0, onFadeComplete }: Props = $props();
27
+
28
+ $effect(() => {
29
+ console.log('[MeasurementManager] Grid props:', gridProps);
30
+ });
31
+
32
+ let currentMeasurement: IMeasurement | null = null;
33
+ let measurementGroup = $state(new THREE.Group());
34
+ let autoHideTimeoutId: ReturnType<typeof setTimeout> | null = null;
35
+
36
+ // Fade animation state
37
+ let isFading = $state(false);
38
+ let fadeStartTime = $state(0);
39
+ let fadeOpacity = $state(1.0);
40
+ let receivedFadeoutTime = $state<number | null>(null); // Store fadeout time for received measurements
41
+
42
+ // Preview indicator
43
+ let previewMesh = $state(new THREE.Mesh());
44
+ let previewMaterial = $state(new THREE.MeshBasicMaterial());
45
+ let previewGeometry = $state(new THREE.PlaneGeometry());
46
+ let previewSize = $derived(props ? props.markerSize + props.outlineThickness * 2 : 22);
47
+ let showPreview = $state(false);
48
+
49
+ // Task for fade animation
50
+ useTask(() => {
51
+ if (isFading && props) {
52
+ const now = performance.now();
53
+ const fadeElapsed = now - fadeStartTime;
54
+ // Use receivedFadeoutTime if set, otherwise use props.fadeoutTime
55
+ const fadeTime = receivedFadeoutTime ?? props.fadeoutTime;
56
+ const progress = Math.min(fadeElapsed / fadeTime, 1);
57
+
58
+ fadeOpacity = 1 - progress;
59
+
60
+ // Update the opacity of all materials in the measurement group
61
+ if (currentMeasurement) {
62
+ if (currentMeasurement.shapeMesh.material instanceof THREE.MeshBasicMaterial) {
63
+ currentMeasurement.shapeMesh.material.opacity = props.opacity * fadeOpacity;
64
+ }
65
+ if (currentMeasurement.textMesh.material instanceof THREE.MeshBasicMaterial) {
66
+ currentMeasurement.textMesh.material.opacity = props.opacity * fadeOpacity;
67
+ }
68
+ }
69
+
70
+ if (progress >= 1) {
71
+ isFading = false;
72
+ clearMeasurement();
73
+ // Notify parent that fade is complete
74
+ if (onFadeComplete) {
75
+ onFadeComplete();
76
+ }
77
+ }
78
+ }
79
+ });
80
+
81
+ onDestroy(() => {
82
+ // Clear any pending auto-hide timeout
83
+ if (autoHideTimeoutId !== null) {
84
+ clearTimeout(autoHideTimeoutId);
85
+ autoHideTimeoutId = null;
86
+ }
87
+
88
+ // Stop fade animation
89
+ isFading = false;
90
+
91
+ currentMeasurement?.dispose();
92
+ if (previewGeometry) {
93
+ previewGeometry.dispose();
94
+ }
95
+ if (previewMaterial) {
96
+ if (previewMaterial.map) {
97
+ previewMaterial.map.dispose();
98
+ }
99
+ previewMaterial.dispose();
100
+ }
101
+ });
102
+
103
+ /**
104
+ * Creates a canvas texture for the preview marker that matches the measurement point styling.
105
+ * The marker uses the same visual properties (color, thickness, outline) as measurement start/end points.
106
+ */
107
+ $effect(() => {
108
+ if (!props) return;
109
+
110
+ // Create the preview marker texture
111
+ const markerCanvas = createMarkerCanvas();
112
+ const markerTexture = new THREE.CanvasTexture(markerCanvas);
113
+ markerTexture.needsUpdate = true;
114
+
115
+ // Dispose the old texture before assigning new one to prevent memory leak
116
+ if (previewMaterial.map) {
117
+ previewMaterial.map.dispose();
118
+ }
119
+
120
+ // Assign the texture to the preview material
121
+ previewMaterial.map = markerTexture;
122
+ previewMaterial.needsUpdate = true;
123
+ });
124
+
125
+ /**
126
+ * Updates the preview indicator position and visibility based on mouse position and measurement state.
127
+ * The preview is automatically hidden when a measurement is currently being created.
128
+ *
129
+ * @param {THREE.Vector2 | null} position - The world position where the preview should be displayed, or null to hide
130
+ * @param {boolean} [visible=true] - Whether the preview should be visible (defaults to true)
131
+ * @returns {void}
132
+ */
133
+ function updatePreview(position: THREE.Vector2 | null, visible: boolean = true): void {
134
+ if (!position || currentMeasurement) {
135
+ showPreview = false;
136
+ return;
137
+ }
138
+
139
+ showPreview = visible;
140
+ if (previewMesh && showPreview) {
141
+ previewMesh.position.set(position.x, position.y, 0);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Hides the preview indicator from view.
147
+ * Used when measurements are active or when the cursor leaves the measurement area.
148
+ * @returns {void}
149
+ */
150
+ function hidePreview(): void {
151
+ showPreview = false;
152
+ }
153
+
154
+ /**
155
+ * Shows the preview indicator if no measurement is currently active.
156
+ * Provides visual feedback for where the next measurement point will be placed.
157
+ * @returns {void}
158
+ */
159
+ function showPreviewIndicator(): void {
160
+ if (!currentMeasurement) {
161
+ showPreview = true;
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Creates a canvas texture for the measurement marker that matches the measurement point styling.
167
+ * The marker uses the same visual properties (color, thickness, outline) as measurement start/end points.
168
+ *
169
+ * @returns {HTMLCanvasElement} A canvas element with the rendered marker that can be used as a texture
170
+ */
171
+ function createMarkerCanvas(): HTMLCanvasElement {
172
+ const canvas = document.createElement('canvas');
173
+ const context = canvas.getContext('2d', { colorSpace: 'srgb' })!;
174
+
175
+ const width = previewSize;
176
+ const height = previewSize;
177
+
178
+ canvas.width = width;
179
+ canvas.height = height;
180
+
181
+ context.clearRect(0, 0, canvas.width, canvas.height);
182
+
183
+ if (props) {
184
+ drawCircle(
185
+ context,
186
+ previewSize / 2,
187
+ previewSize / 2,
188
+ props.markerSize / 2,
189
+ props.color,
190
+ props.outlineColor,
191
+ props.outlineThickness
192
+ );
193
+ }
194
+
195
+ return canvas;
196
+ }
197
+
198
+ /**
199
+ * Starts a new measurement at the specified point based on the current measurement type.
200
+ * Clears any existing measurement and creates the appropriate measurement class instance.
201
+ *
202
+ * @param {THREE.Vector2} startPoint - The world coordinates where the measurement should begin
203
+ * @returns {void}
204
+ */
205
+ function startMeasurement(startPoint: THREE.Vector2): void {
206
+ if (!props) return;
207
+
208
+ // Clear any existing auto-hide timeout
209
+ if (autoHideTimeoutId !== null) {
210
+ clearTimeout(autoHideTimeoutId);
211
+ autoHideTimeoutId = null;
212
+ }
213
+
214
+ // Reset fade state
215
+ isFading = false;
216
+ fadeOpacity = 1.0;
217
+
218
+ clearMeasurement();
219
+ showPreview = false; // Hide preview when starting measurement
220
+
221
+ // Create new measurement based on type
222
+ let measurement: IMeasurement;
223
+
224
+ switch (props.type) {
225
+ case MeasurementType.Line:
226
+ measurement = new LineMeasurement(startPoint, props, displayProps, gridProps);
227
+ break;
228
+ case MeasurementType.Beam:
229
+ measurement = new BeamMeasurement(startPoint, props, displayProps, gridProps);
230
+ break;
231
+ case MeasurementType.Cone:
232
+ measurement = new ConeMeasurement(startPoint, props, displayProps, gridProps);
233
+ break;
234
+ case MeasurementType.Circle:
235
+ measurement = new CircleMeasurement(startPoint, props, displayProps, gridProps);
236
+ break;
237
+ case MeasurementType.Square:
238
+ measurement = new RectangleMeasurement(startPoint, props, displayProps, gridProps);
239
+ break;
240
+ default:
241
+ measurement = new LineMeasurement(startPoint, props, displayProps, gridProps);
242
+ break;
243
+ }
244
+
245
+ currentMeasurement = measurement;
246
+ measurementGroup.add(measurement.object);
247
+ }
248
+
249
+ /**
250
+ * Updates the current measurement with a new end point, typically called during mouse movement.
251
+ * Recalculates measurement geometry and updates the visual representation in real-time.
252
+ *
253
+ * @param {THREE.Vector2} endPoint - The world coordinates for the current end point of the measurement
254
+ * @returns {void}
255
+ */
256
+ function updateMeasurement(endPoint: THREE.Vector2): void {
257
+ currentMeasurement?.update(endPoint, sceneRotation);
258
+ }
259
+
260
+ /**
261
+ * Completes the current measurement and schedules its automatic removal.
262
+ * The measurement is displayed for the duration specified by autoHideDelay, then fades out over fadeoutTime before being cleared.
263
+ * @returns {void}
264
+ */
265
+ function finishMeasurement(): void {
266
+ if (!currentMeasurement || !props) return;
267
+
268
+ // Don't finish measurements with zero distance (same start and end point)
269
+ const distance = currentMeasurement.startPoint.distanceTo(currentMeasurement.endPoint);
270
+ if (distance === 0) {
271
+ clearMeasurement();
272
+ return;
273
+ }
274
+
275
+ autoHideTimeoutId = setTimeout(() => {
276
+ // Start the fade animation
277
+ fadeStartTime = performance.now();
278
+ isFading = true;
279
+ }, props.autoHideDelay);
280
+ }
281
+
282
+ /**
283
+ * Removes the current measurement from the scene and disposes of its resources.
284
+ * Cleans up Three.js objects to prevent memory leaks and resets the measurement state.
285
+ * @returns {void}
286
+ */
287
+ function clearMeasurement(): void {
288
+ // Clear any existing auto-hide timeout
289
+ if (autoHideTimeoutId !== null) {
290
+ clearTimeout(autoHideTimeoutId);
291
+ autoHideTimeoutId = null;
292
+ }
293
+
294
+ // Reset fade state
295
+ isFading = false;
296
+ fadeOpacity = 1.0;
297
+ receivedFadeoutTime = null; // Reset received fadeout time
298
+
299
+ if (currentMeasurement) {
300
+ currentMeasurement.dispose();
301
+ currentMeasurement = null;
302
+ measurementGroup.clear();
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Displays a measurement received from another user (via Y.js).
308
+ * Creates the measurement and immediately sets it to finished state to trigger auto-fade.
309
+ *
310
+ * @param {THREE.Vector2} startPoint - The starting point of the measurement
311
+ * @param {THREE.Vector2} endPoint - The ending point of the measurement
312
+ * @param {number} type - The type of measurement (MeasurementType enum)
313
+ * @returns {void}
314
+ */
315
+ function displayReceivedMeasurement(
316
+ startPoint: THREE.Vector2,
317
+ endPoint: THREE.Vector2,
318
+ type: number,
319
+ beamWidth?: number,
320
+ coneAngle?: number,
321
+ color?: string,
322
+ thickness?: number,
323
+ outlineColor?: string,
324
+ outlineThickness?: number,
325
+ opacity?: number,
326
+ markerSize?: number,
327
+ autoHideDelay?: number,
328
+ fadeoutTime?: number,
329
+ showDistance?: boolean,
330
+ snapToGrid?: boolean,
331
+ enableDMG252?: boolean
332
+ ): void {
333
+ if (!props) {
334
+ console.log('[MeasurementManager] No props available for displayReceivedMeasurement');
335
+ return;
336
+ }
337
+
338
+ console.log('[MeasurementManager] displayReceivedMeasurement called:', {
339
+ startPoint,
340
+ endPoint,
341
+ type,
342
+ beamWidth,
343
+ coneAngle,
344
+ thickness,
345
+ hasCurrentMeasurement: !!currentMeasurement
346
+ });
347
+
348
+ // Clear any existing measurement
349
+ clearMeasurement();
350
+
351
+ // Create the appropriate measurement type
352
+ let measurement: IMeasurement;
353
+
354
+ // Use the received properties if provided, override the props temporarily
355
+ const measurementProps = {
356
+ ...props,
357
+ type,
358
+ ...(beamWidth !== undefined && { beamWidth }),
359
+ ...(coneAngle !== undefined && { coneAngle }),
360
+ ...(color !== undefined && { color }),
361
+ ...(thickness !== undefined && { thickness }),
362
+ ...(outlineColor !== undefined && { outlineColor }),
363
+ ...(outlineThickness !== undefined && { outlineThickness }),
364
+ ...(opacity !== undefined && { opacity }),
365
+ ...(markerSize !== undefined && { markerSize }),
366
+ ...(autoHideDelay !== undefined && { autoHideDelay }),
367
+ ...(fadeoutTime !== undefined && { fadeoutTime }),
368
+ ...(showDistance !== undefined && { showDistance }),
369
+ ...(snapToGrid !== undefined && { snapToGrid }),
370
+ ...(enableDMG252 !== undefined && { enableDMG252 })
371
+ };
372
+
373
+ switch (type) {
374
+ case MeasurementType.Line:
375
+ measurement = new LineMeasurement(startPoint, measurementProps, displayProps, gridProps);
376
+ break;
377
+ case MeasurementType.Beam:
378
+ measurement = new BeamMeasurement(startPoint, measurementProps, displayProps, gridProps);
379
+ break;
380
+ case MeasurementType.Cone:
381
+ measurement = new ConeMeasurement(startPoint, measurementProps, displayProps, gridProps);
382
+ break;
383
+ case MeasurementType.Circle:
384
+ measurement = new CircleMeasurement(startPoint, measurementProps, displayProps, gridProps);
385
+ break;
386
+ case MeasurementType.Square:
387
+ measurement = new RectangleMeasurement(startPoint, measurementProps, displayProps, gridProps);
388
+ break;
389
+ default:
390
+ measurement = new LineMeasurement(startPoint, measurementProps, displayProps, gridProps);
391
+ break;
392
+ }
393
+
394
+ // Set the measurement
395
+ currentMeasurement = measurement;
396
+ measurementGroup.add(measurement.object);
397
+
398
+ console.log('[MeasurementManager] Measurement created and added to group:', {
399
+ groupChildren: measurementGroup.children.length,
400
+ measurementObject: measurement.object,
401
+ visible: measurementGroup.visible
402
+ });
403
+
404
+ // Update to the end point
405
+ currentMeasurement.update(endPoint, sceneRotation);
406
+
407
+ console.log('[MeasurementManager] Measurement updated to endpoint');
408
+
409
+ // Store the fadeout time if provided for the fade animation
410
+ if (fadeoutTime !== undefined) {
411
+ receivedFadeoutTime = fadeoutTime;
412
+ }
413
+
414
+ // Schedule auto-fade with the received timing properties
415
+ const delay = autoHideDelay ?? props.autoHideDelay;
416
+ console.log(
417
+ '[MeasurementManager] Scheduling auto-fade with delay:',
418
+ delay,
419
+ 'fadeTime:',
420
+ fadeoutTime ?? props.fadeoutTime
421
+ );
422
+
423
+ autoHideTimeoutId = setTimeout(() => {
424
+ // Start the fade animation
425
+ fadeStartTime = performance.now();
426
+ isFading = true;
427
+ }, delay);
428
+
429
+ console.log('[MeasurementManager] Measurement auto-fade scheduled');
430
+ }
431
+
432
+ // Export the methods for use by parent components
433
+ export {
434
+ startMeasurement,
435
+ updateMeasurement,
436
+ finishMeasurement,
437
+ clearMeasurement,
438
+ updatePreview,
439
+ hidePreview,
440
+ showPreviewIndicator,
441
+ displayReceivedMeasurement
442
+ };
443
+ </script>
444
+
445
+ <!-- Measurement Group -->
446
+ <!-- Always visible to show received measurements in playfield -->
447
+ <T.Group
448
+ bind:ref={measurementGroup}
449
+ visible={true}
450
+ layers={[SceneLayer.Overlay]}
451
+ renderOrder={SceneLayerOrder.Measurement}
452
+ >
453
+ <!-- Measurement objects will be added here dynamically -->
454
+ </T.Group>
455
+
456
+ <!-- Preview indicator -->
457
+ <T.Mesh
458
+ bind:ref={previewMesh}
459
+ visible={showPreview && visible}
460
+ layers={[SceneLayer.Overlay]}
461
+ renderOrder={SceneLayerOrder.Measurement}
462
+ >
463
+ <T.MeshBasicMaterial
464
+ bind:ref={previewMaterial}
465
+ transparent={true}
466
+ opacity={props?.opacity ?? 1}
467
+ side={THREE.DoubleSide}
468
+ depthWrite={false}
469
+ depthTest={false}
470
+ toneMapped={false}
471
+ />
472
+ <T.PlaneGeometry bind:ref={previewGeometry} args={[previewSize, previewSize]} />
473
+ </T.Mesh>