@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,265 @@
1
+ import { decompressFrames, parseGIF } from 'gifuct-js';
2
+ import * as THREE from 'three';
3
+ import type { Size } from '../../../types';
4
+ import type { IMapDataSource } from './IMapDataSource';
5
+
6
+ interface GifFrame {
7
+ dims: {
8
+ width: number;
9
+ height: number;
10
+ left: number;
11
+ top: number;
12
+ };
13
+ patch: Uint8ClampedArray;
14
+ delay: number;
15
+ disposalType?: number;
16
+ }
17
+
18
+ /**
19
+ * Data source for loading and animating GIF images as textures in a 3D scene.
20
+ *
21
+ * This class implements the IMapDataSource interface to provide GIF animation
22
+ * capabilities for map layers. It handles GIF parsing, frame decompression,
23
+ * and real-time animation rendering using HTML5 Canvas and Three.js textures.
24
+ */
25
+ export class GifDataSource implements IMapDataSource {
26
+ /** Canvas element used for rendering the GIF frames */
27
+ private gifCanvas: HTMLCanvasElement | null = null;
28
+ /** 2D rendering context for the main GIF canvas */
29
+ private gifCtx: CanvasRenderingContext2D | null = null;
30
+ /** Temporary canvas used for patch rendering of individual frames */
31
+ private tempCanvas: HTMLCanvasElement | null = null;
32
+ /** 2D rendering context for the temporary canvas */
33
+ private tempCtx: CanvasRenderingContext2D | null = null;
34
+ /** Three.js texture created from the GIF canvas */
35
+ private texture: THREE.CanvasTexture | null = null;
36
+ /** Dimensions of the GIF image */
37
+ private size: Size | null = null;
38
+ /** Array of decompressed GIF frames */
39
+ private frames: GifFrame[] = [];
40
+ /** Index of the currently displayed frame */
41
+ private currentFrameIndex = 0;
42
+ /** Animation frame ID for canceling the animation loop */
43
+ private animationId: number | null = null;
44
+ /** Timestamp when the animation started for timing calculations */
45
+ private startTime = 0;
46
+ /** Timestamp of the last frame render */
47
+ private lastFrameTime = 0;
48
+ /** The total time the animation has been playing */
49
+ private totalPlayTime = 0;
50
+
51
+ /**
52
+ * Gets the Three.js texture containing the animated GIF.
53
+ *
54
+ * @returns The canvas texture with the current frame, or null if not loaded
55
+ */
56
+ getTexture(): THREE.Texture | null {
57
+ return this.texture;
58
+ }
59
+
60
+ /**
61
+ * Gets the dimensions of the GIF image.
62
+ *
63
+ * @returns The width and height of the GIF, or null if not loaded
64
+ */
65
+ getSize(): Size | null {
66
+ return this.size;
67
+ }
68
+
69
+ /**
70
+ * Loads and initializes a GIF from a URL.
71
+ *
72
+ * This method fetches the GIF data, parses it, decompresses the frames,
73
+ * sets up the rendering canvases, and starts the animation loop.
74
+ *
75
+ * @param url - The URL of the GIF file to load
76
+ * @returns A promise that resolves when the GIF is loaded and ready
77
+ * @throws Error if the GIF cannot be fetched, parsed, or has no frames
78
+ */
79
+ async load(url: string): Promise<void> {
80
+ // Dispose of previous resources
81
+ this.dispose();
82
+
83
+ return new Promise(async (resolve, reject) => {
84
+ try {
85
+ // Fetch the GIF data
86
+ const response = await fetch(url);
87
+ const arrayBuffer = await response.arrayBuffer();
88
+
89
+ // Parse the GIF
90
+ const gif = parseGIF(arrayBuffer);
91
+ this.frames = decompressFrames(gif, true);
92
+
93
+ if (this.frames.length === 0) {
94
+ throw new Error('No frames found in GIF');
95
+ }
96
+
97
+ // Get dimensions from the first frame
98
+ const firstFrame = this.frames[0];
99
+ this.size = {
100
+ width: firstFrame.dims.width,
101
+ height: firstFrame.dims.height
102
+ };
103
+
104
+ // Create canvas for rendering
105
+ this.gifCanvas = document.createElement('canvas');
106
+ this.gifCanvas.width = this.size.width;
107
+ this.gifCanvas.height = this.size.height;
108
+
109
+ // Create temporary canvas for patch rendering
110
+ this.tempCanvas = document.createElement('canvas');
111
+ this.tempCtx = this.tempCanvas.getContext('2d')!;
112
+
113
+ // Create texture from canvas
114
+ this.texture = new THREE.CanvasTexture(this.gifCanvas);
115
+ this.texture.colorSpace = THREE.SRGBColorSpace;
116
+
117
+ // Start animation
118
+ this.startAnimation();
119
+
120
+ resolve();
121
+ } catch (error) {
122
+ console.error('Failed to load GIF:', error);
123
+ reject(error);
124
+ }
125
+ });
126
+ }
127
+
128
+ /**
129
+ * Starts the GIF animation loop.
130
+ *
131
+ * This method initializes the animation by setting up the rendering context,
132
+ * starting the timing mechanism, and beginning the animation frame loop.
133
+ * The animation automatically loops when all frames have been displayed.
134
+ */
135
+ private startAnimation(): void {
136
+ if (!this.gifCanvas || this.frames.length === 0) return;
137
+
138
+ this.gifCtx = this.gifCanvas.getContext('2d')!;
139
+ this.startTime = performance.now();
140
+ this.lastFrameTime = this.startTime;
141
+ this.currentFrameIndex = -1;
142
+ this.totalPlayTime = 0;
143
+
144
+ const animate = () => {
145
+ this.animationId = requestAnimationFrame(animate);
146
+
147
+ const currentTime = performance.now();
148
+ const elapsedTime = currentTime - this.lastFrameTime;
149
+ this.lastFrameTime = currentTime;
150
+
151
+ const currentFrame = this.frames[this.currentFrameIndex];
152
+ const frameDelay = currentFrame?.delay ?? 0;
153
+
154
+ this.totalPlayTime += elapsedTime;
155
+
156
+ // If the current frame has played for its duration, advance to the next frame
157
+ if (this.totalPlayTime >= frameDelay) {
158
+ this.totalPlayTime -= frameDelay;
159
+ this.currentFrameIndex++;
160
+
161
+ // Loop back to the beginning
162
+ if (this.currentFrameIndex >= this.frames.length) {
163
+ this.currentFrameIndex = 0;
164
+ }
165
+
166
+ this.renderFrame(this.currentFrameIndex);
167
+
168
+ // Update texture
169
+ if (this.texture) {
170
+ this.texture.needsUpdate = true;
171
+ }
172
+ }
173
+ };
174
+
175
+ // Start the animation loop
176
+ this.animationId = requestAnimationFrame(animate);
177
+ }
178
+
179
+ /**
180
+ * Renders a specific frame of the GIF animation.
181
+ *
182
+ * This method handles frame disposal methods and delegates the actual
183
+ * drawing to the drawPatch method.
184
+ *
185
+ * @param frameIndex - The index of the frame to render
186
+ */
187
+ private renderFrame(frameIndex: number): void {
188
+ const frame = this.frames[frameIndex];
189
+
190
+ // Clear canvas if disposal method requires it (like the example)
191
+ if (frame.disposalType === 2) {
192
+ this.gifCtx!.clearRect(0, 0, this.gifCanvas!.width, this.gifCanvas!.height);
193
+ }
194
+
195
+ // Draw the patch using the temporary canvas approach
196
+ this.drawPatch(frame);
197
+ }
198
+
199
+ /**
200
+ * Draws a frame patch onto the main canvas.
201
+ *
202
+ * This method uses a temporary canvas to efficiently render individual
203
+ * frame patches at their correct positions within the GIF.
204
+ *
205
+ * @param frame - The frame object containing patch data and dimensions
206
+ */
207
+ private drawPatch(frame: GifFrame): void {
208
+ const dims = frame.dims;
209
+
210
+ // Resize temp canvas if needed
211
+ if (!this.tempCanvas || dims.width !== this.tempCanvas.width || dims.height !== this.tempCanvas.height) {
212
+ this.tempCanvas!.width = dims.width;
213
+ this.tempCanvas!.height = dims.height;
214
+ }
215
+
216
+ // Create ImageData for the patch
217
+ const frameImageData = this.tempCtx!.createImageData(dims.width, dims.height);
218
+
219
+ // Set the patch data
220
+ frameImageData.data.set(frame.patch);
221
+
222
+ // Put the patch data on the temp canvas
223
+ this.tempCtx!.putImageData(frameImageData, 0, 0);
224
+
225
+ // Draw the temp canvas onto the main canvas at the correct position
226
+ this.gifCtx!.drawImage(this.tempCanvas!, dims.left, dims.top);
227
+ }
228
+
229
+ /**
230
+ * Disposes of all resources used by this data source.
231
+ *
232
+ * This method stops the animation loop, disposes of the Three.js texture,
233
+ * clears all canvas references, and resets internal state. It should be
234
+ * called before loading a new GIF or when the data source is no longer needed.
235
+ */
236
+ dispose(): void {
237
+ // Stop animation
238
+ if (this.animationId) {
239
+ cancelAnimationFrame(this.animationId);
240
+ this.animationId = null;
241
+ }
242
+
243
+ // Dispose of texture
244
+ if (this.texture) {
245
+ this.texture.dispose();
246
+ this.texture = null;
247
+ }
248
+
249
+ // Clear canvas
250
+ if (this.gifCanvas) {
251
+ this.gifCanvas = null;
252
+ }
253
+
254
+ // Clear temp canvas
255
+ if (this.tempCanvas) {
256
+ this.tempCanvas = null;
257
+ this.tempCtx = null;
258
+ }
259
+
260
+ // Clear frames
261
+ this.frames = [];
262
+ this.currentFrameIndex = 0;
263
+ this.size = null;
264
+ }
265
+ }
@@ -0,0 +1,55 @@
1
+ import type * as THREE from 'three';
2
+ import type { Size } from '../../../types';
3
+
4
+ /**
5
+ * Interface for map data sources that provide textures for map layers.
6
+ *
7
+ * This interface defines the contract that all map data sources must implement.
8
+ * It provides a unified way to handle different types of media (images, videos, GIFs)
9
+ * as textures in a 3D map environment.
10
+ */
11
+ export interface IMapDataSource {
12
+ /**
13
+ * Gets the texture to be displayed on the map plane.
14
+ *
15
+ * This can be a static texture, a video texture, or a canvas texture
16
+ * depending on the implementation. The texture is used to render the
17
+ * data source on a 3D plane in the scene.
18
+ *
19
+ * @returns The texture to be displayed on the map plane, or null if not loaded
20
+ */
21
+ getTexture(): THREE.Texture | null;
22
+
23
+ /**
24
+ * Gets the dimensions of the map source.
25
+ *
26
+ * Returns the width and height of the data source, which is used
27
+ * to properly size and position the map layer in the 3D scene.
28
+ *
29
+ * @returns The dimensions of the map source, or null if not loaded
30
+ */
31
+ getSize(): Size | null;
32
+
33
+ /**
34
+ * Loads the data source from a URL.
35
+ *
36
+ * This method handles the loading of different media types (images, videos, GIFs)
37
+ * from a given URL. The implementation should handle any necessary parsing,
38
+ * decompression, or setup required for the specific media type.
39
+ *
40
+ * @param url - The URL of the data source to load
41
+ * @returns A promise that resolves when the data source is loaded and ready
42
+ * @throws Error if the data source cannot be loaded or processed
43
+ */
44
+ load(url: string): Promise<void>;
45
+
46
+ /**
47
+ * Cleans up resources used by the data source.
48
+ *
49
+ * This method should dispose of any textures, canvases, video elements,
50
+ * or other resources that were created during the loading process.
51
+ * It should be called when the data source is no longer needed to
52
+ * prevent memory leaks.
53
+ */
54
+ dispose(): void;
55
+ }
@@ -0,0 +1,87 @@
1
+ import * as THREE from 'three';
2
+ import type { Size } from '../../../types';
3
+ import type { IMapDataSource } from './IMapDataSource';
4
+
5
+ /**
6
+ * Data source for loading static images as textures in a 3D scene.
7
+ *
8
+ * This class implements the IMapDataSource interface to provide static image
9
+ * loading capabilities for map layers. It uses Three.js TextureLoader to load
10
+ * various image formats and create textures suitable for 3D rendering.
11
+ */
12
+ export class ImageDataSource implements IMapDataSource {
13
+ /** The loaded texture from the image file */
14
+ private texture: THREE.Texture | null = null;
15
+ /** Dimensions of the loaded image */
16
+ private size: Size | null = null;
17
+ /** Three.js texture loader instance for loading images */
18
+ private loader = new THREE.TextureLoader();
19
+
20
+ /**
21
+ * Gets the Three.js texture containing the loaded image.
22
+ *
23
+ * @returns The texture with the loaded image, or null if not loaded
24
+ */
25
+ getTexture(): THREE.Texture | null {
26
+ return this.texture;
27
+ }
28
+
29
+ /**
30
+ * Gets the dimensions of the loaded image.
31
+ *
32
+ * @returns The width and height of the image, or null if not loaded
33
+ */
34
+ getSize(): Size | null {
35
+ return this.size;
36
+ }
37
+
38
+ /**
39
+ * Loads an image from a URL and creates a texture.
40
+ *
41
+ * This method uses Three.js TextureLoader to load various image formats
42
+ * (JPG, PNG, WebP, etc.) and creates a texture with proper color space
43
+ * settings for 3D rendering.
44
+ *
45
+ * @param url - The URL of the image file to load
46
+ * @returns A promise that resolves when the image is loaded and ready
47
+ * @throws Error if the image cannot be loaded or processed
48
+ */
49
+ async load(url: string): Promise<void> {
50
+ // Dispose of previous texture if it exists
51
+ this.dispose();
52
+
53
+ return new Promise((resolve, reject) => {
54
+ this.loader.load(
55
+ url,
56
+ (texture) => {
57
+ texture.colorSpace = THREE.SRGBColorSpace;
58
+ this.texture = texture;
59
+ this.size = {
60
+ width: texture.image.width,
61
+ height: texture.image.height
62
+ };
63
+ resolve();
64
+ },
65
+ undefined,
66
+ (error) => {
67
+ console.error('Failed to load image:', error);
68
+ reject(error);
69
+ }
70
+ );
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Disposes of the loaded texture and resets internal state.
76
+ *
77
+ * This method cleans up the Three.js texture to prevent memory leaks
78
+ * and resets the size information.
79
+ */
80
+ dispose(): void {
81
+ if (this.texture) {
82
+ this.texture.dispose();
83
+ this.texture = null;
84
+ }
85
+ this.size = null;
86
+ }
87
+ }
@@ -0,0 +1,150 @@
1
+ import * as THREE from 'three';
2
+ import type { Size } from '../../../types';
3
+ import type { IMapDataSource } from './IMapDataSource';
4
+
5
+ /**
6
+ * Data source for loading and playing video files as textures in a 3D scene.
7
+ *
8
+ * This class implements the IMapDataSource interface to provide video playback
9
+ * capabilities for map layers. It creates an HTML video element, sets up
10
+ * proper video attributes for web playback, and creates a Three.js VideoTexture
11
+ * for real-time video rendering in 3D space.
12
+ */
13
+ export class VideoDataSource implements IMapDataSource {
14
+ /** HTML video element used for video playback */
15
+ private videoElement: HTMLVideoElement | null = null;
16
+ /** Three.js video texture created from the video element */
17
+ private texture: THREE.VideoTexture | null = null;
18
+ /** Dimensions of the video */
19
+ private size: Size | null = null;
20
+
21
+ /**
22
+ * Gets the Three.js video texture containing the playing video.
23
+ *
24
+ * @returns The video texture with the current video frame, or null if not loaded
25
+ */
26
+ getTexture(): THREE.Texture | null {
27
+ return this.texture;
28
+ }
29
+
30
+ /**
31
+ * Gets the dimensions of the loaded video.
32
+ *
33
+ * @returns The width and height of the video, or null if not loaded
34
+ */
35
+ getSize(): Size | null {
36
+ return this.size;
37
+ }
38
+
39
+ /**
40
+ * Loads a video from a URL and starts playback.
41
+ *
42
+ * This method creates an HTML video element with appropriate attributes
43
+ * for web playback (autoplay, muted, loop, etc.), sets up event listeners
44
+ * for loading and error handling, and creates a Three.js VideoTexture
45
+ * for real-time rendering in 3D space.
46
+ *
47
+ * @param url - The URL of the video file to load
48
+ * @returns A promise that resolves when the video is loaded and ready to play
49
+ * @throws Error if the video cannot be loaded or played
50
+ */
51
+ async load(url: string): Promise<void> {
52
+ // Dispose of previous resources
53
+ this.dispose();
54
+
55
+ return new Promise((resolve, reject) => {
56
+ // Create video element
57
+ this.videoElement = document.createElement('video');
58
+ // Set crossOrigin to 'anonymous' for CORS-enabled servers
59
+ // This is required for THREE.VideoTexture to work with cross-origin videos
60
+ this.videoElement.crossOrigin = 'anonymous';
61
+ this.videoElement.loop = true;
62
+ this.videoElement.muted = true;
63
+ this.videoElement.playsInline = true;
64
+ this.videoElement.autoplay = true;
65
+ // Preload the video to ensure it's ready
66
+ this.videoElement.preload = 'auto';
67
+
68
+ // Wait for enough data to be loaded before creating texture
69
+ // 'canplaythrough' ensures the video has enough data buffered
70
+ const handleCanPlay = () => {
71
+ if (this.videoElement && this.videoElement.readyState >= 2) {
72
+ this.size = {
73
+ width: this.videoElement.videoWidth,
74
+ height: this.videoElement.videoHeight
75
+ };
76
+
77
+ // Create video texture only when video is ready
78
+ this.texture = new THREE.VideoTexture(this.videoElement);
79
+ this.texture.colorSpace = THREE.SRGBColorSpace;
80
+ // Set texture parameters for better compatibility
81
+ this.texture.minFilter = THREE.LinearFilter;
82
+ this.texture.magFilter = THREE.LinearFilter;
83
+ this.texture.format = THREE.RGBAFormat;
84
+ // Disable mipmaps to prevent power-of-two issues
85
+ this.texture.generateMipmaps = false;
86
+ // Force texture update on every frame for videos
87
+ this.texture.needsUpdate = true;
88
+
89
+ // Start playback
90
+ this.videoElement.play().catch((e) => {
91
+ console.warn('Video autoplay failed:', e);
92
+ });
93
+
94
+ resolve();
95
+ }
96
+ };
97
+
98
+ // Use canplaythrough for Chrome compatibility
99
+ this.videoElement.addEventListener('canplaythrough', handleCanPlay, { once: true });
100
+
101
+ // Fallback: also listen for loadeddata as some browsers may not fire canplaythrough
102
+ this.videoElement.addEventListener(
103
+ 'loadeddata',
104
+ () => {
105
+ // Give it a small delay to ensure the first frame is ready
106
+ setTimeout(() => {
107
+ if (!this.texture && this.videoElement && this.videoElement.readyState >= 2) {
108
+ handleCanPlay();
109
+ }
110
+ }, 100);
111
+ },
112
+ { once: true }
113
+ );
114
+
115
+ this.videoElement.addEventListener('error', (error) => {
116
+ console.error('Failed to load video:', error);
117
+ reject(error);
118
+ });
119
+
120
+ // Set source with cache busting
121
+ // Force reload by ensuring the URL is unique
122
+ this.videoElement.src = url;
123
+ // Force the browser to reload the video
124
+ this.videoElement.load();
125
+ });
126
+ }
127
+
128
+ /**
129
+ * Disposes of the video element and texture, stopping playback.
130
+ *
131
+ * This method stops video playback, removes the video source,
132
+ * disposes of the Three.js texture, and cleans up all references
133
+ * to prevent memory leaks.
134
+ */
135
+ dispose(): void {
136
+ if (this.videoElement) {
137
+ this.videoElement.pause();
138
+ this.videoElement.removeAttribute('src');
139
+ this.videoElement.load();
140
+ this.videoElement = null;
141
+ }
142
+
143
+ if (this.texture) {
144
+ this.texture.dispose();
145
+ this.texture = null;
146
+ }
147
+
148
+ this.size = null;
149
+ }
150
+ }
@@ -0,0 +1,48 @@
1
+ import { GifDataSource } from './GifDataSource';
2
+ import { ImageDataSource } from './ImageDataSource';
3
+ import type { IMapDataSource } from './IMapDataSource';
4
+ import { VideoDataSource } from './VideoDataSource';
5
+
6
+ /**
7
+ * Factory function that creates the appropriate data source based on file extension.
8
+ *
9
+ * This function analyzes the URL's file extension to determine which type of
10
+ * data source to create. It supports various media formats:
11
+ * - Video formats (mp4, webm, mov, avi) → VideoDataSource
12
+ * - GIF animations → GifDataSource
13
+ * - Image formats (jpg, jpeg, png, webp, bmp, tiff) → ImageDataSource
14
+ * - Unknown formats default to ImageDataSource
15
+ *
16
+ * @param url - The URL of the media file to load
17
+ * @returns An instance of the appropriate data source implementation
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const dataSource = createDataSource('https://example.com/image.jpg');
22
+ * await dataSource.load('https://example.com/image.jpg');
23
+ * ```
24
+ */
25
+ export function createDataSource(url: string): IMapDataSource {
26
+ const urlWithoutParams = url.split('?')[0];
27
+ const extension = urlWithoutParams.split('.').pop()?.toLowerCase() || '';
28
+
29
+ switch (extension) {
30
+ case 'mp4':
31
+ case 'webm':
32
+ case 'mov':
33
+ case 'avi':
34
+ return new VideoDataSource();
35
+
36
+ case 'gif':
37
+ return new GifDataSource();
38
+
39
+ case 'jpg':
40
+ case 'jpeg':
41
+ case 'png':
42
+ case 'webp':
43
+ case 'bmp':
44
+ case 'tiff':
45
+ default:
46
+ return new ImageDataSource();
47
+ }
48
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Map Layer Data Sources Module
3
+ *
4
+ * This module provides a unified interface for loading different types of media
5
+ * as textures in 3D map layers. It includes implementations for static images,
6
+ * video files, and animated GIFs, along with a factory function to automatically
7
+ * select the appropriate data source based on file extension.
8
+ *
9
+ * @module MapLayerDataSources
10
+ */
11
+
12
+ export { createDataSource } from './dataSourceFactory';
13
+ export { GifDataSource } from './GifDataSource';
14
+ export { ImageDataSource } from './ImageDataSource';
15
+ export type { IMapDataSource } from './IMapDataSource';
16
+ export { VideoDataSource } from './VideoDataSource';
@@ -0,0 +1,58 @@
1
+ import * as THREE from 'three';
2
+
3
+ export enum MapLayerType {
4
+ None = 0,
5
+ FogOfWar = 1,
6
+ Marker = 2,
7
+ Annotation = 3,
8
+ Measurement = 4
9
+ }
10
+
11
+ /**
12
+ * Props for the Map layer
13
+ */
14
+ export interface MapLayerProps {
15
+ /**
16
+ * The position of the map relative to the scene
17
+ */
18
+ offset: {
19
+ x: number;
20
+ y: number;
21
+ };
22
+
23
+ /**
24
+ * Rotation of the map relative to the scene in degrees
25
+ */
26
+ rotation: number;
27
+
28
+ /**
29
+ * Url for the map image
30
+ */
31
+ url: string;
32
+
33
+ /**
34
+ * Indicates if the map should autoplay (for video assets)
35
+ * @default true
36
+ */
37
+ autoplay?: boolean;
38
+
39
+ /**
40
+ * The scale of the map relative to the scene
41
+ */
42
+ zoom: number;
43
+ }
44
+
45
+ export interface MapLayerExports {
46
+ getCompositeMapTexture: () => THREE.Texture | null;
47
+ fit: () => void;
48
+ fill: () => void;
49
+ mapSize: { width: number; height: number } | null;
50
+ fogOfWar: {
51
+ clear: () => void;
52
+ reset: () => void;
53
+ toPng: () => Promise<Blob>;
54
+ toRLE: () => Promise<Uint8Array>;
55
+ fromRLE: (rleData: Uint8Array, width: number, height: number) => Promise<void>;
56
+ isDrawing: () => boolean;
57
+ };
58
+ }