@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,79 @@
1
+ export enum GridType {
2
+ Square = 0,
3
+ Hex = 1
4
+ }
5
+
6
+ export enum GridMode {
7
+ FillSpace = 0,
8
+ MapDefined = 1
9
+ }
10
+
11
+ export interface GridLayerProps {
12
+ /**
13
+ * The type of grid. 0 for square, 1 for hex
14
+ */
15
+ gridType: GridType;
16
+
17
+ /**
18
+ * The grid calculation mode. FillSpace fills display with 1-inch squares, MapDefined uses exact grid dimensions
19
+ */
20
+ gridMode: GridMode;
21
+
22
+ /**
23
+ * Fixed grid dimensions when using MapDefined mode
24
+ */
25
+ fixedGridCount: {
26
+ x: number;
27
+ y: number;
28
+ };
29
+
30
+ /**
31
+ * The opacity of the grid lines (0 to 1)
32
+ */
33
+ opacity: number;
34
+
35
+ /**
36
+ * The grid spacing in inches
37
+ */
38
+ spacing: number;
39
+
40
+ /**
41
+ * The real-world size of one grid square
42
+ */
43
+ worldGridSize: number;
44
+
45
+ /**
46
+ * The units for the world grid size (e.g., 'ft', 'm', 'yd')
47
+ */
48
+ worldGridUnits: string;
49
+
50
+ /**
51
+ * Line thickness
52
+ */
53
+ lineThickness: number;
54
+
55
+ /**
56
+ * Line color represented as a hex string (e.g. 0x60A3FE)
57
+ */
58
+ lineColor: string;
59
+
60
+ /**
61
+ * Opacity of the drop shadow
62
+ */
63
+ shadowOpacity: number;
64
+
65
+ /**
66
+ * Blur factor for the drop shadow
67
+ */
68
+ shadowBlur: number;
69
+
70
+ /**
71
+ * Spread amount for the drop shadow
72
+ */
73
+ shadowSpread: number;
74
+
75
+ /**
76
+ * Shadow represented as a hex string (e.g. 0x60A3FE)
77
+ */
78
+ shadowColor: string;
79
+ }
@@ -0,0 +1,300 @@
1
+ <script lang="ts">
2
+ import * as THREE from 'three';
3
+ import { onDestroy, onMount } from 'svelte';
4
+ import { useThrelte } from '@threlte/core';
5
+ import type { Size } from '../../types';
6
+ import { SceneLayer } from '../Scene/types';
7
+
8
+ interface Props {
9
+ id?: string;
10
+ isActive: boolean;
11
+ target?: THREE.Mesh;
12
+ layerSize?: Size | null;
13
+ onMouseDown?: (e: MouseEvent | TouchEvent, coords: THREE.Vector2 | null) => void;
14
+ onMouseUp?: (e: MouseEvent | TouchEvent, coords: THREE.Vector2 | null) => void;
15
+ onMouseMove?: (e: MouseEvent | TouchEvent, coords: THREE.Vector2 | null) => void;
16
+ onContextMenu?: (e: MouseEvent | TouchEvent, coords: THREE.Vector2 | null) => void;
17
+ onMouseEnter?: () => void;
18
+ onMouseLeave?: () => void;
19
+ onWheel?: (e: WheelEvent) => void;
20
+ }
21
+
22
+ let {
23
+ id,
24
+ layerSize,
25
+ isActive,
26
+ target,
27
+ onMouseDown,
28
+ onMouseUp,
29
+ onMouseMove,
30
+ onMouseEnter,
31
+ onMouseLeave,
32
+ onWheel,
33
+ onContextMenu
34
+ }: Props = $props();
35
+
36
+ const { camera, renderer, size } = useThrelte();
37
+
38
+ let raycaster = new THREE.Raycaster();
39
+ raycaster.layers.enable(SceneLayer.Overlay);
40
+ raycaster.layers.enable(SceneLayer.Main);
41
+ raycaster.layers.enable(SceneLayer.Input);
42
+
43
+ function isTouchDevice() {
44
+ return window.matchMedia('(any-pointer: coarse)').matches;
45
+ }
46
+
47
+ // Track pending touch to detect multi-touch gestures
48
+ let touchStartTimer: ReturnType<typeof setTimeout> | null = null;
49
+ let pendingTouchEvent: TouchEvent | null = null;
50
+ let pendingTouchCoords: THREE.Vector2 | null = null;
51
+ let isDrawing = false;
52
+
53
+ // Bind events to the renderer's canvas element
54
+ onMount(() => {
55
+ // Mouse events
56
+ if (onMouseDown) renderer.domElement.addEventListener('mousedown', handleMouseDown);
57
+ if (onMouseMove) renderer.domElement.addEventListener('mousemove', handleMouseMove);
58
+ if (onMouseUp) renderer.domElement.addEventListener('mouseup', handleMouseUp);
59
+ if (onMouseEnter) renderer.domElement.addEventListener('mouseenter', handleMouseEnter);
60
+ if (onMouseLeave) renderer.domElement.addEventListener('mouseleave', handleMouseLeave);
61
+ if (onContextMenu) renderer.domElement.addEventListener('contextmenu', handleContextMenu);
62
+ if (onWheel) renderer.domElement.addEventListener('wheel', handleWheel);
63
+
64
+ // Touch events
65
+ if (onMouseDown) renderer.domElement.addEventListener('touchstart', handleTouchStart);
66
+ if (onMouseMove) renderer.domElement.addEventListener('touchmove', handleTouchMove);
67
+ if (onMouseUp) renderer.domElement.addEventListener('touchend', handleTouchEnd);
68
+ if (onMouseLeave) renderer.domElement.addEventListener('touchcancel', handleTouchCancel);
69
+ });
70
+
71
+ onDestroy(() => {
72
+ // Clean up any pending timers
73
+ if (touchStartTimer) {
74
+ clearTimeout(touchStartTimer);
75
+ touchStartTimer = null;
76
+ }
77
+
78
+ // Mouse events
79
+ if (onMouseDown) renderer.domElement.removeEventListener('mousedown', handleMouseDown);
80
+ if (onMouseMove) renderer.domElement.removeEventListener('mousemove', handleMouseMove);
81
+ if (onMouseUp) renderer.domElement.removeEventListener('mouseup', handleMouseUp);
82
+ if (onMouseEnter) renderer.domElement.removeEventListener('mouseenter', handleMouseEnter);
83
+ if (onMouseLeave) renderer.domElement.removeEventListener('mouseleave', handleMouseLeave);
84
+ if (onContextMenu) renderer.domElement.removeEventListener('contextmenu', handleContextMenu);
85
+ if (onWheel) renderer.domElement.removeEventListener('wheel', handleWheel);
86
+
87
+ // Touch events
88
+ if (onMouseDown) renderer.domElement.removeEventListener('touchstart', handleTouchStart);
89
+ if (onMouseMove) renderer.domElement.removeEventListener('touchmove', handleTouchMove);
90
+ if (onMouseUp) renderer.domElement.removeEventListener('touchend', handleTouchEnd);
91
+ if (onMouseLeave) renderer.domElement.removeEventListener('touchcancel', handleTouchCancel);
92
+ });
93
+
94
+ export function getId() {
95
+ return id;
96
+ }
97
+
98
+ // Internal event handler methods for mouse
99
+ function handleMouseDown(event: MouseEvent) {
100
+ if (onMouseDown && isActive) {
101
+ onMouseDown(event, mouseToCanvasCoords(event));
102
+ }
103
+ }
104
+
105
+ function handleMouseUp(event: MouseEvent) {
106
+ if (onMouseUp && isActive) {
107
+ onMouseUp(event, mouseToCanvasCoords(event));
108
+ }
109
+ }
110
+
111
+ function handleMouseMove(event: MouseEvent) {
112
+ if (onMouseMove && isActive) {
113
+ onMouseMove(event, mouseToCanvasCoords(event));
114
+ }
115
+ }
116
+
117
+ function handleMouseEnter() {
118
+ if (onMouseEnter && isActive) {
119
+ onMouseEnter();
120
+ }
121
+ }
122
+
123
+ function handleMouseLeave() {
124
+ if (onMouseLeave && isActive) {
125
+ onMouseLeave();
126
+ }
127
+ }
128
+
129
+ function handleContextMenu(event: MouseEvent) {
130
+ if (onContextMenu && isActive) {
131
+ onContextMenu(event, mouseToCanvasCoords(event));
132
+ }
133
+ }
134
+
135
+ function handleWheel(event: WheelEvent) {
136
+ if (onWheel && isActive) {
137
+ onWheel(event);
138
+ }
139
+ }
140
+
141
+ function handleTouchStart(event: TouchEvent) {
142
+ if (onMouseDown && isActive) {
143
+ // If multiple touches detected immediately, cancel any pending touch
144
+ if (event.touches.length > 1) {
145
+ // Cancel pending touch if it exists
146
+ if (touchStartTimer) {
147
+ clearTimeout(touchStartTimer);
148
+ touchStartTimer = null;
149
+ pendingTouchEvent = null;
150
+ pendingTouchCoords = null;
151
+ }
152
+ // Stop any ongoing drawing
153
+ if (isDrawing && onMouseUp) {
154
+ onMouseUp(event, null);
155
+ isDrawing = false;
156
+ }
157
+ return;
158
+ }
159
+
160
+ event.preventDefault(); // Prevent scrolling when interacting with the canvas
161
+ const touch = event.touches[0];
162
+ const coords = touchToCanvasCoords(touch);
163
+
164
+ // Store the touch event and wait to see if another finger touches
165
+ pendingTouchEvent = event;
166
+ pendingTouchCoords = coords;
167
+
168
+ // Clear any existing timer
169
+ if (touchStartTimer) {
170
+ clearTimeout(touchStartTimer);
171
+ }
172
+
173
+ // Wait 50ms to see if this becomes a multi-touch gesture
174
+ touchStartTimer = setTimeout(() => {
175
+ // If we still only have one touch after the delay, start drawing
176
+ if (pendingTouchEvent && pendingTouchCoords && event.touches.length === 1) {
177
+ onMouseDown(pendingTouchEvent, pendingTouchCoords);
178
+ isDrawing = true;
179
+ }
180
+ touchStartTimer = null;
181
+ pendingTouchEvent = null;
182
+ pendingTouchCoords = null;
183
+ }, 50);
184
+ }
185
+ }
186
+
187
+ function handleTouchEnd(event: TouchEvent) {
188
+ if (onMouseUp && isActive && isTouchDevice()) {
189
+ event.preventDefault();
190
+
191
+ // Cancel any pending touch start
192
+ if (touchStartTimer) {
193
+ clearTimeout(touchStartTimer);
194
+ touchStartTimer = null;
195
+ pendingTouchEvent = null;
196
+ pendingTouchCoords = null;
197
+ }
198
+
199
+ // Only trigger mouse up if we were actually drawing
200
+ if (isDrawing) {
201
+ const touch = event.changedTouches[0];
202
+ onMouseUp(event, touch ? touchToCanvasCoords(touch) : null);
203
+ isDrawing = false;
204
+ }
205
+ }
206
+ }
207
+
208
+ function handleTouchMove(event: TouchEvent) {
209
+ if (onMouseMove && isActive && isTouchDevice()) {
210
+ // Stop drawing if multiple touches detected (pinch/zoom gesture)
211
+ if (event.touches.length > 1) {
212
+ // Cancel any pending touch
213
+ if (touchStartTimer) {
214
+ clearTimeout(touchStartTimer);
215
+ touchStartTimer = null;
216
+ pendingTouchEvent = null;
217
+ pendingTouchCoords = null;
218
+ }
219
+ // Stop any ongoing drawing
220
+ if (isDrawing && onMouseUp) {
221
+ onMouseUp(event, null);
222
+ isDrawing = false;
223
+ }
224
+ return;
225
+ }
226
+
227
+ // Only process move if we're actually drawing
228
+ if (isDrawing) {
229
+ event.preventDefault();
230
+ const touch = event.touches[0];
231
+ onMouseMove(event, touchToCanvasCoords(touch));
232
+ }
233
+ }
234
+ }
235
+
236
+ function handleTouchCancel() {
237
+ // Clean up any pending or ongoing touch operations
238
+ if (touchStartTimer) {
239
+ clearTimeout(touchStartTimer);
240
+ touchStartTimer = null;
241
+ pendingTouchEvent = null;
242
+ pendingTouchCoords = null;
243
+ }
244
+
245
+ if (isDrawing) {
246
+ isDrawing = false;
247
+ }
248
+
249
+ if (onMouseLeave && isActive) {
250
+ onMouseLeave();
251
+ }
252
+ }
253
+
254
+ function mouseToCanvasCoords(e: MouseEvent): THREE.Vector2 | null {
255
+ if (!target || !layerSize) return null;
256
+
257
+ const mouse = new THREE.Vector2((e.offsetX / $size.width) * 2 - 1, -(e.offsetY / $size.height) * 2 + 1);
258
+
259
+ raycaster.setFromCamera(mouse, $camera);
260
+ const intersects = raycaster.intersectObject(target);
261
+
262
+ if (intersects.length > 0) {
263
+ const { point } = intersects[0];
264
+ const localPoint = target.worldToLocal(point);
265
+ const canvasPoint = new THREE.Vector2(
266
+ layerSize.width * (localPoint.x + 0.5),
267
+ layerSize.height * (localPoint.y + 0.5)
268
+ );
269
+ return canvasPoint;
270
+ } else {
271
+ return null;
272
+ }
273
+ }
274
+
275
+ function touchToCanvasCoords(touch: Touch): THREE.Vector2 | null {
276
+ if (!target || !layerSize || !renderer.domElement) return null;
277
+
278
+ const rect = renderer.domElement.getBoundingClientRect();
279
+
280
+ const offsetX = touch.clientX - rect.left;
281
+ const offsetY = touch.clientY - rect.top;
282
+
283
+ const mouse = new THREE.Vector2((offsetX / $size.width) * 2 - 1, -(offsetY / $size.height) * 2 + 1);
284
+
285
+ raycaster.setFromCamera(mouse, $camera);
286
+ const intersects = raycaster.intersectObject(target);
287
+
288
+ if (intersects.length > 0) {
289
+ const { point } = intersects[0];
290
+ const localPoint = target.worldToLocal(point);
291
+ const canvasPoint = new THREE.Vector2(
292
+ layerSize.width * (localPoint.x + 0.5),
293
+ layerSize.height * (localPoint.y + 0.5)
294
+ );
295
+ return canvasPoint;
296
+ } else {
297
+ return null;
298
+ }
299
+ }
300
+ </script>
@@ -0,0 +1,196 @@
1
+ <script lang="ts">
2
+ import * as THREE from 'three';
3
+ import { T } from '@threlte/core';
4
+ import { MapLayerType } from './types';
5
+ import type { Size } from '../../types';
6
+ import FogOfWarLayer from '../FogOfWarLayer/FogOfWarLayer.svelte';
7
+ import type { FogOfWarExports } from '../FogOfWarLayer/types';
8
+ import type { Callbacks, StageProps } from '../Stage/types';
9
+ import { getContext } from 'svelte';
10
+ import { SceneLayer, SceneLayerOrder } from '../Scene/types';
11
+ import FogLayer from '../FogLayer/FogLayer.svelte';
12
+ import { createDataSource, type IMapDataSource } from './dataSources';
13
+
14
+ interface Props {
15
+ props: StageProps;
16
+ onMapLoading: () => void;
17
+ onMapLoaded: (mapUrl: string, mapSize: Size) => void;
18
+ }
19
+
20
+ const { props, onMapLoading, onMapLoaded }: Props = $props();
21
+
22
+ const callbacks = getContext<Callbacks>('callbacks');
23
+ const onMapUpdate = callbacks.onMapUpdate;
24
+
25
+ let currentMapUrl: string | null = $state(null);
26
+ let dataSource: IMapDataSource | null = null;
27
+ let mapImageMaterial = new THREE.MeshBasicMaterial();
28
+ let fogOfWarLayer: FogOfWarExports;
29
+
30
+ // The size of the map image
31
+ let mapSize: Size | null = $state(null);
32
+
33
+ // Track if the material needs a full update
34
+ let materialUpdateKey = $state(0);
35
+
36
+ $effect(() => {
37
+ if (!props.map.url) {
38
+ currentMapUrl = props.map.url;
39
+ // Dispose of data source when URL is null
40
+ if (dataSource) {
41
+ dataSource.dispose();
42
+ dataSource = null;
43
+ }
44
+ return;
45
+ }
46
+
47
+ // Check if the URL is changing (including query params for cache busting)
48
+ // For video files, we need to check the full URL including query params
49
+ const newMapUrlWithoutParams = getUrlWithoutParams(props.map.url);
50
+ const currentMapUrlWithoutParams = getUrlWithoutParams(currentMapUrl);
51
+
52
+ // Check if this is a video file
53
+ const isVideo = props.map.url.match(/\.(mp4|webm|mov|avi)/i);
54
+
55
+ // For videos, compare full URLs (including query params) to ensure cache busting
56
+ // For images, compare without params to avoid unnecessary reloads
57
+ if (isVideo) {
58
+ if (currentMapUrl === props.map.url) {
59
+ return;
60
+ }
61
+ } else {
62
+ if (currentMapUrlWithoutParams === newMapUrlWithoutParams) {
63
+ return;
64
+ }
65
+ }
66
+
67
+ // Update the current URL immediately
68
+ currentMapUrl = props.map.url;
69
+
70
+ onMapLoading();
71
+
72
+ // Dispose of previous data source
73
+ if (dataSource) {
74
+ dataSource.dispose();
75
+ dataSource = null;
76
+ }
77
+
78
+ // Dispose of previous material map
79
+ if (mapImageMaterial.map) {
80
+ mapImageMaterial.map.dispose();
81
+ mapImageMaterial.map = null;
82
+ mapImageMaterial.needsUpdate = true;
83
+ }
84
+
85
+ // Create new data source based on file type
86
+ dataSource = createDataSource(props.map.url);
87
+
88
+ // Load the new data source
89
+ dataSource
90
+ .load(props.map.url)
91
+ .then(() => {
92
+ const texture = dataSource?.getTexture();
93
+ const size = dataSource?.getSize();
94
+
95
+ if (texture && size) {
96
+ mapImageMaterial.map = texture;
97
+ mapImageMaterial.needsUpdate = true;
98
+ mapSize = size;
99
+ // Force material key update to trigger re-render
100
+ materialUpdateKey++;
101
+ onMapLoaded(props.map.url, mapSize);
102
+ }
103
+ })
104
+ .catch((reason) => {
105
+ console.error('Failed to load map:', reason);
106
+ });
107
+ });
108
+
109
+ function getUrlWithoutParams(url: string | null): string {
110
+ if (!url) return '';
111
+ return url.split('?')[0];
112
+ }
113
+
114
+ export function getCompositeMapTexture(): THREE.Texture | null {
115
+ return mapImageMaterial.map;
116
+ }
117
+
118
+ export function fill() {
119
+ if (!mapSize) return;
120
+
121
+ const imageAspectRatio = mapSize.width / mapSize.height;
122
+ const sceneAspectRatio = props.display.resolution.x / props.display.resolution.y;
123
+
124
+ let newZoom: number;
125
+ if (imageAspectRatio > sceneAspectRatio) {
126
+ newZoom = props.display.resolution.y / mapSize.height;
127
+ } else {
128
+ newZoom = props.display.resolution.x / mapSize.width;
129
+ }
130
+
131
+ onMapUpdate({ x: 0, y: 0 }, newZoom);
132
+ }
133
+
134
+ export function fit() {
135
+ if (!mapSize) return;
136
+
137
+ const imageAspectRatio = mapSize.width / mapSize.height;
138
+ const sceneAspectRatio = props.display.resolution.x / props.display.resolution.y;
139
+
140
+ let newZoom: number;
141
+ if (imageAspectRatio > sceneAspectRatio) {
142
+ newZoom = props.display.resolution.x / mapSize.width;
143
+ } else {
144
+ newZoom = props.display.resolution.y / mapSize.height;
145
+ }
146
+
147
+ onMapUpdate({ x: 0, y: 0 }, newZoom);
148
+ }
149
+
150
+ // Export the map size for external components
151
+ export { mapSize };
152
+
153
+ // References to the layer doesn't exist until the component is mounted,
154
+ // so we need create these wrapper functions
155
+ export const fogOfWar = {
156
+ clear: () => fogOfWarLayer.clearFog(),
157
+ reset: () => fogOfWarLayer.resetFog(),
158
+ toPng: () => fogOfWarLayer.toPng(),
159
+ toRLE: () => fogOfWarLayer.toRLE(),
160
+ fromRLE: (rleData: Uint8Array, width: number, height: number) => fogOfWarLayer.fromRLE(rleData, width, height),
161
+ isDrawing: () => fogOfWarLayer?.isDrawing() ?? false
162
+ };
163
+ </script>
164
+
165
+ <!-- Map -->
166
+ <T.Object3D
167
+ name="mapLayer"
168
+ position={[props.map.offset.x, props.map.offset.y, 0]}
169
+ rotation.z={(props.map.rotation / 180.0) * Math.PI}
170
+ scale={[(mapSize?.width ?? 0) * props.map.zoom, (mapSize?.height ?? 0) * props.map.zoom, 1]}
171
+ >
172
+ <!-- Map image -->
173
+ {#key materialUpdateKey}
174
+ <T.Mesh name="mapImage" layers={[SceneLayer.Main]} renderOrder={SceneLayerOrder.Map} visible={true}>
175
+ <T.MeshBasicMaterial is={mapImageMaterial} />
176
+ <T.PlaneGeometry />
177
+ </T.Mesh>
178
+ {/key}
179
+
180
+ <FogLayer
181
+ props={props.fog}
182
+ {mapSize}
183
+ layers={[SceneLayer.Main]}
184
+ renderOrder={SceneLayerOrder.Fog}
185
+ visible={props.fog.enabled}
186
+ />
187
+
188
+ <FogOfWarLayer
189
+ bind:this={fogOfWarLayer}
190
+ props={props.fogOfWar}
191
+ isActive={props.activeLayer === MapLayerType.FogOfWar}
192
+ {mapSize}
193
+ layers={[SceneLayer.Main]}
194
+ renderOrder={SceneLayerOrder.FogOfWar}
195
+ />
196
+ </T.Object3D>