@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,364 @@
1
+ <script lang="ts">
2
+ import * as THREE from 'three';
3
+ import { T, useThrelte, useLoader } from '@threlte/core';
4
+ import { DrawMode, type DrawingLayerProps, InitialState } from './types';
5
+ import { onDestroy, untrack } from 'svelte';
6
+ import type { Size } from '../../types';
7
+ import { RenderMode } from './types';
8
+ import { encodeRLE, decodeRLE } from '../../../../utils/rle';
9
+
10
+ import drawVertexShader from '../../shaders/Drawing.vert?raw';
11
+ import drawFragmentShader from '../../shaders/Drawing.frag?raw';
12
+
13
+ interface Props {
14
+ props: DrawingLayerProps;
15
+ size: Size | null;
16
+ initialState: InitialState;
17
+ onRender: (texture: THREE.Texture) => void;
18
+ }
19
+
20
+ const { props, size, initialState, onRender }: Props = $props();
21
+ const { renderer } = useThrelte();
22
+
23
+ // This shader is used for drawing the fog of war on the GPU
24
+ const drawMaterial = new THREE.ShaderMaterial({
25
+ uniforms: {
26
+ uPreviousState: { value: null },
27
+ uBrushTexture: { value: null },
28
+ uStart: { value: new THREE.Vector2(Infinity, Infinity) }, // Initialize off-screen
29
+ uEnd: { value: new THREE.Vector2(Infinity, Infinity) }, // Initialize off-screen
30
+ uBrushSize: { value: props.tool.size },
31
+ uBrushFalloff: { value: 50.0 },
32
+ uTextureSize: { value: new THREE.Vector2() },
33
+ uBrushColor: { value: new THREE.Vector4() },
34
+ uIsRevertOperation: { value: false },
35
+ uIsClearOperation: { value: false },
36
+ uIsFillOperation: { value: false },
37
+ uShapeType: { value: 0 }
38
+ },
39
+ vertexShader: drawVertexShader,
40
+ fragmentShader: drawFragmentShader
41
+ });
42
+
43
+ // Options for the render targets
44
+ const options = {
45
+ format: THREE.RGBAFormat,
46
+ type: THREE.UnsignedByteType,
47
+ minFilter: THREE.LinearMipMapLinearFilter,
48
+ magFilter: THREE.LinearFilter,
49
+ generateMipmaps: true,
50
+ depthBuffer: false,
51
+ alpha: true
52
+ };
53
+
54
+ let imageUrl: string | null = $state(null);
55
+
56
+ const loader = useLoader(THREE.TextureLoader);
57
+
58
+ // Double-buffered render targets
59
+ let tempTarget = new THREE.WebGLRenderTarget(1, 1, options);
60
+ let persistedTarget = new THREE.WebGLRenderTarget(1, 1, options);
61
+
62
+ // Setup the quad that the fog of war is drawn on
63
+ let scene = new THREE.Scene();
64
+ let camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
65
+ const quad = new THREE.Mesh();
66
+
67
+ onDestroy(() => {
68
+ tempTarget?.dispose();
69
+ persistedTarget?.dispose();
70
+ drawMaterial?.dispose();
71
+ });
72
+
73
+ // Map size changed
74
+ $effect(() => {
75
+ if (!size) return;
76
+
77
+ // If map size changed, update the render target sizes
78
+ if (size.width !== tempTarget.width || size.height !== tempTarget.height) {
79
+ tempTarget.setSize(size.width, size.height);
80
+ persistedTarget.setSize(size.width, size.height);
81
+ // Use .set() to avoid allocating new Vector2
82
+ drawMaterial.uniforms.uTextureSize.value.set(size.width, size.height);
83
+
84
+ // If an image is provided, load it, otherwise reset the fog state
85
+ if (props.url) {
86
+ loadImage(props.url);
87
+ untrack(() => (imageUrl = props.url));
88
+ } else {
89
+ if (initialState === InitialState.Fill) {
90
+ render(RenderMode.Fill, true);
91
+ } else {
92
+ render(RenderMode.Clear, true);
93
+ }
94
+ }
95
+ }
96
+ });
97
+
98
+ $effect(() => {
99
+ // If fog image is changed, reload the image
100
+ if (props.url && props.url !== imageUrl) {
101
+ loadImage(props.url);
102
+ untrack(() => (imageUrl = props.url));
103
+ }
104
+ });
105
+
106
+ // Track previous values to avoid unnecessary rerenders
107
+ let prevToolType = props.tool.type;
108
+ let prevToolMode = props.tool.mode;
109
+
110
+ // Whenever the fog of war props change, we need to update the material
111
+ $effect(() => {
112
+ drawMaterial.uniforms.uEnd.value.copy(drawMaterial.uniforms.uStart.value);
113
+ drawMaterial.uniforms.uShapeType.value = props.tool.type;
114
+ drawMaterial.uniforms.uBrushSize.value = props.tool.size;
115
+
116
+ // Use .set() to avoid allocating new Vector4
117
+ if (props.tool.mode === DrawMode.Erase) {
118
+ drawMaterial.uniforms.uBrushColor.value.set(0, 0, 0, 0);
119
+ } else {
120
+ drawMaterial.uniforms.uBrushColor.value.set(1, 1, 1, 1);
121
+ }
122
+
123
+ // Only revert and redraw if tool type or mode changed, not size
124
+ const toolTypeOrModeChanged = prevToolType !== props.tool.type || prevToolMode !== props.tool.mode;
125
+
126
+ if (toolTypeOrModeChanged) {
127
+ // Discard the current buffer by copying the previous buffer to the current buffer
128
+ render(RenderMode.Revert, true);
129
+ }
130
+
131
+ // Always redraw to show the updated tool overlay (including size changes)
132
+ render(RenderMode.Draw);
133
+
134
+ // Update previous values
135
+ prevToolType = props.tool.type;
136
+ prevToolMode = props.tool.mode;
137
+ });
138
+
139
+ /**
140
+ * Loads an image into the current buffer
141
+ * @param url The URL of the image to load
142
+ */
143
+ function loadImage(url: string) {
144
+ loader.load(url).then((texture) => render(RenderMode.Revert, true, texture));
145
+ }
146
+
147
+ /**
148
+ * Swaps the current and previous buffers to persist the current state
149
+ */
150
+ function swapBuffers() {
151
+ const temp = tempTarget;
152
+ tempTarget = persistedTarget;
153
+ persistedTarget = temp;
154
+ }
155
+
156
+ export function clear() {
157
+ render(RenderMode.Clear, true);
158
+ }
159
+
160
+ export function fill() {
161
+ render(RenderMode.Fill, true);
162
+ }
163
+
164
+ export function revert() {
165
+ render(RenderMode.Revert, true);
166
+ }
167
+
168
+ /**
169
+ * Resets the cursor position to hide it
170
+ */
171
+ export function resetCursor() {
172
+ // Set both start and end to a far off-screen position
173
+ const offScreen = new THREE.Vector2(Infinity, Infinity);
174
+ drawMaterial.uniforms.uStart.value.copy(offScreen);
175
+ drawMaterial.uniforms.uEnd.value.copy(offScreen);
176
+ // Re-render to clear the cursor from the texture
177
+ render(RenderMode.Revert, false);
178
+ }
179
+
180
+ /**
181
+ * Renders the to the current buffer
182
+ * @param operation The operation to perform. 'fill' will reset the fog of war to the initial state, 'revert' will copy the current state to the previous state, 'clear' will clear the current state, and 'draw' will draw the current state
183
+ * @param persist Whether to persist the current state
184
+ * @param lastTexture The texture to use for the previous state
185
+ */
186
+ export function render(operation: RenderMode, persist: boolean = false, lastTexture: THREE.Texture | null = null) {
187
+ // If no previous state is provided, use the last target
188
+ drawMaterial.uniforms.uPreviousState.value = lastTexture ?? persistedTarget.texture;
189
+
190
+ drawMaterial.uniforms.uIsRevertOperation.value = operation === RenderMode.Revert;
191
+ drawMaterial.uniforms.uIsFillOperation.value = operation === RenderMode.Fill;
192
+ drawMaterial.uniforms.uIsClearOperation.value = operation === RenderMode.Clear;
193
+
194
+ scene.visible = true;
195
+ renderer.setRenderTarget(tempTarget);
196
+ renderer.render(scene, camera);
197
+ scene.visible = false;
198
+
199
+ onRender(tempTarget.texture);
200
+
201
+ drawMaterial.uniforms.uIsRevertOperation.value = false;
202
+ drawMaterial.uniforms.uIsFillOperation.value = false;
203
+ drawMaterial.uniforms.uIsClearOperation.value = false;
204
+
205
+ renderer.setRenderTarget(null);
206
+
207
+ if (persist) {
208
+ swapBuffers();
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Draws a path on the current buffer
214
+ * @param start The start position of the path
215
+ * @param last The last position of the path
216
+ * @param persist Whether to persist the current state
217
+ */
218
+ export function drawPath(start: THREE.Vector2, last: THREE.Vector2 | null, persist: boolean) {
219
+ drawMaterial.uniforms.uStart.value.copy(start);
220
+ drawMaterial.uniforms.uEnd.value.copy(last ?? start);
221
+ render(RenderMode.Draw, persist);
222
+ }
223
+
224
+ /**
225
+ * Gets the current texture
226
+ * @returns The current texture
227
+ */
228
+ export function getTexture() {
229
+ return tempTarget.texture;
230
+ }
231
+
232
+ /**
233
+ * Serializes the current fog of war state to a binary buffer
234
+ * @returns A binary buffer representation of the fog of war texture
235
+ */
236
+ export async function toPng(): Promise<Blob> {
237
+ // Create a temporary canvas to draw the texture
238
+ const canvas = new OffscreenCanvas(persistedTarget.width, persistedTarget.height);
239
+ const ctx = canvas.getContext('2d')!;
240
+
241
+ // Read pixels from WebGL render target
242
+ const pixels = new Uint8Array(4 * persistedTarget.width * persistedTarget.height);
243
+ renderer.readRenderTargetPixels(persistedTarget, 0, 0, persistedTarget.width, persistedTarget.height, pixels);
244
+
245
+ // Draw pixels to canvas
246
+ const imageData = ctx.createImageData(persistedTarget.width, persistedTarget.height);
247
+ imageData.data.set(pixels);
248
+ ctx.putImageData(imageData, 0, 0);
249
+
250
+ // The pixel data is flipped vertically when read from the WebGL render target, so we need to flip it back
251
+ const flippedCanvas = document.createElement('canvas');
252
+ flippedCanvas.width = canvas.width;
253
+ flippedCanvas.height = canvas.height;
254
+ const flippedCtx = flippedCanvas.getContext('2d')!;
255
+
256
+ flippedCtx.scale(1, -1);
257
+ flippedCtx.translate(0, -canvas.height);
258
+ flippedCtx.drawImage(canvas, 0, 0);
259
+
260
+ // Convert to blob with lossless PNG compression
261
+ return new Promise((resolve) => {
262
+ flippedCanvas.toBlob((blob) => resolve(blob!), 'image/png');
263
+ });
264
+ }
265
+
266
+ /**
267
+ * Exports the current state as RLE-encoded data
268
+ * @returns RLE encoded Uint8Array with dimensions prepended
269
+ */
270
+ export async function toRLE(): Promise<Uint8Array> {
271
+ const width = persistedTarget.width;
272
+ const height = persistedTarget.height;
273
+
274
+ // Read pixels from WebGL render target
275
+ const pixels = new Uint8Array(4 * width * height);
276
+ renderer.readRenderTargetPixels(persistedTarget, 0, 0, width, height, pixels);
277
+
278
+ // Extract alpha channel and flip vertically
279
+ const binaryData = new Uint8Array(width * height);
280
+ for (let y = 0; y < height; y++) {
281
+ for (let x = 0; x < width; x++) {
282
+ const srcIndex = (y * width + x) * 4 + 3; // Alpha channel
283
+ const dstIndex = (height - 1 - y) * width + x; // Flip vertically
284
+ binaryData[dstIndex] = pixels[srcIndex] > 127 ? 255 : 0;
285
+ }
286
+ }
287
+
288
+ const rleData = encodeRLE(binaryData);
289
+
290
+ // Prepend dimensions to the RLE data (4 bytes for width, 4 bytes for height)
291
+ const result = new Uint8Array(8 + rleData.length);
292
+ const view = new DataView(result.buffer);
293
+ view.setUint32(0, width, true); // little-endian
294
+ view.setUint32(4, height, true); // little-endian
295
+ result.set(rleData, 8);
296
+
297
+ return result;
298
+ }
299
+
300
+ /**
301
+ * Loads RLE-encoded data into the drawing buffer
302
+ * @param rleData RLE encoded data (with dimensions prepended)
303
+ * @param width Image width (ignored if dimensions are in data)
304
+ * @param height Image height (ignored if dimensions are in data)
305
+ */
306
+ export async function fromRLE(rleData: Uint8Array, width?: number, height?: number) {
307
+ // Check if dimensions are prepended (new format)
308
+ let actualWidth = width || 1024;
309
+ let actualHeight = height || 1024;
310
+ let rleStart = 0;
311
+
312
+ if (rleData.length > 8) {
313
+ const view = new DataView(rleData.buffer, rleData.byteOffset, rleData.byteLength);
314
+ const possibleWidth = view.getUint32(0, true);
315
+ const possibleHeight = view.getUint32(4, true);
316
+
317
+ // Sanity check - dimensions should be reasonable
318
+ if (possibleWidth > 0 && possibleWidth <= 4096 && possibleHeight > 0 && possibleHeight <= 4096) {
319
+ actualWidth = possibleWidth;
320
+ actualHeight = possibleHeight;
321
+ rleStart = 8;
322
+ }
323
+ }
324
+
325
+ // Extract the actual RLE data
326
+ const actualRleData = rleStart > 0 ? rleData.slice(rleStart) : rleData;
327
+
328
+ // Decode RLE to binary
329
+ const binaryData = decodeRLE(actualRleData, actualWidth * actualHeight);
330
+
331
+ // Create texture from binary data - flip vertically to match WebGL coordinate system
332
+ const rgba = new Uint8Array(actualWidth * actualHeight * 4);
333
+ for (let y = 0; y < actualHeight; y++) {
334
+ for (let x = 0; x < actualWidth; x++) {
335
+ const srcIndex = y * actualWidth + x;
336
+ const dstIndex = (actualHeight - 1 - y) * actualWidth + x; // Flip vertically
337
+ const idx = dstIndex * 4;
338
+ rgba[idx] = 0; // R
339
+ rgba[idx + 1] = 0; // G
340
+ rgba[idx + 2] = 0; // B
341
+ rgba[idx + 3] = binaryData[srcIndex]; // A
342
+ }
343
+ }
344
+
345
+ // Create texture
346
+ const texture = new THREE.DataTexture(rgba, actualWidth, actualHeight, THREE.RGBAFormat);
347
+ texture.needsUpdate = true;
348
+
349
+ // Load into buffer
350
+ render(RenderMode.Revert, true, texture);
351
+
352
+ // Dispose the temporary texture after rendering to buffer
353
+ texture.dispose();
354
+ }
355
+ </script>
356
+
357
+ <!-- Hidden scene that renders to the render target -->
358
+ <T.Scene is={scene} visible={false}>
359
+ <T.OrthographicCamera is={camera} />
360
+ <T.Mesh is={quad}>
361
+ <T.ShaderMaterial is={drawMaterial} />
362
+ <T.PlaneGeometry args={[2, 2]} />
363
+ </T.Mesh>
364
+ </T.Scene>
@@ -0,0 +1,65 @@
1
+ export enum InitialState {
2
+ Clear = 0,
3
+ Fill = 1
4
+ }
5
+
6
+ export enum ToolType {
7
+ Brush = 1,
8
+ Rectangle = 2,
9
+ Ellipse = 3
10
+ }
11
+
12
+ export enum DrawMode {
13
+ Erase = 0,
14
+ Draw = 1
15
+ }
16
+
17
+ export enum RenderMode {
18
+ Draw = 'draw',
19
+ Clear = 'clear',
20
+ Revert = 'revert',
21
+ Fill = 'fill'
22
+ }
23
+
24
+ /**
25
+ * The properties for a drawing layer
26
+ */
27
+ export interface DrawingLayerProps {
28
+ /**
29
+ * The URL of the texture to use for the drawing layer
30
+ */
31
+ url: string | null;
32
+
33
+ /**
34
+ * Version timestamp for mask data changes (for real-time sync)
35
+ */
36
+ maskVersion?: number;
37
+
38
+ /**
39
+ * The opacity of the drawing layer
40
+ */
41
+ opacity: {
42
+ dm: number;
43
+ player: number;
44
+ };
45
+
46
+ /**
47
+ * The tool settings
48
+ */
49
+ tool: {
50
+ /**
51
+ * The current drawing mode (`DrawMode.Erase` or `DrawMode.Draw`)
52
+ */
53
+ mode: DrawMode;
54
+
55
+ /**
56
+ * When `toolType = ToolType.Brush`, setting this controls the brush size
57
+ */
58
+ size: number;
59
+
60
+ /**
61
+ * The type of drawing tool currently selected
62
+ */
63
+ type: ToolType;
64
+ };
65
+ }
@@ -0,0 +1,72 @@
1
+ <script lang="ts">
2
+ import * as THREE from 'three';
3
+ import { T, useLoader, type Props as ThrelteProps } from '@threlte/core';
4
+ import { TextureLoader } from 'three';
5
+ import type { EdgeOverlayProps } from './types';
6
+ import type { DisplayProps } from '../Stage/types';
7
+ import { SceneLayer } from '../Scene/types';
8
+
9
+ import vertexShader from '../../shaders/default.vert?raw';
10
+ import fragmentShader from '../../shaders/Overlay.frag?raw';
11
+
12
+ interface Props extends ThrelteProps<typeof THREE.Mesh> {
13
+ props: EdgeOverlayProps;
14
+ display: DisplayProps;
15
+ }
16
+
17
+ const { props, display, ...meshProps }: Props = $props();
18
+ const loader = useLoader(TextureLoader);
19
+ let texture: THREE.Texture | undefined = $state();
20
+
21
+ // Load texture when URL changes
22
+ $effect(() => {
23
+ if (!props.url) return;
24
+
25
+ loader
26
+ .load(props.url, {
27
+ transform: (tex) => {
28
+ tex.colorSpace = THREE.SRGBColorSpace;
29
+ return tex;
30
+ }
31
+ })
32
+ .then((tex) => {
33
+ texture?.dispose();
34
+ texture = tex;
35
+ });
36
+ });
37
+
38
+ const material = new THREE.ShaderMaterial({
39
+ uniforms: {
40
+ uTexture: { value: null },
41
+ uResolution: { value: new THREE.Vector2(display.resolution.x, display.resolution.y) },
42
+ uOpacity: { value: props.opacity },
43
+ uFadeStart: { value: props.fadeStart },
44
+ uFadeEnd: { value: props.fadeEnd },
45
+ uScale: { value: props.scale }
46
+ },
47
+ vertexShader,
48
+ fragmentShader,
49
+ transparent: true,
50
+ depthWrite: false,
51
+ depthTest: false
52
+ });
53
+
54
+ // Update uniforms when props change
55
+ $effect(() => {
56
+ material.uniforms.uTexture.value = texture;
57
+ material.uniforms.uOpacity.value = props.opacity;
58
+ material.uniforms.uFadeStart.value = props.fadeStart;
59
+ material.uniforms.uFadeEnd.value = props.fadeEnd;
60
+ material.uniforms.uScale.value = props.scale;
61
+ });
62
+ </script>
63
+
64
+ <T.Mesh
65
+ name="gridLayer"
66
+ scale={[display.resolution.x, display.resolution.y, 1]}
67
+ layers={[SceneLayer.Overlay]}
68
+ {...meshProps}
69
+ >
70
+ <T.ShaderMaterial is={material} />
71
+ <T.PlaneGeometry />
72
+ </T.Mesh>
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Props for the EdgeOverlayLayer component
3
+ */
4
+ export interface EdgeOverlayProps {
5
+ /**
6
+ * Whether the edge overlay is enabled
7
+ */
8
+ enabled: boolean;
9
+
10
+ /**
11
+ * URL of the overlay texture
12
+ */
13
+ url: string | null;
14
+
15
+ /**
16
+ * Opacity of the overlay
17
+ */
18
+ opacity: number;
19
+
20
+ /**
21
+ * Texture scale of the overlay
22
+ */
23
+ scale: number;
24
+
25
+ /**
26
+ * Distance from center where fade starts (0-1)
27
+ */
28
+ fadeStart: number;
29
+
30
+ /**
31
+ * Distance from center where fade ends (0-1)
32
+ */
33
+ fadeEnd: number;
34
+ }
@@ -0,0 +1,75 @@
1
+ <script lang="ts">
2
+ import * as THREE from 'three';
3
+ import { T, useTask, type Props as ThrelteProps, useThrelte } from '@threlte/core';
4
+ import type { Size } from '../../types';
5
+ import type { FogLayerProps } from './types';
6
+ import { clippingPlaneStore } from '../../helpers/clippingPlaneStore.svelte';
7
+
8
+ import vertexShader from '../../shaders/default.vert?raw';
9
+ import fragmentShader from '../../shaders/FractalNoise.frag?raw';
10
+
11
+ interface Props extends ThrelteProps<typeof THREE.Mesh> {
12
+ props: FogLayerProps;
13
+ mapSize: Size | null;
14
+ }
15
+
16
+ const { props, mapSize, ...meshProps }: Props = $props();
17
+
18
+ const { renderStage } = useThrelte();
19
+
20
+ const aspectRatio = $derived((mapSize?.width ?? 1) / (mapSize?.height ?? 1));
21
+
22
+ const material = new THREE.ShaderMaterial({
23
+ uniforms: {
24
+ uTime: { value: 0.0 },
25
+ uAspectRatio: { value: 1 },
26
+ uFogColor: { value: new THREE.Color(props.color) },
27
+ uOpacity: { value: props.opacity },
28
+ uFogSpeed: { value: props.speed },
29
+ uPersistence: { value: props.persistence },
30
+ uLacunarity: { value: props.lacunarity },
31
+ uFrequency: { value: props.frequency },
32
+ uOffset: { value: props.offset },
33
+ uAmplitude: { value: props.amplitude },
34
+ uLevels: { value: props.levels },
35
+ uClippingPlanes: new THREE.Uniform(
36
+ clippingPlaneStore.value.map((p) => new THREE.Vector4(p.normal.x, p.normal.y, p.normal.z, p.constant))
37
+ )
38
+ },
39
+ vertexShader,
40
+ fragmentShader,
41
+ transparent: true,
42
+ depthWrite: false,
43
+ depthTest: false
44
+ });
45
+
46
+ // Update uniforms when props change
47
+ $effect(() => {
48
+ material.uniforms.uFogColor.value.set(props.color);
49
+ material.uniforms.uAspectRatio.value = aspectRatio;
50
+ material.uniforms.uOpacity.value = props.opacity;
51
+ material.uniforms.uFogSpeed.value = props.speed;
52
+ material.uniforms.uPersistence.value = props.persistence;
53
+ material.uniforms.uLacunarity.value = props.lacunarity;
54
+ material.uniforms.uFrequency.value = props.frequency;
55
+ material.uniforms.uOffset.value = props.offset;
56
+ material.uniforms.uAmplitude.value = props.amplitude;
57
+ material.uniforms.uLevels.value = props.levels;
58
+
59
+ material.uniforms.uClippingPlanes.value = clippingPlaneStore.value.map(
60
+ (p) => new THREE.Vector4(p.normal.x, p.normal.y, p.normal.z, p.constant)
61
+ );
62
+ });
63
+
64
+ useTask(
65
+ (dt) => {
66
+ material.uniforms.uTime.value += dt;
67
+ },
68
+ { stage: renderStage }
69
+ );
70
+ </script>
71
+
72
+ <T.Mesh {...meshProps}>
73
+ <T.ShaderMaterial is={material} />
74
+ <T.PlaneGeometry />
75
+ </T.Mesh>
@@ -0,0 +1,51 @@
1
+ export interface FogLayerProps {
2
+ /**
3
+ * Whether the fog layer is enabled
4
+ */
5
+ enabled: boolean;
6
+
7
+ /**
8
+ * Color of the fog
9
+ */
10
+ color: string;
11
+
12
+ /**
13
+ * Overall opacity of the fog layer
14
+ */
15
+ opacity: number;
16
+
17
+ /**
18
+ * Speed of fog movement
19
+ */
20
+ speed: number;
21
+
22
+ /**
23
+ * How quickly the amplitude decreases for each octave
24
+ */
25
+ persistence: number;
26
+
27
+ /**
28
+ * How quickly the frequency increases for each octave
29
+ */
30
+ lacunarity: number;
31
+
32
+ /**
33
+ * Base frequency of the noise
34
+ */
35
+ frequency: number;
36
+
37
+ /**
38
+ * Vertical offset of the noise
39
+ */
40
+ offset: number;
41
+
42
+ /**
43
+ * Overall amplitude of the noise
44
+ */
45
+ amplitude: number;
46
+
47
+ /**
48
+ * Number of octaves to use
49
+ */
50
+ levels: number;
51
+ }