@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,548 @@
1
+ import * as THREE from 'three';
2
+ import { GridMode, GridType, type GridLayerProps } from '../components/GridLayer/types';
3
+ import type { DisplayProps } from '../components/Stage/types';
4
+
5
+ // Hexagonal grid offset factor
6
+ const s = { x: 1.0, y: 1.7320508 }; // sqrt(3)
7
+
8
+ /**
9
+ * Gets the size of a grid cell in pixels
10
+ * @param gridType Type of grid (square or hex)
11
+ * @param gridSizePixels Base grid size in pixels
12
+ * @returns Width of grid cell in pixels
13
+ */
14
+ export function getGridCellSize(grid: GridLayerProps, display: DisplayProps): number {
15
+ return (grid.spacing * display.resolution.x) / display.size.x;
16
+ }
17
+
18
+ /**
19
+ * Calculates the grid origin for Map defined mode
20
+ * Matches the shader logic for grid positioning
21
+ * @param grid Grid configuration
22
+ * @param display Display properties
23
+ * @returns Grid origin in pixels (from top-left corner)
24
+ */
25
+ export function getGridOrigin(grid: GridLayerProps, display: DisplayProps): THREE.Vector2 {
26
+ // In FillSpace mode, grid is centered with padding
27
+ if ((grid.gridMode || 0) === GridMode.FillSpace) {
28
+ // Grid starts at padding position
29
+ return new THREE.Vector2(display.padding.x, display.padding.y);
30
+ }
31
+
32
+ // In MapDefined mode, calculate based on grid size
33
+ if (!grid.fixedGridCount) {
34
+ // Fallback to padding if no fixed count
35
+ return new THREE.Vector2(display.padding.x, display.padding.y);
36
+ }
37
+
38
+ // Calculate pixel pitch (inches per pixel)
39
+ const pixelPitchX = display.size.x / display.resolution.x;
40
+ const pixelPitchY = display.size.y / display.resolution.y;
41
+
42
+ // Calculate grid spacing in pixels
43
+ const gridSpacingX = grid.spacing / pixelPitchX;
44
+ const gridSpacingY = grid.spacing / pixelPitchY;
45
+
46
+ // Calculate total grid size in pixels (must match shader calculation)
47
+ // This matches the shader: gridSize_px = gridSpacing_px * gridCount + uLineThickness / 2.0
48
+ const gridWidthPx = gridSpacingX * grid.fixedGridCount.x + grid.lineThickness / 2.0;
49
+ const gridHeightPx = gridSpacingY * grid.fixedGridCount.y + grid.lineThickness / 2.0;
50
+
51
+ let originX: number;
52
+ let originY: number;
53
+
54
+ // If grid fits horizontally, center it; otherwise align left
55
+ if (gridWidthPx <= display.resolution.x) {
56
+ originX = (display.resolution.x - gridWidthPx) / 2.0;
57
+ } else {
58
+ originX = 0;
59
+ }
60
+
61
+ // If grid fits vertically, center it; otherwise align top
62
+ if (gridHeightPx <= display.resolution.y) {
63
+ originY = (display.resolution.y - gridHeightPx) / 2.0;
64
+ } else {
65
+ // Grid overflows - start at top (Y=0 in screen coordinates)
66
+ originY = 0;
67
+ }
68
+
69
+ return new THREE.Vector2(originX, originY);
70
+ }
71
+
72
+ /**
73
+ * Snaps a position to the nearest square grid intersection
74
+ * @param position Position to snap in pixels
75
+ * @param spacing Grid spacing in pixels
76
+ * @returns Snapped position in pixels
77
+ */
78
+ function snapToSquareGrid(position: THREE.Vector2, spacing: THREE.Vector2): THREE.Vector2 {
79
+ const roundedX = Math.round(position.x / spacing.x) * spacing.x;
80
+ const roundedY = Math.round(position.y / spacing.y) * spacing.y;
81
+
82
+ return new THREE.Vector2(roundedX, roundedY);
83
+ }
84
+
85
+ /**
86
+ * Snaps a position to the nearest hexagonal grid center only
87
+ * Exactly matches the shader's getHex implementation
88
+ * @param position Position to snap in pixels
89
+ * @param spacing Grid spacing in pixels
90
+ * @returns The nearest hex center position
91
+ */
92
+ function snapToHexCenter(position: THREE.Vector2, spacing: THREE.Vector2): THREE.Vector2 {
93
+ // Exact match of shader's getHex function
94
+ // First normalize by spacing like the shader does: hexUv = getHex(coords / spacing)
95
+ const p = new THREE.Vector2(position.x / spacing.x, position.y / spacing.y);
96
+
97
+ // vec4 hC = floor(vec4(p, p - vec2(0.5, 1.0)) / s.xyxy) + 0.5;
98
+ // where s = vec2(1.0, 1.7320508)
99
+ const hC = {
100
+ x: Math.floor(p.x / s.x) + 0.5,
101
+ y: Math.floor(p.y / s.y) + 0.5,
102
+ z: Math.floor((p.x - 0.5) / s.x) + 0.5,
103
+ w: Math.floor((p.y - 1.0) / s.y) + 0.5
104
+ };
105
+
106
+ // vec4 h = vec4(p - hC.xy * s, p - (hC.zw + 0.5) * s);
107
+ const h = {
108
+ x: p.x - hC.x * s.x,
109
+ y: p.y - hC.y * s.y,
110
+ z: p.x - (hC.z + 0.5) * s.x,
111
+ w: p.y - (hC.w + 0.5) * s.y
112
+ };
113
+
114
+ // dot(h.xy, h.xy) < dot(h.zw, h.zw)
115
+ const dist1 = h.x * h.x + h.y * h.y;
116
+ const dist2 = h.z * h.z + h.w * h.w;
117
+
118
+ // The shader returns the relative position, but we want the actual center
119
+ // So we need to convert back to the center position
120
+ let center: THREE.Vector2;
121
+ if (dist1 < dist2) {
122
+ // First grid center
123
+ center = new THREE.Vector2(hC.x * s.x, hC.y * s.y);
124
+ } else {
125
+ // Second grid center (offset)
126
+ center = new THREE.Vector2((hC.z + 0.5) * s.x, (hC.w + 0.5) * s.y);
127
+ }
128
+
129
+ // Scale back to pixel coordinates
130
+ return new THREE.Vector2(center.x * spacing.x, center.y * spacing.y);
131
+ }
132
+
133
+ /**
134
+ * Snaps a position to the nearest hexagonal grid intersection (includes vertices and edges)
135
+ * Based on the shader's hexagonal grid implementation
136
+ * @param position Position to snap in pixels
137
+ * @param spacing Grid spacing in pixels
138
+ * @returns The distance to the nearest center and the center
139
+ */
140
+ function distanceToHexCenter(position: THREE.Vector2, spacing: THREE.Vector2): THREE.Vector2 {
141
+ // This function finds the nearest center in a hexagonal grid. The centers of a
142
+ // hexagonal grid can be represented by two offset square grids. The two grids
143
+ // are offset by half the grid spacing in the x direction and sqrt(3)/2 times
144
+ // the grid spacing in the y direction.
145
+
146
+ // Calculate the nearest cell for first grid
147
+ const gridPoints = {
148
+ first: {
149
+ x: Math.floor(position.x / (spacing.x * s.x)) + 0.5,
150
+ y: Math.floor(position.y / (spacing.y * s.y)) + 0.5
151
+ },
152
+ // Offset in x and y directions
153
+ second: {
154
+ x: Math.floor((position.x - s.x * spacing.x * 0.5) / (spacing.x * s.x)) + 0.5,
155
+ y: Math.floor((position.y - s.y * spacing.y * 0.5) / (spacing.y * s.y)) + 0.5
156
+ },
157
+ // Additional grid offset in y direction by spacing/sqrt(3)
158
+ third: {
159
+ x: Math.floor(position.x / (spacing.x * s.x)) + 0.5,
160
+ y: Math.floor((position.y - spacing.y / Math.sqrt(3)) / (spacing.y * s.y)) + 0.5
161
+ },
162
+ // Additional grid offset in y direction by spacing/sqrt(3)
163
+ fourth: {
164
+ x: Math.floor(position.x / (spacing.x * s.x)) + 0.5,
165
+ y: Math.floor((position.y + spacing.y / Math.sqrt(3)) / (spacing.y * s.y)) + 0.5
166
+ }
167
+ };
168
+
169
+ // Calculate the centers
170
+ const center1 = new THREE.Vector2(gridPoints.first.x * spacing.x * s.x, gridPoints.first.y * spacing.y * s.y);
171
+ const center2 = new THREE.Vector2(
172
+ (gridPoints.second.x + 0.5) * spacing.x * s.x,
173
+ (gridPoints.second.y + 0.5) * spacing.y * s.y
174
+ );
175
+ const center3 = new THREE.Vector2(
176
+ gridPoints.third.x * spacing.x * s.x,
177
+ gridPoints.third.y * spacing.y * s.y + spacing.y / Math.sqrt(3)
178
+ );
179
+ const center4 = new THREE.Vector2(
180
+ gridPoints.fourth.x * spacing.x * s.x,
181
+ gridPoints.fourth.y * spacing.y * s.y - spacing.y / Math.sqrt(3)
182
+ );
183
+
184
+ // Calculate vertices around each center (line intersections)
185
+ const getHexVertices = (center: THREE.Vector2) => {
186
+ const vertices: THREE.Vector2[] = [];
187
+ const radius = spacing.x; // Distance from center to vertex
188
+
189
+ for (let i = 0; i < 6; i++) {
190
+ const angle = (Math.PI / 3) * i;
191
+ vertices.push(new THREE.Vector2(center.x + radius * Math.cos(angle), center.y + radius * Math.sin(angle)));
192
+ }
193
+ return vertices;
194
+ };
195
+
196
+ // Get vertices for each center
197
+ const vertices1 = getHexVertices(center1);
198
+ const vertices2 = getHexVertices(center2);
199
+ const vertices3 = getHexVertices(center3);
200
+ const vertices4 = getHexVertices(center4);
201
+
202
+ // Calculate distances to all points (centers and vertices)
203
+ const distances = [
204
+ { d: Math.pow(position.x - center1.x, 2) + Math.pow(position.y - center1.y, 2), point: center1 },
205
+ { d: Math.pow(position.x - center2.x, 2) + Math.pow(position.y - center2.y, 2), point: center2 },
206
+ { d: Math.pow(position.x - center3.x, 2) + Math.pow(position.y - center3.y, 2), point: center3 },
207
+ { d: Math.pow(position.x - center4.x, 2) + Math.pow(position.y - center4.y, 2), point: center4 },
208
+ ...vertices1.map((v) => ({
209
+ d: Math.pow(position.x - v.x, 2) + Math.pow(position.y - v.y, 2),
210
+ point: v
211
+ })),
212
+ ...vertices2.map((v) => ({
213
+ d: Math.pow(position.x - v.x, 2) + Math.pow(position.y - v.y, 2),
214
+ point: v
215
+ })),
216
+ ...vertices3.map((v) => ({
217
+ d: Math.pow(position.x - v.x, 2) + Math.pow(position.y - v.y, 2),
218
+ point: v
219
+ })),
220
+ ...vertices4.map((v) => ({
221
+ d: Math.pow(position.x - v.x, 2) + Math.pow(position.y - v.y, 2),
222
+ point: v
223
+ }))
224
+ ];
225
+
226
+ // Find the closest point
227
+ const closest = distances.reduce((prev, curr) => (curr.d < prev.d ? curr : prev));
228
+
229
+ return closest.point;
230
+ }
231
+
232
+ /**
233
+ * Snaps a position to the nearest grid intersection based on the current grid configuration
234
+ * @param position Position to snap (in screen coordinates, relative to center)
235
+ * @param grid Grid configuration
236
+ * @param display Display properties
237
+ * @param centerOnly For hex grids, whether to snap to centers only (used for measurements)
238
+ * @returns Snapped position (in screen coordinates, relative to center)
239
+ */
240
+ export function snapToGrid(
241
+ position: THREE.Vector2,
242
+ grid: GridLayerProps,
243
+ display: DisplayProps,
244
+ centerOnly: boolean = false
245
+ ): THREE.Vector2 {
246
+ // Compute the pixel pitch in inches
247
+ const pixelsPerInch = new THREE.Vector2(display.resolution.x / display.size.x, display.resolution.y / display.size.y);
248
+
249
+ // Compute the grid spacing in pixels
250
+ const gridSpacingPixels = new THREE.Vector2(grid.spacing * pixelsPerInch.x, grid.spacing * pixelsPerInch.y);
251
+
252
+ // In Map defined mode, we need to adjust for the grid origin and shader offset
253
+ if (grid.gridMode === GridMode.MapDefined && grid.fixedGridCount) {
254
+ // Get the grid origin in screen coordinates
255
+ const gridOrigin = getGridOrigin(grid, display);
256
+
257
+ // Convert position from center-relative to top-left relative (screen coords)
258
+ const screenPos = new THREE.Vector2(
259
+ position.x + display.resolution.x / 2,
260
+ display.resolution.y / 2 - position.y // Flip Y axis
261
+ );
262
+
263
+ // Convert to grid-relative coordinates
264
+ let gridRelativePos = new THREE.Vector2(screenPos.x - gridOrigin.x, screenPos.y - gridOrigin.y);
265
+
266
+ // Account for the shader offset (t / 4.0) that shifts the visual grid lines
267
+ const shaderOffset = grid.lineThickness / 4.0;
268
+ gridRelativePos.x -= shaderOffset;
269
+ gridRelativePos.y -= shaderOffset;
270
+
271
+ let snappedGridPos: THREE.Vector2;
272
+
273
+ if (grid.gridType === GridType.Square) {
274
+ // For square grids, snap to cell centers (half-spacing increments)
275
+ const halfSpacing = new THREE.Vector2(gridSpacingPixels.x / 2.0, gridSpacingPixels.y / 2.0);
276
+ snappedGridPos = snapToSquareGrid(gridRelativePos, halfSpacing);
277
+ } else {
278
+ // For hex grids, use center-only snapping if requested (e.g., for measurements)
279
+ if (centerOnly) {
280
+ snappedGridPos = snapToHexCenter(gridRelativePos, gridSpacingPixels);
281
+ } else {
282
+ // Otherwise allow snapping to vertices and edges too
283
+ snappedGridPos = distanceToHexCenter(gridRelativePos, gridSpacingPixels);
284
+ }
285
+ }
286
+
287
+ // Add back the shader offset
288
+ snappedGridPos.x += shaderOffset;
289
+ snappedGridPos.y += shaderOffset;
290
+
291
+ // Convert back to screen coordinates
292
+ const snappedScreenPos = new THREE.Vector2(snappedGridPos.x + gridOrigin.x, snappedGridPos.y + gridOrigin.y);
293
+
294
+ // Convert back to center-relative coordinates
295
+ const result = new THREE.Vector2(
296
+ snappedScreenPos.x - display.resolution.x / 2,
297
+ display.resolution.y / 2 - snappedScreenPos.y // Flip Y axis back
298
+ );
299
+
300
+ return result;
301
+ }
302
+
303
+ // Fill space mode - use the original simple logic
304
+ if (grid.gridType === GridType.Square) {
305
+ // For square grids, snap to cell centers (half-spacing increments)
306
+ const halfSpacing = new THREE.Vector2(gridSpacingPixels.x / 2.0, gridSpacingPixels.y / 2.0);
307
+ return snapToSquareGrid(position, halfSpacing);
308
+ } else {
309
+ // For hex grids, use center-only snapping if requested (e.g., for measurements)
310
+ if (centerOnly) {
311
+ return snapToHexCenter(position, gridSpacingPixels);
312
+ } else {
313
+ // Otherwise allow snapping to vertices and edges too
314
+ return distanceToHexCenter(position, gridSpacingPixels);
315
+ }
316
+ }
317
+ }
318
+
319
+ // =============================================================================
320
+ // HEX GRID COORDINATE FUNCTIONS
321
+ // =============================================================================
322
+
323
+ /**
324
+ * Hex coordinate type using axial coordinates (q, r)
325
+ */
326
+ export interface HexCoordinate {
327
+ q: number;
328
+ r: number;
329
+ }
330
+
331
+ /**
332
+ * Cube coordinate type for hex calculations (x, y, z where x + y + z = 0)
333
+ */
334
+ export interface CubeCoordinate {
335
+ x: number;
336
+ y: number;
337
+ z: number;
338
+ }
339
+
340
+ /**
341
+ * Rounds fractional hex coordinates to the nearest hex
342
+ * @param hex Fractional hex coordinates
343
+ * @returns Rounded hex coordinates
344
+ */
345
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
346
+ function hexRound(hex: HexCoordinate): HexCoordinate {
347
+ // Convert to cube coordinates for rounding
348
+ const cube = axialToCube(hex);
349
+
350
+ let rx = Math.round(cube.x);
351
+ let ry = Math.round(cube.y);
352
+ let rz = Math.round(cube.z);
353
+
354
+ const xDiff = Math.abs(rx - cube.x);
355
+ const yDiff = Math.abs(ry - cube.y);
356
+ const zDiff = Math.abs(rz - cube.z);
357
+
358
+ // Reset the coordinate with the largest rounding error
359
+ if (xDiff > yDiff && xDiff > zDiff) {
360
+ rx = -ry - rz;
361
+ } else if (yDiff > zDiff) {
362
+ ry = -rx - rz;
363
+ } else {
364
+ rz = -rx - ry;
365
+ }
366
+
367
+ return cubeToAxial({ x: rx, y: ry, z: rz });
368
+ }
369
+
370
+ /**
371
+ * Converts axial hex coordinates to cube coordinates
372
+ * @param hex Axial coordinates
373
+ * @returns Cube coordinates
374
+ */
375
+ function axialToCube(hex: HexCoordinate): CubeCoordinate {
376
+ const x = hex.q;
377
+ const z = hex.r;
378
+ const y = -x - z;
379
+ return { x, y, z };
380
+ }
381
+
382
+ /**
383
+ * Converts cube coordinates to axial hex coordinates
384
+ * @param cube Cube coordinates
385
+ * @returns Axial coordinates
386
+ */
387
+ function cubeToAxial(cube: CubeCoordinate): HexCoordinate {
388
+ return { q: cube.x, r: cube.z };
389
+ }
390
+
391
+ /**
392
+ * Converts pixel coordinates to hex grid coordinates
393
+ * Returns the grid indices of the hex containing the point
394
+ * @param point Position in pixel coordinates
395
+ * @param spacing Grid spacing in pixels
396
+ * @returns Hex grid coordinates (indices and grid type)
397
+ */
398
+ export function pixelToHex(point: THREE.Vector2, spacing: number): HexCoordinate & { isGrid2?: boolean } {
399
+ // Exact match of shader's getHex function
400
+ const p = new THREE.Vector2(point.x / spacing, point.y / spacing);
401
+
402
+ // Calculate candidate grid positions
403
+ const hC = {
404
+ x: Math.floor(p.x / s.x) + 0.5,
405
+ y: Math.floor(p.y / s.y) + 0.5,
406
+ z: Math.floor((p.x - 0.5) / s.x) + 0.5,
407
+ w: Math.floor((p.y - 1.0) / s.y) + 0.5
408
+ };
409
+
410
+ // Calculate relative positions
411
+ const h = {
412
+ x: p.x - hC.x * s.x,
413
+ y: p.y - hC.y * s.y,
414
+ z: p.x - (hC.z + 0.5) * s.x,
415
+ w: p.y - (hC.w + 0.5) * s.y
416
+ };
417
+
418
+ // Choose the closer center
419
+ const dist1 = h.x * h.x + h.y * h.y;
420
+ const dist2 = h.z * h.z + h.w * h.w;
421
+
422
+ if (dist1 < dist2) {
423
+ // Grid 1 - use the indices directly
424
+ return { q: Math.floor(hC.x), r: Math.floor(hC.y), isGrid2: false };
425
+ } else {
426
+ // Grid 2 - offset grid
427
+ return { q: Math.floor(hC.z), r: Math.floor(hC.w), isGrid2: true };
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Converts hex grid coordinates to pixel coordinates
433
+ * For flat-topped hexagons in offset coordinate system
434
+ * @param hex Hex grid coordinates in offset format
435
+ * @param spacing Grid spacing in pixels (width of hex)
436
+ * @returns Position in pixel coordinates
437
+ */
438
+ export function hexToPixel(hex: HexCoordinate, spacing: number): THREE.Vector2 {
439
+ const hexHeight = spacing * Math.sqrt(3);
440
+ const hexWidth = spacing;
441
+
442
+ // Calculate pixel position
443
+ let x: number;
444
+ const y = hex.r * hexHeight * 0.75; // Rows overlap by 1/4
445
+
446
+ if (hex.r % 2 === 0) {
447
+ // Even row - no offset
448
+ x = hex.q * hexWidth;
449
+ } else {
450
+ // Odd row - offset by half a hex width
451
+ x = hex.q * hexWidth + hexWidth / 2;
452
+ }
453
+
454
+ return new THREE.Vector2(x, y);
455
+ }
456
+
457
+ /**
458
+ * Calculates the hex distance between two hex positions
459
+ * Returns 1 for adjacent hexes, 2 for hexes with one between them, etc.
460
+ * @param hex1 First hex coordinate (with isGrid2 flag)
461
+ * @param hex2 Second hex coordinate (with isGrid2 flag)
462
+ * @returns Number of hexes in the shortest path
463
+ */
464
+ export function hexDistance(
465
+ hex1: HexCoordinate & { isGrid2?: boolean },
466
+ hex2: HexCoordinate & { isGrid2?: boolean }
467
+ ): number {
468
+ // Special case: if the indices and grid types are identical, distance is 0
469
+ if (hex1.q === hex2.q && hex1.r === hex2.r && hex1.isGrid2 === hex2.isGrid2) {
470
+ return 0;
471
+ }
472
+
473
+ // For the shader's two-grid system, we can use a simpler approach
474
+ // The grids are offset by (0.5, 0.5) in grid units
475
+ // This creates a regular hex pattern
476
+
477
+ // Calculate effective positions in a unified coordinate system
478
+ let q1 = hex1.q * 2; // Scale up to avoid fractions
479
+ let r1 = hex1.r * 2;
480
+ let q2 = hex2.q * 2;
481
+ let r2 = hex2.r * 2;
482
+
483
+ // Adjust for grid 2 offset
484
+ if (hex1.isGrid2) {
485
+ q1 += 1;
486
+ r1 += 1;
487
+ }
488
+ if (hex2.isGrid2) {
489
+ q2 += 1;
490
+ r2 += 1;
491
+ }
492
+
493
+ // Now we have positions in a unified grid where each hex is at integer coordinates
494
+ // For pointy-top hexagons arranged by the shader's method:
495
+ // Adjacent hexes differ by specific patterns
496
+
497
+ const dq = Math.abs(q2 - q1);
498
+ const dr = Math.abs(r2 - r1);
499
+
500
+ // For pointy-top hexagons in the shader's two-grid system:
501
+ // Direct neighbors can be at different patterns depending on direction
502
+
503
+ // Simple case: same column (dq === 0)
504
+ if (dq === 0) {
505
+ // Moving directly north/south
506
+ // Adjacent hexes are at dr = 1 (switching between grid1 and grid2)
507
+ // Two hexes away are at dr = 2 (same grid type)
508
+ return dr;
509
+ }
510
+
511
+ // Same row (dr === 0)
512
+ if (dr === 0) {
513
+ // Moving directly east/west
514
+ // In the shader's arrangement, moving horizontally by 2 units = 1 hex
515
+ return Math.ceil(dq / 2);
516
+ }
517
+
518
+ // Diagonal movement in the two-grid system
519
+ // The actual hex distance depends on the specific arrangement
520
+ // Use Manhattan distance as an approximation, then adjust
521
+ const manhattan = dq + dr;
522
+
523
+ // In the shader's hex arrangement:
524
+ // - Direct neighbors are at manhattan distance 2 (e.g., (0,0) to (1,1))
525
+ // - Hexes with one between are at manhattan distance 4
526
+ // So we divide by 2
527
+ const distance = Math.ceil(manhattan / 2);
528
+
529
+ // However, we need to account for the hex geometry
530
+ // Some diagonal moves are shorter in hex distance
531
+ // If both dq and dr are non-zero and similar, it's a diagonal move
532
+ if (dq > 0 && dr > 0) {
533
+ // For true hex distance, we need to consider that some diagonals are direct neighbors
534
+ // In a hex grid, you can move diagonally in 6 directions
535
+ const minDiff = Math.min(dq, dr);
536
+ const maxDiff = Math.max(dq, dr);
537
+
538
+ // If moving equally in both directions, it's a direct diagonal
539
+ if (minDiff === maxDiff) {
540
+ return minDiff;
541
+ }
542
+
543
+ // Otherwise, it's a combination of diagonal and straight moves
544
+ return minDiff + Math.ceil((maxDiff - minDiff) / 2);
545
+ }
546
+
547
+ return distance;
548
+ }