@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.
- package/package.json +2 -13
- package/src/lib/components/Avatar/Avatar.svelte +82 -0
- package/src/lib/components/Avatar/AvatarFileInput.svelte +85 -0
- package/src/lib/components/Avatar/AvatarPopover.svelte +34 -0
- package/src/lib/components/Avatar/index.ts +4 -0
- package/src/lib/components/Avatar/types.ts +24 -0
- package/src/lib/components/BrushSizeSlider/BrushSizeSlider.svelte +174 -0
- package/src/lib/components/BrushSizeSlider/index.ts +1 -0
- package/src/lib/components/Button/Button.svelte +182 -0
- package/src/lib/components/Button/ConfirmActionButton.svelte +98 -0
- package/src/lib/components/Button/IconButton.svelte +121 -0
- package/src/lib/components/Button/RadioButton.svelte +93 -0
- package/src/lib/components/Button/index.ts +5 -0
- package/src/lib/components/Button/types.ts +54 -0
- package/src/lib/components/CardFan/CardFan.svelte +165 -0
- package/src/lib/components/CardFan/index.ts +2 -0
- package/src/lib/components/CardFan/types.ts +6 -0
- package/src/lib/components/CodeBlock/Code.svelte +7 -0
- package/src/lib/components/CodeBlock/CodeBlock.svelte +102 -0
- package/src/lib/components/CodeBlock/index.ts +3 -0
- package/src/lib/components/CodeBlock/types.ts +10 -0
- package/src/lib/components/ColorMode/ColorMode.svelte +8 -0
- package/src/lib/components/ColorMode/index.ts +2 -0
- package/src/lib/components/ColorMode/types.ts +12 -0
- package/src/lib/components/ColorPicker/ColorPicker.svelte +838 -0
- package/src/lib/components/ColorPicker/ColorPickerSwatch.svelte +32 -0
- package/src/lib/components/ColorPicker/index.ts +3 -0
- package/src/lib/components/ColorPicker/types.ts +51 -0
- package/src/lib/components/ContextMenu/ContextMenu.svelte +86 -0
- package/src/lib/components/ContextMenu/index.ts +2 -0
- package/src/lib/components/ContextMenu/types.ts +15 -0
- package/src/lib/components/DrawingSliders/DrawingSliders.svelte +379 -0
- package/src/lib/components/DrawingSliders/index.ts +1 -0
- package/src/lib/components/Editor/Editor.svelte +825 -0
- package/src/lib/components/Editor/index.ts +1 -0
- package/src/lib/components/FogSliders/FogSliders.svelte +33 -0
- package/src/lib/components/FogSliders/index.ts +1 -0
- package/src/lib/components/Hr/Hr.svelte +15 -0
- package/src/lib/components/Hr/index.ts +1 -0
- package/src/lib/components/Icon/Icon.svelte +6 -0
- package/src/lib/components/Icon/index.ts +2 -0
- package/src/lib/components/Icon/types.ts +20 -0
- package/src/lib/components/Input/DualInputSlider.svelte +126 -0
- package/src/lib/components/Input/FileInput.svelte +176 -0
- package/src/lib/components/Input/FormControl.svelte +150 -0
- package/src/lib/components/Input/FormError.svelte +37 -0
- package/src/lib/components/Input/Input.svelte +56 -0
- package/src/lib/components/Input/InputCheckbox.svelte +99 -0
- package/src/lib/components/Input/InputSlider.svelte +86 -0
- package/src/lib/components/Input/Label.svelte +19 -0
- package/src/lib/components/Input/index.ts +9 -0
- package/src/lib/components/Input/types.ts +39 -0
- package/src/lib/components/Link/Link.svelte +41 -0
- package/src/lib/components/Link/LinkBox.svelte +20 -0
- package/src/lib/components/Link/LinkOverlay.svelte +23 -0
- package/src/lib/components/Link/index.ts +4 -0
- package/src/lib/components/Link/types.ts +17 -0
- package/src/lib/components/Loading/Loader.svelte +60 -0
- package/src/lib/components/Loading/Skeleton.svelte +9 -0
- package/src/lib/components/Loading/index.ts +2 -0
- package/src/lib/components/Logo/Logo.svelte +16 -0
- package/src/lib/components/Logo/index.ts +1 -0
- package/src/lib/components/MarkerTooltip/MarkerTooltip.svelte +435 -0
- package/src/lib/components/MarkerTooltip/index.ts +1 -0
- package/src/lib/components/Menu/SelectorMenu.svelte +280 -0
- package/src/lib/components/Menu/index.ts +2 -0
- package/src/lib/components/Menu/types.ts +17 -0
- package/src/lib/components/MyCounterButton.svelte +11 -0
- package/src/lib/components/Panel/index.ts +2 -0
- package/src/lib/components/Panel/panel.svelte +18 -0
- package/src/lib/components/Panel/types.ts +8 -0
- package/src/lib/components/PersistButton/PersistButton.svelte +100 -0
- package/src/lib/components/PersistButton/index.ts +1 -0
- package/src/lib/components/Popover/Popover.svelte +81 -0
- package/src/lib/components/Popover/index.ts +2 -0
- package/src/lib/components/Popover/types.ts +19 -0
- package/src/lib/components/PropsTable/PropsTable.svelte +107 -0
- package/src/lib/components/RadialMenu/EffectPreview.svelte +36 -0
- package/src/lib/components/RadialMenu/EffectPreviewScene.svelte +194 -0
- package/src/lib/components/RadialMenu/RadialMenu.svelte +503 -0
- package/src/lib/components/RadialMenu/RadialMenuItem.svelte +176 -0
- package/src/lib/components/RadialMenu/index.ts +2 -0
- package/src/lib/components/RadialMenu/types.ts +35 -0
- package/src/lib/components/Select/Select.svelte +342 -0
- package/src/lib/components/Select/index.ts +2 -0
- package/src/lib/components/Select/types.ts +22 -0
- package/src/lib/components/Spacer/Spacer.svelte +14 -0
- package/src/lib/components/Spacer/index.ts +2 -0
- package/src/lib/components/Spacer/types.ts +5 -0
- package/src/lib/components/Stage/components/AnnotationLayer/AnnotationLayer.svelte +445 -0
- package/src/lib/components/Stage/components/AnnotationLayer/AnnotationMaterial.svelte +167 -0
- package/src/lib/components/Stage/components/AnnotationLayer/types.ts +196 -0
- package/src/lib/components/Stage/components/CursorLayer/CursorLayer.svelte +148 -0
- package/src/lib/components/Stage/components/CursorLayer/cursor.svg +26 -0
- package/src/lib/components/Stage/components/CursorLayer/index.ts +2 -0
- package/src/lib/components/Stage/components/CursorLayer/types.ts +23 -0
- package/src/lib/components/Stage/components/DrawingLayer/DrawingMaterial.svelte +364 -0
- package/src/lib/components/Stage/components/DrawingLayer/types.ts +65 -0
- package/src/lib/components/Stage/components/EdgeOverlayLayer/EdgeOverlayLayer.svelte +72 -0
- package/src/lib/components/Stage/components/EdgeOverlayLayer/types.ts +34 -0
- package/src/lib/components/Stage/components/FogLayer/FogLayer.svelte +75 -0
- package/src/lib/components/Stage/components/FogLayer/types.ts +51 -0
- package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarLayer.svelte +249 -0
- package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarMaterial.svelte +200 -0
- package/src/lib/components/Stage/components/FogOfWarLayer/types.ts +116 -0
- package/src/lib/components/Stage/components/GridLayer/GridLayer.svelte +20 -0
- package/src/lib/components/Stage/components/GridLayer/GridMaterial.svelte +69 -0
- package/src/lib/components/Stage/components/GridLayer/types.ts +79 -0
- package/src/lib/components/Stage/components/LayerInput/LayerInput.svelte +300 -0
- package/src/lib/components/Stage/components/MapLayer/MapLayer.svelte +196 -0
- package/src/lib/components/Stage/components/MapLayer/dataSources/GifDataSource.ts +265 -0
- package/src/lib/components/Stage/components/MapLayer/dataSources/IMapDataSource.ts +55 -0
- package/src/lib/components/Stage/components/MapLayer/dataSources/ImageDataSource.ts +87 -0
- package/src/lib/components/Stage/components/MapLayer/dataSources/VideoDataSource.ts +150 -0
- package/src/lib/components/Stage/components/MapLayer/dataSources/dataSourceFactory.ts +48 -0
- package/src/lib/components/Stage/components/MapLayer/dataSources/index.ts +16 -0
- package/src/lib/components/Stage/components/MapLayer/types.ts +58 -0
- package/src/lib/components/Stage/components/MarkerLayer/MarkerLayer.svelte +398 -0
- package/src/lib/components/Stage/components/MarkerLayer/MarkerToken.svelte +262 -0
- package/src/lib/components/Stage/components/MarkerLayer/types.ts +126 -0
- package/src/lib/components/Stage/components/MeasurementLayer/MeasurementLayer.svelte +364 -0
- package/src/lib/components/Stage/components/MeasurementLayer/MeasurementManager.svelte +473 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/BaseMeasurement.ts +427 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/BeamMeasurement.ts +105 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/CircleMeasurement.ts +98 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/ConeMeasurement.ts +163 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/LineMeasurement.ts +102 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/RectangleMeasurement.ts +120 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/index.ts +7 -0
- package/src/lib/components/Stage/components/MeasurementLayer/types.ts +94 -0
- package/src/lib/components/Stage/components/MeasurementLayer/utils/canvasDrawing.ts +357 -0
- package/src/lib/components/Stage/components/MeasurementLayer/utils/distanceCalculations.ts +170 -0
- package/src/lib/components/Stage/components/ParticleSystem/ParticleSystem.svelte +220 -0
- package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/ash.png +0 -0
- package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/leaves.png +0 -0
- package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/rain.png +0 -0
- package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/snow.png +0 -0
- package/src/lib/components/Stage/components/ParticleSystem/rng.js +20 -0
- package/src/lib/components/Stage/components/ParticleSystem/types.ts +95 -0
- package/src/lib/components/Stage/components/PerformanceDebugger/PerformanceDebugger.svelte +144 -0
- package/src/lib/components/Stage/components/PerformanceDebugger/index.ts +1 -0
- package/src/lib/components/Stage/components/PerformanceOverlay/PerformanceOverlay.svelte +208 -0
- package/src/lib/components/Stage/components/PerformanceOverlay/index.ts +1 -0
- package/src/lib/components/Stage/components/PointerInputManager/PointerInputManager.svelte +201 -0
- package/src/lib/components/Stage/components/Scene/Scene.svelte +651 -0
- package/src/lib/components/Stage/components/Scene/luts.ts +24 -0
- package/src/lib/components/Stage/components/Scene/types.ts +225 -0
- package/src/lib/components/Stage/components/Stage/Stage.svelte +332 -0
- package/src/lib/components/Stage/components/Stage/types.ts +136 -0
- package/src/lib/components/Stage/components/WeatherLayer/WeatherLayer.svelte +135 -0
- package/src/lib/components/Stage/components/WeatherLayer/presets/AshPreset.ts +71 -0
- package/src/lib/components/Stage/components/WeatherLayer/presets/LeavesPreset.ts +70 -0
- package/src/lib/components/Stage/components/WeatherLayer/presets/RainPreset.ts +68 -0
- package/src/lib/components/Stage/components/WeatherLayer/presets/SnowPreset.ts +70 -0
- package/src/lib/components/Stage/components/WeatherLayer/presets/index.ts +6 -0
- package/src/lib/components/Stage/components/WeatherLayer/types.ts +35 -0
- package/src/lib/components/Stage/helpers/clippingPlaneStore.svelte.ts +28 -0
- package/src/lib/components/Stage/helpers/debugState.svelte.ts +18 -0
- package/src/lib/components/Stage/helpers/grid.ts +548 -0
- package/src/lib/components/Stage/helpers/lazyBrush.ts +171 -0
- package/src/lib/components/Stage/helpers/performanceMetrics.svelte.ts +220 -0
- package/src/lib/components/Stage/helpers/utils.ts +21 -0
- package/src/lib/components/Stage/index.ts +49 -0
- package/src/lib/components/Stage/shaders/AnnotationEffects.frag +1070 -0
- package/src/lib/components/Stage/shaders/Annotations.frag +29 -0
- package/src/lib/components/Stage/shaders/Drawing.frag +83 -0
- package/src/lib/components/Stage/shaders/Drawing.vert +5 -0
- package/src/lib/components/Stage/shaders/Fog.frag +147 -0
- package/src/lib/components/Stage/shaders/FractalNoise.frag +96 -0
- package/src/lib/components/Stage/shaders/GridShader.frag +174 -0
- package/src/lib/components/Stage/shaders/Overlay.frag +23 -0
- package/src/lib/components/Stage/shaders/Overlay.vert +0 -0
- package/src/lib/components/Stage/shaders/Particles.frag +27 -0
- package/src/lib/components/Stage/shaders/Particles.vert +51 -0
- package/src/lib/components/Stage/shaders/ToolOutline.frag +59 -0
- package/src/lib/components/Stage/shaders/default.vert +8 -0
- package/src/lib/components/Stage/types.ts +4 -0
- package/src/lib/components/Table/Table.svelte +16 -0
- package/src/lib/components/Table/Td.svelte +17 -0
- package/src/lib/components/Table/Th.svelte +18 -0
- package/src/lib/components/Table/index.ts +4 -0
- package/src/lib/components/Table/types.ts +14 -0
- package/src/lib/components/Text/Text.svelte +23 -0
- package/src/lib/components/Text/index.ts +2 -0
- package/src/lib/components/Text/types.ts +12 -0
- package/src/lib/components/Title/Title.svelte +54 -0
- package/src/lib/components/Title/index.ts +2 -0
- package/src/lib/components/Title/types.ts +9 -0
- package/src/lib/components/Toast/Toast.svelte +155 -0
- package/src/lib/components/Toast/index.ts +5 -0
- package/src/lib/components/Toast/toastCookie.ts +24 -0
- package/src/lib/components/Toast/types.ts +6 -0
- package/src/lib/components/ToolTip/ToolTip.svelte +70 -0
- package/src/lib/components/ToolTip/index.ts +2 -0
- package/src/lib/components/ToolTip/types.ts +14 -0
- package/src/lib/components/index.ts +32 -0
- package/src/lib/components/types.ts +0 -0
- package/src/lib/index.ts +2 -0
- package/src/lib/styles/globals.css +108 -0
- package/src/lib/styles/normalize.css +9 -0
- package/src/lib/styles/reset.css +133 -0
- package/src/lib/styles/utilities.css +179 -0
- package/src/lib/styles/vars.css +1103 -0
- package/src/lib/types/awareness.ts +17 -0
- 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
|
+
}
|