@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,651 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { getContext, onMount, onDestroy, untrack } from 'svelte';
|
|
4
|
+
import { T, useThrelte, useTask } from '@threlte/core';
|
|
5
|
+
import {
|
|
6
|
+
EffectComposer,
|
|
7
|
+
EffectPass,
|
|
8
|
+
RenderPass,
|
|
9
|
+
BloomEffect,
|
|
10
|
+
VignetteEffect,
|
|
11
|
+
ChromaticAberrationEffect,
|
|
12
|
+
BlendFunction,
|
|
13
|
+
ToneMappingEffect,
|
|
14
|
+
ToneMappingMode,
|
|
15
|
+
LUT3DEffect
|
|
16
|
+
} from 'postprocessing';
|
|
17
|
+
import { getLUT } from './luts';
|
|
18
|
+
import { type Callbacks, type StageProps } from '../Stage/types';
|
|
19
|
+
import { MapLayerType, type MapLayerExports } from '../MapLayer/types';
|
|
20
|
+
import { clippingPlaneStore, updateClippingPlanes } from '../../helpers/clippingPlaneStore.svelte';
|
|
21
|
+
import { beginFrame, endFrame, startTiming, endTiming, logMetrics } from '../../helpers/performanceMetrics.svelte';
|
|
22
|
+
import { debugState } from '../../helpers/debugState.svelte';
|
|
23
|
+
import { getGridCellSize as getGridCellSizeHelper } from '../../helpers/grid';
|
|
24
|
+
import { SceneLayer, SceneLayerOrder, SceneLoadingState } from './types';
|
|
25
|
+
import type { AnnotationExports } from '../AnnotationLayer/types';
|
|
26
|
+
import AnnotationLayer from '../AnnotationLayer/AnnotationLayer.svelte';
|
|
27
|
+
import CursorLayer from '../CursorLayer/CursorLayer.svelte';
|
|
28
|
+
import type { CursorData } from '../CursorLayer/types';
|
|
29
|
+
import EdgeOverlayLayer from '../EdgeOverlayLayer/EdgeOverlayLayer.svelte';
|
|
30
|
+
import GridLayer from '../GridLayer/GridLayer.svelte';
|
|
31
|
+
import MapLayer from '../MapLayer/MapLayer.svelte';
|
|
32
|
+
import MarkerLayer from '../MarkerLayer/MarkerLayer.svelte';
|
|
33
|
+
import MeasurementLayer from '../MeasurementLayer/MeasurementLayer.svelte';
|
|
34
|
+
import type { MarkerLayerExports } from '../MarkerLayer/types';
|
|
35
|
+
import WeatherLayer from '../WeatherLayer/WeatherLayer.svelte';
|
|
36
|
+
|
|
37
|
+
interface Props {
|
|
38
|
+
props: StageProps;
|
|
39
|
+
cursors?: CursorData[];
|
|
40
|
+
trackLocalCursor?: boolean;
|
|
41
|
+
receivedMeasurement?: {
|
|
42
|
+
startPoint: { x: number; y: number };
|
|
43
|
+
endPoint: { x: number; y: number };
|
|
44
|
+
type: number;
|
|
45
|
+
beamWidth?: number;
|
|
46
|
+
coneAngle?: number;
|
|
47
|
+
// Visual properties
|
|
48
|
+
color?: string;
|
|
49
|
+
thickness?: number;
|
|
50
|
+
outlineColor?: string;
|
|
51
|
+
outlineThickness?: number;
|
|
52
|
+
opacity?: number;
|
|
53
|
+
markerSize?: number;
|
|
54
|
+
// Timing properties
|
|
55
|
+
autoHideDelay?: number;
|
|
56
|
+
fadeoutTime?: number;
|
|
57
|
+
// Distance properties
|
|
58
|
+
showDistance?: boolean;
|
|
59
|
+
snapToGrid?: boolean;
|
|
60
|
+
enableDMG252?: boolean;
|
|
61
|
+
} | null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let { props, receivedMeasurement = null, cursors = [], trackLocalCursor = false }: Props = $props();
|
|
65
|
+
|
|
66
|
+
const { scene, renderer, camera, size, autoRender, renderStage } = useThrelte();
|
|
67
|
+
|
|
68
|
+
const callbacks = getContext<Callbacks>('callbacks');
|
|
69
|
+
const onSceneUpdate = callbacks.onSceneUpdate;
|
|
70
|
+
|
|
71
|
+
// Type definition for MeasurementLayer exports
|
|
72
|
+
type MeasurementLayerExports = {
|
|
73
|
+
getCurrentMeasurement: () => {
|
|
74
|
+
startPoint: THREE.Vector2 | null;
|
|
75
|
+
endPoint: THREE.Vector2 | null;
|
|
76
|
+
type: number;
|
|
77
|
+
} | null;
|
|
78
|
+
isCurrentlyDrawing: () => boolean;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
let annotationsLayer: AnnotationExports;
|
|
82
|
+
let mapLayer: MapLayerExports;
|
|
83
|
+
let markerLayer: MarkerLayerExports;
|
|
84
|
+
let measurementLayer: MeasurementLayerExports | null = $state(null);
|
|
85
|
+
let needsResize = true;
|
|
86
|
+
let loadingState = SceneLoadingState.LoadingMap;
|
|
87
|
+
|
|
88
|
+
// Local cursor tracking
|
|
89
|
+
let raycaster = new THREE.Raycaster();
|
|
90
|
+
raycaster.layers.enable(SceneLayer.Main);
|
|
91
|
+
|
|
92
|
+
// Pre-allocated objects for mouse tracking to avoid GC pressure
|
|
93
|
+
const mouseNDC = new THREE.Vector2();
|
|
94
|
+
const intersectionPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
|
|
95
|
+
const intersectionPoint = new THREE.Vector3();
|
|
96
|
+
|
|
97
|
+
let composer = new EffectComposer(renderer);
|
|
98
|
+
|
|
99
|
+
onMount(() => {
|
|
100
|
+
let before = autoRender.current;
|
|
101
|
+
autoRender.set(false);
|
|
102
|
+
renderer.autoClear = false;
|
|
103
|
+
renderer.setClearColor(0, 0);
|
|
104
|
+
// Cap pixel ratio for performance on weak GPUs (e.g., Mac Mini)
|
|
105
|
+
const maxDpr = props.display.maxPixelRatio ?? 2;
|
|
106
|
+
const dpr = Math.min(window.devicePixelRatio, maxDpr);
|
|
107
|
+
renderer.setPixelRatio(dpr);
|
|
108
|
+
renderer.localClippingEnabled = true;
|
|
109
|
+
|
|
110
|
+
// Add mouse tracking if enabled
|
|
111
|
+
if (trackLocalCursor && callbacks.onCursorMove) {
|
|
112
|
+
const handleMouseMove = (event: MouseEvent) => {
|
|
113
|
+
// Convert mouse position to normalized device coordinates
|
|
114
|
+
const rect = renderer.domElement.getBoundingClientRect();
|
|
115
|
+
mouseNDC.set(
|
|
116
|
+
((event.clientX - rect.left) / rect.width) * 2 - 1,
|
|
117
|
+
-((event.clientY - rect.top) / rect.height) * 2 + 1
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Update raycaster with camera and mouse position
|
|
121
|
+
raycaster.setFromCamera(mouseNDC, $camera);
|
|
122
|
+
|
|
123
|
+
// Find intersection with the z=0 plane using ray.intersectPlane (no allocations)
|
|
124
|
+
const hit = raycaster.ray.intersectPlane(intersectionPlane, intersectionPoint);
|
|
125
|
+
|
|
126
|
+
if (hit) {
|
|
127
|
+
// Account for scene transform (offset and zoom)
|
|
128
|
+
const adjustedPos = {
|
|
129
|
+
x: (intersectionPoint.x - props.scene.offset.x) / props.scene.zoom,
|
|
130
|
+
y: (intersectionPoint.y - props.scene.offset.y) / props.scene.zoom,
|
|
131
|
+
z: 0
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
callbacks.onCursorMove?.(adjustedPos);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
renderer.domElement.addEventListener('mousemove', handleMouseMove);
|
|
139
|
+
|
|
140
|
+
return () => {
|
|
141
|
+
autoRender.set(before);
|
|
142
|
+
renderer.domElement.removeEventListener('mousemove', handleMouseMove);
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return () => {
|
|
147
|
+
autoRender.set(before);
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
onDestroy(() => {
|
|
152
|
+
composer.dispose();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Setup camera and renderer in effect
|
|
156
|
+
$effect(() => {
|
|
157
|
+
if (!camera) return;
|
|
158
|
+
|
|
159
|
+
// Configure camera to see both layers
|
|
160
|
+
$camera.layers.disableAll();
|
|
161
|
+
$camera.layers.enable(SceneLayer.Main);
|
|
162
|
+
$camera.layers.enable(SceneLayer.Overlay);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Whenever the scene or display properties change, update the clipping planes
|
|
166
|
+
$effect(() => {
|
|
167
|
+
updateClippingPlanes(props.scene, props.display);
|
|
168
|
+
untrack(() => (renderer.clippingPlanes = clippingPlaneStore.value));
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Update needsResize when map URL changes
|
|
172
|
+
$effect(() => {
|
|
173
|
+
const mapUrl = props.map.url;
|
|
174
|
+
if (mapUrl) {
|
|
175
|
+
needsResize = true;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Effect to update post-processing settings when props change
|
|
180
|
+
$effect(() => {
|
|
181
|
+
const postProcessing = $state.snapshot(props.postProcessing);
|
|
182
|
+
|
|
183
|
+
// Need to convert the LUT to a LookupTexture
|
|
184
|
+
Promise.resolve(getLUT(postProcessing.lut.url))
|
|
185
|
+
.then((lut) => {
|
|
186
|
+
composer.dispose();
|
|
187
|
+
composer = new EffectComposer(renderer);
|
|
188
|
+
|
|
189
|
+
const effects = [];
|
|
190
|
+
|
|
191
|
+
const renderPass = new RenderPass(scene, $camera);
|
|
192
|
+
composer.addPass(renderPass);
|
|
193
|
+
|
|
194
|
+
if (postProcessing.enabled) {
|
|
195
|
+
if (postProcessing.bloom.enabled) {
|
|
196
|
+
const bloomEffect = new BloomEffect({
|
|
197
|
+
intensity: postProcessing.bloom.intensity,
|
|
198
|
+
mipmapBlur: postProcessing.bloom.mipmapBlur,
|
|
199
|
+
radius: postProcessing.bloom.radius,
|
|
200
|
+
levels: postProcessing.bloom.levels,
|
|
201
|
+
luminanceThreshold: postProcessing.bloom.threshold,
|
|
202
|
+
luminanceSmoothing: postProcessing.bloom.smoothing
|
|
203
|
+
});
|
|
204
|
+
effects.push(bloomEffect);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (postProcessing.chromaticAberration.enabled) {
|
|
208
|
+
const chromaticAberrationEffect = new ChromaticAberrationEffect({
|
|
209
|
+
offset: new THREE.Vector2(postProcessing.chromaticAberration.offset),
|
|
210
|
+
radialModulation: true,
|
|
211
|
+
modulationOffset: 0.025
|
|
212
|
+
});
|
|
213
|
+
effects.push(chromaticAberrationEffect);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (postProcessing.vignette.enabled) {
|
|
217
|
+
const vignetteEffect = new VignetteEffect({
|
|
218
|
+
offset: postProcessing.vignette.offset,
|
|
219
|
+
darkness: postProcessing.vignette.darkness,
|
|
220
|
+
blendFunction: BlendFunction.NORMAL
|
|
221
|
+
});
|
|
222
|
+
effects.push(vignetteEffect);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (postProcessing.lut.enabled && postProcessing.lut.url !== null) {
|
|
226
|
+
const lutEffect = new LUT3DEffect(new THREE.Data3DTexture(), {
|
|
227
|
+
blendFunction: BlendFunction.SET
|
|
228
|
+
});
|
|
229
|
+
lutEffect.setSize($size.width, $size.height);
|
|
230
|
+
|
|
231
|
+
if (!lut) return;
|
|
232
|
+
lutEffect.lut.dispose();
|
|
233
|
+
lutEffect.lut = lut;
|
|
234
|
+
|
|
235
|
+
effects.push(lutEffect);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Add final tonemapping pass
|
|
239
|
+
const toneMappingEffect = new ToneMappingEffect({
|
|
240
|
+
mode:
|
|
241
|
+
postProcessing.enabled && postProcessing.toneMapping.enabled
|
|
242
|
+
? postProcessing.toneMapping.mode
|
|
243
|
+
: ToneMappingMode.LINEAR
|
|
244
|
+
});
|
|
245
|
+
effects.push(toneMappingEffect);
|
|
246
|
+
|
|
247
|
+
const effectPass = new EffectPass($camera, ...effects);
|
|
248
|
+
composer.addPass(effectPass);
|
|
249
|
+
}
|
|
250
|
+
})
|
|
251
|
+
.catch((error) => console.error(error));
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
$effect(() => {
|
|
255
|
+
const renderSize = new THREE.Vector2();
|
|
256
|
+
renderer.getSize(renderSize);
|
|
257
|
+
|
|
258
|
+
// Only update render/composer size if it doesn't match the canvas size
|
|
259
|
+
// This check must be done here; it does not work when placed in $effect
|
|
260
|
+
if (
|
|
261
|
+
renderSize.width !== $size.width ||
|
|
262
|
+
renderSize.height !== $size.height ||
|
|
263
|
+
composer.outputBuffer.width !== $size.width ||
|
|
264
|
+
composer.outputBuffer.height !== $size.height
|
|
265
|
+
) {
|
|
266
|
+
needsResize = true;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// Check if any post-processing effects are active
|
|
271
|
+
const hasActiveEffects = $derived(() => {
|
|
272
|
+
const pp = props.postProcessing;
|
|
273
|
+
if (!pp.enabled) return false;
|
|
274
|
+
return pp.bloom.enabled || pp.chromaticAberration.enabled || pp.vignette.enabled || pp.lut.enabled;
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Custom render task
|
|
278
|
+
useTask(
|
|
279
|
+
(dt) => {
|
|
280
|
+
if (!scene || !renderer || !camera) return;
|
|
281
|
+
|
|
282
|
+
const enableMetrics = debugState.enableMetrics;
|
|
283
|
+
const frameStart = enableMetrics ? beginFrame() : 0;
|
|
284
|
+
|
|
285
|
+
if (needsResize) {
|
|
286
|
+
needsResize = false;
|
|
287
|
+
renderer.setSize($size.width, $size.height);
|
|
288
|
+
composer.setSize($size.width, $size.height);
|
|
289
|
+
if (props.scene.autoFit) {
|
|
290
|
+
fit();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
renderer.clear();
|
|
295
|
+
|
|
296
|
+
// Render main scene with post-processing (or bypass if no effects active)
|
|
297
|
+
camera.current.layers.set(SceneLayer.Main);
|
|
298
|
+
|
|
299
|
+
let composerTime = 0;
|
|
300
|
+
if (hasActiveEffects()) {
|
|
301
|
+
const composerStart = enableMetrics ? startTiming() : 0;
|
|
302
|
+
composer.render(dt);
|
|
303
|
+
composerTime = enableMetrics ? endTiming(composerStart) : 0;
|
|
304
|
+
} else {
|
|
305
|
+
const composerStart = enableMetrics ? startTiming() : 0;
|
|
306
|
+
renderer.render(scene, camera.current);
|
|
307
|
+
composerTime = enableMetrics ? endTiming(composerStart) : 0;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Render overlays (grid/ping) without post-processing
|
|
311
|
+
camera.current.layers.set(SceneLayer.Overlay);
|
|
312
|
+
|
|
313
|
+
const overlayStart = enableMetrics ? startTiming() : 0;
|
|
314
|
+
renderer.render(scene, camera.current);
|
|
315
|
+
const overlayTime = enableMetrics ? endTiming(overlayStart) : 0;
|
|
316
|
+
|
|
317
|
+
// Reset camera back to main layer
|
|
318
|
+
camera.current.layers.set(SceneLayer.Main);
|
|
319
|
+
|
|
320
|
+
// Update metrics if enabled
|
|
321
|
+
if (enableMetrics) {
|
|
322
|
+
endFrame(frameStart, renderer, { composerTime, overlayTime });
|
|
323
|
+
|
|
324
|
+
if (debugState.logMetricsToConsole) {
|
|
325
|
+
logMetrics(props.debug.loggingRate);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// If scene was resized, need to wait for prop update to finish
|
|
330
|
+
if (loadingState === SceneLoadingState.Resizing) {
|
|
331
|
+
setLoadingState(SceneLoadingState.Rendering);
|
|
332
|
+
} else if (loadingState === SceneLoadingState.Rendering) {
|
|
333
|
+
setLoadingState(SceneLoadingState.Initialized);
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
{ stage: renderStage }
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
function setLoadingState(state: SceneLoadingState) {
|
|
340
|
+
loadingState = state;
|
|
341
|
+
if (state === SceneLoadingState.Initialized) {
|
|
342
|
+
callbacks.onStageInitialized();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function fill() {
|
|
347
|
+
const canvasAspectRatio = renderer.domElement.clientWidth / renderer.domElement.clientHeight;
|
|
348
|
+
let sceneAspectRatio = props.display.resolution.x / props.display.resolution.y;
|
|
349
|
+
let sceneWidth = props.display.resolution.x;
|
|
350
|
+
let sceneHeight = props.display.resolution.y;
|
|
351
|
+
|
|
352
|
+
// Swap dimensions if rotated 90 or 270 degrees
|
|
353
|
+
if (props.scene.rotation === 90 || props.scene.rotation === 270) {
|
|
354
|
+
sceneAspectRatio = props.display.resolution.y / props.display.resolution.x;
|
|
355
|
+
sceneWidth = props.display.resolution.y;
|
|
356
|
+
sceneHeight = props.display.resolution.x;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
let newZoom: number;
|
|
360
|
+
if (sceneAspectRatio > canvasAspectRatio) {
|
|
361
|
+
newZoom = renderer.domElement.clientHeight / sceneHeight;
|
|
362
|
+
} else {
|
|
363
|
+
newZoom = renderer.domElement.clientWidth / sceneWidth;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
onSceneUpdate({ x: 0, y: 0 }, newZoom);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export function fit() {
|
|
370
|
+
const canvasAspectRatio = $size.width / $size.height;
|
|
371
|
+
let sceneAspectRatio = props.display.resolution.x / props.display.resolution.y;
|
|
372
|
+
let sceneWidth = props.display.resolution.x;
|
|
373
|
+
let sceneHeight = props.display.resolution.y;
|
|
374
|
+
|
|
375
|
+
// Swap dimensions if rotated 90 or 270 degrees
|
|
376
|
+
if (props.scene.rotation === 90 || props.scene.rotation === 270) {
|
|
377
|
+
sceneAspectRatio = props.display.resolution.y / props.display.resolution.x;
|
|
378
|
+
sceneWidth = props.display.resolution.y;
|
|
379
|
+
sceneHeight = props.display.resolution.x;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
let newZoom: number;
|
|
383
|
+
if (sceneAspectRatio < canvasAspectRatio) {
|
|
384
|
+
newZoom = $size.height / sceneHeight;
|
|
385
|
+
} else {
|
|
386
|
+
newZoom = $size.width / sceneWidth;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
onSceneUpdate({ x: 0, y: 0 }, newZoom);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export async function generateThumbnail(quality: number = 0.5): Promise<Blob> {
|
|
393
|
+
const texture = mapLayer.getCompositeMapTexture();
|
|
394
|
+
|
|
395
|
+
if (!texture) return new Blob();
|
|
396
|
+
|
|
397
|
+
// Store original scene state
|
|
398
|
+
const originalScene = scene;
|
|
399
|
+
const originalCamera = camera.current;
|
|
400
|
+
const originalSize = $size;
|
|
401
|
+
|
|
402
|
+
const displayWidth = props.display.resolution.x;
|
|
403
|
+
const displayHeight = props.display.resolution.y;
|
|
404
|
+
|
|
405
|
+
// Handle both image and video textures
|
|
406
|
+
let imageWidth: number;
|
|
407
|
+
let imageHeight: number;
|
|
408
|
+
|
|
409
|
+
const textureImage = texture.image as HTMLVideoElement | HTMLImageElement;
|
|
410
|
+
if (textureImage instanceof HTMLVideoElement) {
|
|
411
|
+
// For video textures, use videoWidth and videoHeight
|
|
412
|
+
imageWidth = textureImage.videoWidth || displayWidth;
|
|
413
|
+
imageHeight = textureImage.videoHeight || displayHeight;
|
|
414
|
+
} else {
|
|
415
|
+
// For image textures, use width and height
|
|
416
|
+
imageWidth = textureImage.width;
|
|
417
|
+
imageHeight = textureImage.height;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Create a temporary scene and camera for rendering
|
|
421
|
+
const tempScene = new THREE.Scene();
|
|
422
|
+
const tempCamera = new THREE.OrthographicCamera(
|
|
423
|
+
-displayWidth / 2,
|
|
424
|
+
displayWidth / 2,
|
|
425
|
+
displayHeight / 2,
|
|
426
|
+
-displayHeight / 2,
|
|
427
|
+
0.1,
|
|
428
|
+
1000
|
|
429
|
+
);
|
|
430
|
+
tempCamera.position.z = 100;
|
|
431
|
+
|
|
432
|
+
// Create a quad to render the texture
|
|
433
|
+
const geometry = new THREE.PlaneGeometry(1, 1);
|
|
434
|
+
const material = new THREE.MeshBasicMaterial({ map: texture.clone() });
|
|
435
|
+
const quad = new THREE.Mesh(geometry, material);
|
|
436
|
+
quad.position.set(props.map.offset.x, props.map.offset.y, 0);
|
|
437
|
+
quad.rotation.z = (props.map.rotation / 180.0) * Math.PI;
|
|
438
|
+
quad.scale.set(imageWidth * props.map.zoom, imageHeight * props.map.zoom, 1);
|
|
439
|
+
tempScene.add(quad);
|
|
440
|
+
|
|
441
|
+
// Temporarily replace scene and camera
|
|
442
|
+
composer.setMainScene(tempScene);
|
|
443
|
+
composer.setMainCamera(tempCamera);
|
|
444
|
+
renderer.setSize(displayWidth, displayHeight);
|
|
445
|
+
composer.setSize(displayWidth, displayHeight);
|
|
446
|
+
|
|
447
|
+
// Temporarily disable clipping planes
|
|
448
|
+
renderer.clippingPlanes = [];
|
|
449
|
+
|
|
450
|
+
// Render to the offscreen canvas
|
|
451
|
+
composer.render();
|
|
452
|
+
|
|
453
|
+
const offscreenCanvas = new OffscreenCanvas(displayWidth, displayHeight);
|
|
454
|
+
const context = offscreenCanvas.getContext('2d');
|
|
455
|
+
context?.drawImage(renderer.domElement, 0, 0, displayWidth, displayHeight);
|
|
456
|
+
|
|
457
|
+
// Restore original state
|
|
458
|
+
composer.setMainScene(originalScene);
|
|
459
|
+
composer.setMainCamera(originalCamera);
|
|
460
|
+
renderer.setSize(originalSize.width, originalSize.height);
|
|
461
|
+
composer.setSize(originalSize.width, originalSize.height);
|
|
462
|
+
renderer.clippingPlanes = clippingPlaneStore.value;
|
|
463
|
+
|
|
464
|
+
// Clean up
|
|
465
|
+
geometry.dispose();
|
|
466
|
+
material.dispose();
|
|
467
|
+
|
|
468
|
+
return offscreenCanvas.convertToBlob({ type: 'image/jpeg', quality });
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export const annotations = {
|
|
472
|
+
clear: (layerId: string) => annotationsLayer.clear(layerId),
|
|
473
|
+
toRLE: () => annotationsLayer?.toRLE(),
|
|
474
|
+
fromRLE: (rleData: Uint8Array, width: number, height: number) => annotationsLayer?.fromRLE(rleData, width, height),
|
|
475
|
+
loadMask: (layerId: string, rleData: Uint8Array) => annotationsLayer?.loadMask(layerId, rleData),
|
|
476
|
+
isDrawing: () => annotationsLayer?.isDrawing() ?? false
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
export const map = {
|
|
480
|
+
fill: () => mapLayer.fill(),
|
|
481
|
+
fit: () => mapLayer.fit(),
|
|
482
|
+
getSize: () => mapLayer?.mapSize ?? null
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
// References to the layer doesn't exist until the component is mounted,
|
|
486
|
+
// so we need create these wrapper functions
|
|
487
|
+
export const fogOfWar = {
|
|
488
|
+
clear: () => mapLayer.fogOfWar.clear(),
|
|
489
|
+
reset: () => mapLayer.fogOfWar.reset(),
|
|
490
|
+
toPng: () => mapLayer.fogOfWar.toPng(),
|
|
491
|
+
toRLE: () => mapLayer.fogOfWar.toRLE(),
|
|
492
|
+
fromRLE: (rleData: Uint8Array, width: number, height: number) => mapLayer.fogOfWar.fromRLE(rleData, width, height),
|
|
493
|
+
isDrawing: () => mapLayer?.fogOfWar?.isDrawing() ?? false
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
// Export marker state getters
|
|
497
|
+
export const markers = {
|
|
498
|
+
get isHoveringMarker() {
|
|
499
|
+
return markerLayer?.markerState?.isHovering ?? false;
|
|
500
|
+
},
|
|
501
|
+
get isDraggingMarker() {
|
|
502
|
+
return markerLayer?.markerState?.isDragging ?? false;
|
|
503
|
+
},
|
|
504
|
+
get hoveredMarker() {
|
|
505
|
+
return markerLayer?.markerState?.hoveredMarker ?? null;
|
|
506
|
+
},
|
|
507
|
+
get selectedMarker() {
|
|
508
|
+
return markerLayer?.markerState?.selectedMarker ?? null;
|
|
509
|
+
},
|
|
510
|
+
maintainHover: (maintain: boolean) => {
|
|
511
|
+
markerLayer?.maintainHover?.(maintain);
|
|
512
|
+
},
|
|
513
|
+
onSceneChange: () => {
|
|
514
|
+
markerLayer?.onSceneChange?.();
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
// Export measurement layer methods
|
|
519
|
+
export const measurement = {
|
|
520
|
+
getCurrentMeasurement: () => measurementLayer?.getCurrentMeasurement?.() ?? null,
|
|
521
|
+
isDrawing: () => measurementLayer?.isCurrentlyDrawing?.() ?? false
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
export function getMarkerSizeInScreenSpace(markerSize = 1) {
|
|
525
|
+
const worldGridSize = getGridCellSizeHelper(props.grid, props.display);
|
|
526
|
+
const worldMarkerDiameter = worldGridSize * markerSize * 0.9;
|
|
527
|
+
const zoomedMarkerDiameter = worldMarkerDiameter * props.scene.zoom;
|
|
528
|
+
const screenMarkerDiameter = (zoomedMarkerDiameter / props.display.resolution.x) * $size.width;
|
|
529
|
+
|
|
530
|
+
return screenMarkerDiameter;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export function getMarkerScreenPosition(marker: { position?: { x: number; y: number } }) {
|
|
534
|
+
if (!marker?.position) return null;
|
|
535
|
+
|
|
536
|
+
// Create a vector at the marker's local position
|
|
537
|
+
const vector = new THREE.Vector3(marker.position.x, marker.position.y, 0);
|
|
538
|
+
|
|
539
|
+
// Apply scene transformations to get world position
|
|
540
|
+
// The markers are rendered inside a T.Object3D with position and scale transforms
|
|
541
|
+
vector.x = vector.x * props.scene.zoom + props.scene.offset.x;
|
|
542
|
+
vector.y = vector.y * props.scene.zoom + props.scene.offset.y;
|
|
543
|
+
|
|
544
|
+
// Project world position to screen space
|
|
545
|
+
vector.project(camera.current);
|
|
546
|
+
|
|
547
|
+
// Convert from normalized device coordinates (-1 to 1) to screen coordinates
|
|
548
|
+
const x = (vector.x * 0.5 + 0.5) * $size.width;
|
|
549
|
+
const y = (-vector.y * 0.5 + 0.5) * $size.height;
|
|
550
|
+
|
|
551
|
+
return { x, y };
|
|
552
|
+
}
|
|
553
|
+
</script>
|
|
554
|
+
|
|
555
|
+
<T.OrthographicCamera
|
|
556
|
+
makeDefault
|
|
557
|
+
near={0.1}
|
|
558
|
+
far={1000}
|
|
559
|
+
rotation={[0, 0, (props.scene.rotation * Math.PI) / 180]}
|
|
560
|
+
position={[0, 0, 100]}
|
|
561
|
+
></T.OrthographicCamera>
|
|
562
|
+
|
|
563
|
+
<!-- Scene -->
|
|
564
|
+
<T.Object3D position={[props.scene.offset.x, props.scene.offset.y, 0]} scale={[props.scene.zoom, props.scene.zoom, 1]}>
|
|
565
|
+
<MapLayer
|
|
566
|
+
bind:this={mapLayer}
|
|
567
|
+
{props}
|
|
568
|
+
onMapLoading={() => {
|
|
569
|
+
callbacks.onStageLoading();
|
|
570
|
+
setLoadingState(SceneLoadingState.LoadingMap);
|
|
571
|
+
}}
|
|
572
|
+
onMapLoaded={() => {
|
|
573
|
+
needsResize = true;
|
|
574
|
+
if (loadingState === SceneLoadingState.LoadingMap) {
|
|
575
|
+
setLoadingState(SceneLoadingState.Resizing);
|
|
576
|
+
}
|
|
577
|
+
}}
|
|
578
|
+
/>
|
|
579
|
+
|
|
580
|
+
<WeatherLayer
|
|
581
|
+
{props}
|
|
582
|
+
size={props.display.resolution}
|
|
583
|
+
layers={[SceneLayer.Main]}
|
|
584
|
+
renderOrder={SceneLayerOrder.Weather}
|
|
585
|
+
/>
|
|
586
|
+
|
|
587
|
+
<!-- Map overlays that scale with the scene -->
|
|
588
|
+
<GridLayer
|
|
589
|
+
grid={props.grid}
|
|
590
|
+
display={props.display}
|
|
591
|
+
sceneZoom={props.scene.zoom}
|
|
592
|
+
layers={[SceneLayer.Overlay]}
|
|
593
|
+
renderOrder={SceneLayerOrder.Grid}
|
|
594
|
+
/>
|
|
595
|
+
|
|
596
|
+
<EdgeOverlayLayer
|
|
597
|
+
props={props.edgeOverlay}
|
|
598
|
+
display={props.display}
|
|
599
|
+
visible={props.edgeOverlay.enabled}
|
|
600
|
+
layers={[SceneLayer.Overlay]}
|
|
601
|
+
renderOrder={SceneLayerOrder.EdgeOverlay}
|
|
602
|
+
/>
|
|
603
|
+
|
|
604
|
+
<AnnotationLayer
|
|
605
|
+
bind:this={annotationsLayer}
|
|
606
|
+
props={props.annotations}
|
|
607
|
+
mode={props.mode}
|
|
608
|
+
isActive={props.activeLayer === MapLayerType.Annotation}
|
|
609
|
+
sceneZoom={props.scene.zoom}
|
|
610
|
+
display={props.display}
|
|
611
|
+
/>
|
|
612
|
+
|
|
613
|
+
<MarkerLayer
|
|
614
|
+
bind:this={markerLayer}
|
|
615
|
+
{props}
|
|
616
|
+
isActive={props.activeLayer === MapLayerType.Marker || props.activeLayer === MapLayerType.None}
|
|
617
|
+
grid={props.grid}
|
|
618
|
+
display={props.display}
|
|
619
|
+
/>
|
|
620
|
+
|
|
621
|
+
{#if props.measurement}
|
|
622
|
+
<MeasurementLayer
|
|
623
|
+
bind:this={measurementLayer}
|
|
624
|
+
props={props.measurement}
|
|
625
|
+
isActive={props.activeLayer === MapLayerType.Measurement}
|
|
626
|
+
display={props.display}
|
|
627
|
+
grid={props.grid}
|
|
628
|
+
sceneRotation={props.scene.rotation}
|
|
629
|
+
onMeasurementStart={callbacks.onMeasurementStart}
|
|
630
|
+
onMeasurementUpdate={callbacks.onMeasurementUpdate}
|
|
631
|
+
onMeasurementEnd={callbacks.onMeasurementEnd}
|
|
632
|
+
{receivedMeasurement}
|
|
633
|
+
/>
|
|
634
|
+
{:else}
|
|
635
|
+
<!-- MeasurementLayer skipped: props.measurement is undefined -->
|
|
636
|
+
{/if}
|
|
637
|
+
|
|
638
|
+
<!-- Cursor Layer for rendering remote cursors -->
|
|
639
|
+
<CursorLayer
|
|
640
|
+
props={{
|
|
641
|
+
cursors: cursors,
|
|
642
|
+
showLabels: true,
|
|
643
|
+
fadeOutDelay: 5000,
|
|
644
|
+
fadeOutDuration: 500,
|
|
645
|
+
gridSpacing: props.grid.spacing,
|
|
646
|
+
displaySize: props.display.size,
|
|
647
|
+
displayResolution: props.display.resolution,
|
|
648
|
+
sceneZoom: props.scene.zoom
|
|
649
|
+
}}
|
|
650
|
+
/>
|
|
651
|
+
</T.Object3D>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { LookupTexture } from 'postprocessing';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { LUTCubeLoader } from 'three/examples/jsm/loaders/LUTCubeLoader';
|
|
4
|
+
|
|
5
|
+
const loadingManager = new THREE.LoadingManager();
|
|
6
|
+
const lutLoader = new LUTCubeLoader(loadingManager);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Gets a LUT
|
|
10
|
+
* @param url The URL of the LUT to get
|
|
11
|
+
* @returns A promise that resolves to the LookupTexture
|
|
12
|
+
*/
|
|
13
|
+
export const getLUT = async (url: string | null): Promise<LookupTexture | null> => {
|
|
14
|
+
if (!url) return null;
|
|
15
|
+
const result = await lutLoader.loadAsync(url);
|
|
16
|
+
try {
|
|
17
|
+
const lookupTexture = LookupTexture.from(result.texture3D);
|
|
18
|
+
lookupTexture.colorSpace = THREE.LinearSRGBColorSpace;
|
|
19
|
+
return lookupTexture;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error(error);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
};
|