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