@pascal-app/viewer 0.9.1 → 1.0.0-beta.1
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/README.md +20 -5
- package/dist/components/error-boundary.d.ts +6 -0
- package/dist/components/error-boundary.d.ts.map +1 -1
- package/dist/components/error-boundary.js +6 -0
- package/dist/components/renderers/node-renderer.d.ts +5 -1
- package/dist/components/renderers/node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.js +5 -2
- package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/parametric-node-renderer.js +7 -4
- package/dist/components/viewer/bvh-ecctrl.d.ts +77 -0
- package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -0
- package/dist/components/viewer/bvh-ecctrl.js +552 -0
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +59 -4
- package/dist/components/viewer/glb-interactive.d.ts +41 -0
- package/dist/components/viewer/glb-interactive.d.ts.map +1 -0
- package/dist/components/viewer/glb-interactive.js +394 -0
- package/dist/components/viewer/glb-reference-nodes.d.ts +29 -0
- package/dist/components/viewer/glb-reference-nodes.d.ts.map +1 -0
- package/dist/components/viewer/glb-reference-nodes.js +80 -0
- package/dist/components/viewer/glb-replace-instances.d.ts +18 -0
- package/dist/components/viewer/glb-replace-instances.d.ts.map +1 -0
- package/dist/components/viewer/glb-replace-instances.js +61 -0
- package/dist/components/viewer/glb-scene.d.ts +54 -0
- package/dist/components/viewer/glb-scene.d.ts.map +1 -0
- package/dist/components/viewer/glb-scene.js +920 -0
- package/dist/components/viewer/glb-walkthrough-controller.d.ts +22 -0
- package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -0
- package/dist/components/viewer/glb-walkthrough-controller.js +445 -0
- package/dist/components/viewer/index.d.ts +19 -2
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +113 -12
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +36 -5
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
- package/dist/components/viewer/perf-monitor.js +17 -2
- package/dist/components/viewer/post-processing.d.ts +7 -1
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +180 -24
- package/dist/components/viewer/registered-systems.d.ts.map +1 -1
- package/dist/components/viewer/registered-systems.js +6 -4
- package/dist/components/viewer/scene-bvh.d.ts +2 -1
- package/dist/components/viewer/scene-bvh.d.ts.map +1 -1
- package/dist/components/viewer/scene-bvh.js +3 -0
- package/dist/components/viewer/scene-bvh.test.d.ts +2 -0
- package/dist/components/viewer/scene-bvh.test.d.ts.map +1 -0
- package/dist/components/viewer/scene-bvh.test.js +13 -0
- package/dist/components/viewer/scene-environment.d.ts +2 -0
- package/dist/components/viewer/scene-environment.d.ts.map +1 -0
- package/dist/components/viewer/scene-environment.js +79 -0
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +23 -12
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.js +2 -24
- package/dist/hooks/use-node-events.d.ts +1 -1
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/hooks/use-node-events.js +22 -14
- package/dist/index.d.ts +24 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -13
- package/dist/lib/backdrop.d.ts +33 -0
- package/dist/lib/backdrop.d.ts.map +1 -0
- package/dist/lib/backdrop.js +92 -0
- package/dist/lib/box-uv.d.ts +13 -0
- package/dist/lib/box-uv.d.ts.map +1 -0
- package/dist/lib/box-uv.js +32 -0
- package/dist/lib/csg-utils.d.ts +3 -9
- package/dist/lib/csg-utils.d.ts.map +1 -1
- package/dist/lib/csg-utils.js +65 -5
- package/dist/lib/csg-utils.test.d.ts +2 -0
- package/dist/lib/csg-utils.test.d.ts.map +1 -0
- package/dist/lib/csg-utils.test.js +45 -0
- package/dist/lib/edge-style.d.ts +1 -0
- package/dist/lib/edge-style.d.ts.map +1 -1
- package/dist/lib/edge-style.js +26 -6
- package/dist/lib/hero-pose.d.ts +47 -0
- package/dist/lib/hero-pose.d.ts.map +1 -0
- package/dist/lib/hero-pose.js +227 -0
- package/dist/lib/ink-edges.d.ts +1 -1
- package/dist/lib/ink-edges.d.ts.map +1 -1
- package/dist/lib/ink-edges.js +13 -7
- package/dist/lib/ktx2-loader.d.ts +46 -0
- package/dist/lib/ktx2-loader.d.ts.map +1 -0
- package/dist/lib/ktx2-loader.js +142 -0
- package/dist/lib/layers.d.ts +10 -0
- package/dist/lib/layers.d.ts.map +1 -1
- package/dist/lib/layers.js +10 -0
- package/dist/lib/materials.d.ts +16 -1
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +158 -34
- package/dist/lib/materials.test.d.ts +2 -0
- package/dist/lib/materials.test.d.ts.map +1 -0
- package/dist/lib/materials.test.js +25 -0
- package/dist/lib/merged-outline-node.test.d.ts +2 -0
- package/dist/lib/merged-outline-node.test.d.ts.map +1 -0
- package/dist/lib/merged-outline-node.test.js +22 -0
- package/dist/lib/scene-themes.d.ts +1 -0
- package/dist/lib/scene-themes.d.ts.map +1 -1
- package/dist/lib/scene-themes.js +25 -16
- package/dist/lib/shadow-only.d.ts +4 -0
- package/dist/lib/shadow-only.d.ts.map +1 -0
- package/dist/lib/shadow-only.js +36 -0
- package/dist/lib/snapshot-pipeline.d.ts +42 -0
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -0
- package/dist/lib/snapshot-pipeline.js +254 -0
- package/dist/lib/terrain-perimeter-fill.d.ts +7 -0
- package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -0
- package/dist/lib/terrain-perimeter-fill.js +75 -0
- package/dist/lib/texture-node-guard.d.ts +13 -0
- package/dist/lib/texture-node-guard.d.ts.map +1 -0
- package/dist/lib/texture-node-guard.js +39 -0
- package/dist/lib/texture-reference.d.ts +31 -0
- package/dist/lib/texture-reference.d.ts.map +1 -0
- package/dist/lib/texture-reference.js +139 -0
- package/dist/lib/texture-reference.test.d.ts +2 -0
- package/dist/lib/texture-reference.test.d.ts.map +1 -0
- package/dist/lib/texture-reference.test.js +86 -0
- package/dist/lib/tsl-compat.d.ts +12 -0
- package/dist/lib/tsl-compat.d.ts.map +1 -0
- package/dist/lib/tsl-compat.js +13 -0
- package/dist/store/use-viewer.d.ts +73 -18
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +191 -4
- package/dist/store/use-viewer.test.d.ts +2 -0
- package/dist/store/use-viewer.test.d.ts.map +1 -0
- package/dist/store/use-viewer.test.js +61 -0
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +12 -5
- package/dist/systems/door/door-animation.test.d.ts +2 -0
- package/dist/systems/door/door-animation.test.d.ts.map +1 -0
- package/dist/systems/door/door-animation.test.js +51 -0
- package/dist/systems/door/door-floor-alignment.test.d.ts +2 -0
- package/dist/systems/door/door-floor-alignment.test.d.ts.map +1 -0
- package/dist/systems/door/door-floor-alignment.test.js +50 -0
- package/dist/systems/door/door-system.d.ts +24 -0
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +494 -161
- package/dist/systems/fence/fence-system.d.ts +17 -0
- package/dist/systems/fence/fence-system.d.ts.map +1 -1
- package/dist/systems/fence/fence-system.js +221 -53
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
- package/dist/systems/floor-elevation/floor-elevation-system.js +30 -7
- package/dist/systems/geometry/geometry-system.d.ts +7 -0
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
- package/dist/systems/geometry/geometry-system.js +95 -26
- package/dist/systems/geometry/geometry-system.test.d.ts +2 -0
- package/dist/systems/geometry/geometry-system.test.d.ts.map +1 -0
- package/dist/systems/geometry/geometry-system.test.js +30 -0
- package/dist/systems/interactive/control-widget.d.ts +10 -0
- package/dist/systems/interactive/control-widget.d.ts.map +1 -0
- package/dist/systems/interactive/control-widget.js +35 -0
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/interactive/interactive-system.js +55 -56
- package/dist/systems/item/item-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.js +20 -6
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +34 -9
- package/dist/systems/level/level-utils.d.ts +0 -3
- package/dist/systems/level/level-utils.d.ts.map +1 -1
- package/dist/systems/level/level-utils.js +7 -45
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +29 -8
- package/dist/systems/roof/roof-system.d.ts +21 -8
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +1079 -221
- package/dist/systems/slab/slab-system.d.ts +7 -4
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +31 -83
- package/dist/systems/slab/slab-system.test.js +54 -14
- package/dist/systems/stair/stair-system.js +7 -4
- package/dist/systems/wall/opening-cutout-geometry.d.ts +31 -0
- package/dist/systems/wall/opening-cutout-geometry.d.ts.map +1 -0
- package/dist/systems/wall/opening-cutout-geometry.js +167 -0
- package/dist/systems/wall/opening-cutout-geometry.test.d.ts +2 -0
- package/dist/systems/wall/opening-cutout-geometry.test.d.ts.map +1 -0
- package/dist/systems/wall/opening-cutout-geometry.test.js +172 -0
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +49 -17
- package/dist/systems/wall/wall-materials.d.ts +11 -7
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +244 -35
- package/dist/systems/wall/wall-opening-cutout.test.d.ts +2 -0
- package/dist/systems/wall/wall-opening-cutout.test.d.ts.map +1 -0
- package/dist/systems/wall/wall-opening-cutout.test.js +59 -0
- package/dist/systems/wall/wall-support-extension.test.d.ts +2 -0
- package/dist/systems/wall/wall-support-extension.test.d.ts.map +1 -0
- package/dist/systems/wall/wall-support-extension.test.js +116 -0
- package/dist/systems/wall/wall-system.d.ts +4 -8
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +397 -177
- package/dist/systems/wall/wall-terrain-bottom.test.d.ts +2 -0
- package/dist/systems/wall/wall-terrain-bottom.test.d.ts.map +1 -0
- package/dist/systems/wall/wall-terrain-bottom.test.js +34 -0
- package/dist/systems/window/window-animation-system.d.ts +12 -0
- package/dist/systems/window/window-animation-system.d.ts.map +1 -1
- package/dist/systems/window/window-animation-system.js +16 -5
- package/dist/systems/window/window-system.d.ts +7 -0
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +240 -14
- package/package.json +4 -4
- package/dist/components/viewer/ground-occluder.d.ts +0 -2
- package/dist/components/viewer/ground-occluder.d.ts.map +0 -1
- package/dist/components/viewer/ground-occluder.js +0 -67
- package/dist/components/viewer/walkthrough-controls.d.ts +0 -2
- package/dist/components/viewer/walkthrough-controls.d.ts.map +0 -1
- package/dist/components/viewer/walkthrough-controls.js +0 -131
package/dist/store/use-viewer.js
CHANGED
|
@@ -1,10 +1,137 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { create } from 'zustand';
|
|
3
3
|
import { persist } from 'zustand/middleware';
|
|
4
|
+
import { SCENE_THEME_IDS } from '../lib/scene-themes';
|
|
5
|
+
const CAMERA_MODES = ['perspective', 'orthographic'];
|
|
6
|
+
const RENDER_SHADINGS = ['solid', 'rendered'];
|
|
7
|
+
const COLOR_PRESETS = ['clay', 'white', 'mono', 'blueprint'];
|
|
8
|
+
const EDGE_MODES = ['off', 'soft', 'strong'];
|
|
9
|
+
const UNITS = ['metric', 'imperial'];
|
|
10
|
+
const METRIC_NOTATIONS = ['meters', 'millimeters'];
|
|
11
|
+
const LEVEL_MODES = ['stacked', 'exploded', 'solo', 'manual'];
|
|
12
|
+
const WALL_MODES = ['up', 'cutaway', 'down', 'translucent'];
|
|
13
|
+
// Countries still on imperial/US customary units: United States, Liberia, Myanmar.
|
|
14
|
+
const IMPERIAL_REGIONS = ['US', 'LR', 'MM'];
|
|
15
|
+
// IANA zones for those countries. The timezone tracks the OS clock (actual
|
|
16
|
+
// location), unlike navigator.language where en-US is a common default for
|
|
17
|
+
// users far outside the US.
|
|
18
|
+
const IMPERIAL_TIMEZONES = new Set([
|
|
19
|
+
'America/New_York',
|
|
20
|
+
'America/Detroit',
|
|
21
|
+
'America/Kentucky/Louisville',
|
|
22
|
+
'America/Kentucky/Monticello',
|
|
23
|
+
'America/Indiana/Indianapolis',
|
|
24
|
+
'America/Indiana/Vincennes',
|
|
25
|
+
'America/Indiana/Winamac',
|
|
26
|
+
'America/Indiana/Marengo',
|
|
27
|
+
'America/Indiana/Petersburg',
|
|
28
|
+
'America/Indiana/Vevay',
|
|
29
|
+
'America/Indiana/Tell_City',
|
|
30
|
+
'America/Indiana/Knox',
|
|
31
|
+
'America/Chicago',
|
|
32
|
+
'America/Menominee',
|
|
33
|
+
'America/North_Dakota/Center',
|
|
34
|
+
'America/North_Dakota/New_Salem',
|
|
35
|
+
'America/North_Dakota/Beulah',
|
|
36
|
+
'America/Denver',
|
|
37
|
+
'America/Boise',
|
|
38
|
+
'America/Phoenix',
|
|
39
|
+
'America/Los_Angeles',
|
|
40
|
+
'America/Anchorage',
|
|
41
|
+
'America/Juneau',
|
|
42
|
+
'America/Sitka',
|
|
43
|
+
'America/Metlakatla',
|
|
44
|
+
'America/Yakutat',
|
|
45
|
+
'America/Nome',
|
|
46
|
+
'America/Adak',
|
|
47
|
+
'Pacific/Honolulu',
|
|
48
|
+
'America/Puerto_Rico',
|
|
49
|
+
'Pacific/Guam',
|
|
50
|
+
'Africa/Monrovia', // Liberia
|
|
51
|
+
'Asia/Yangon', // Myanmar
|
|
52
|
+
'Asia/Rangoon', // Myanmar (legacy alias)
|
|
53
|
+
]);
|
|
54
|
+
function detectDefaultUnit() {
|
|
55
|
+
if (typeof navigator === 'undefined')
|
|
56
|
+
return 'metric';
|
|
57
|
+
try {
|
|
58
|
+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
59
|
+
if (timeZone)
|
|
60
|
+
return IMPERIAL_TIMEZONES.has(timeZone) ? 'imperial' : 'metric';
|
|
61
|
+
// No timezone available: fall back to an explicit locale region subtag
|
|
62
|
+
// only (never maximize() — it turns a bare "en" into region US).
|
|
63
|
+
const region = new Intl.Locale(navigator.language).region;
|
|
64
|
+
return region && IMPERIAL_REGIONS.includes(region) ? 'imperial' : 'metric';
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return 'metric';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function pickString(value, allowed, fallback) {
|
|
71
|
+
return typeof value === 'string' && allowed.includes(value) ? value : fallback;
|
|
72
|
+
}
|
|
73
|
+
function normalizeShadingByContext(value) {
|
|
74
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
75
|
+
return {};
|
|
76
|
+
const next = {};
|
|
77
|
+
for (const [context, shading] of Object.entries(value)) {
|
|
78
|
+
if (context !== 'editor' && context !== 'viewer')
|
|
79
|
+
continue;
|
|
80
|
+
next[context] = pickString(shading, RENDER_SHADINGS, 'rendered');
|
|
81
|
+
}
|
|
82
|
+
return next;
|
|
83
|
+
}
|
|
84
|
+
function normalizeProjectPreferences(value) {
|
|
85
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
86
|
+
return {};
|
|
87
|
+
const next = {};
|
|
88
|
+
for (const [projectId, preferences] of Object.entries(value)) {
|
|
89
|
+
if (!preferences || typeof preferences !== 'object' || Array.isArray(preferences))
|
|
90
|
+
continue;
|
|
91
|
+
const record = preferences;
|
|
92
|
+
next[projectId] = {
|
|
93
|
+
...(typeof record.showScans === 'boolean' ? { showScans: record.showScans } : {}),
|
|
94
|
+
...(typeof record.showGuides === 'boolean' ? { showGuides: record.showGuides } : {}),
|
|
95
|
+
...(typeof record.showGrid === 'boolean' ? { showGrid: record.showGrid } : {}),
|
|
96
|
+
...(typeof record.showMeasurements === 'boolean'
|
|
97
|
+
? { showMeasurements: record.showMeasurements }
|
|
98
|
+
: {}),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return next;
|
|
102
|
+
}
|
|
103
|
+
function normalizePersistedViewerState(value) {
|
|
104
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
105
|
+
return {};
|
|
106
|
+
const state = value;
|
|
107
|
+
return {
|
|
108
|
+
cameraMode: pickString(state.cameraMode, CAMERA_MODES, 'perspective'),
|
|
109
|
+
sceneTheme: pickString(state.sceneTheme, SCENE_THEME_IDS, 'studio'),
|
|
110
|
+
shadingByContext: normalizeShadingByContext(state.shadingByContext),
|
|
111
|
+
textures: typeof state.textures === 'boolean' ? state.textures : true,
|
|
112
|
+
colorPreset: pickString(state.colorPreset, COLOR_PRESETS, 'clay'),
|
|
113
|
+
edges: pickString(state.edges, EDGE_MODES, 'soft'),
|
|
114
|
+
shadows: typeof state.shadows === 'boolean' ? state.shadows : true,
|
|
115
|
+
unit: pickString(state.unit, UNITS, detectDefaultUnit()),
|
|
116
|
+
metricNotation: pickString(state.metricNotation, METRIC_NOTATIONS, 'meters'),
|
|
117
|
+
unitExplicit: typeof state.unit === 'string' && UNITS.includes(state.unit),
|
|
118
|
+
levelMode: pickString(state.levelMode, LEVEL_MODES, 'stacked'),
|
|
119
|
+
wallMode: pickString(state.wallMode, WALL_MODES, 'up'),
|
|
120
|
+
projectPreferences: normalizeProjectPreferences(state.projectPreferences),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
4
123
|
const useViewer = create()(persist((set) => ({
|
|
5
124
|
selection: { buildingId: null, levelId: null, zoneId: null, selectedIds: [] },
|
|
6
125
|
previewSelectedIds: [],
|
|
7
126
|
setPreviewSelectedIds: (ids) => set({ previewSelectedIds: ids }),
|
|
127
|
+
externalSelectedIds: [],
|
|
128
|
+
setExternalSelectedIds: (ids) => set((state) => {
|
|
129
|
+
if (state.externalSelectedIds.length === ids.length &&
|
|
130
|
+
state.externalSelectedIds.every((id, index) => id === ids[index])) {
|
|
131
|
+
return state;
|
|
132
|
+
}
|
|
133
|
+
return { externalSelectedIds: ids };
|
|
134
|
+
}),
|
|
8
135
|
hoverHighlightMode: 'default',
|
|
9
136
|
setHoverHighlightMode: (mode) => set((state) => (state.hoverHighlightMode === mode ? state : { hoverHighlightMode: mode })),
|
|
10
137
|
hoveredId: null,
|
|
@@ -15,6 +142,21 @@ const useViewer = create()(persist((set) => ({
|
|
|
15
142
|
setSceneTheme: (id) => set({ sceneTheme: id }),
|
|
16
143
|
renderContext: 'editor',
|
|
17
144
|
setRenderContext: (context) => set({ renderContext: context }),
|
|
145
|
+
isExporting: false,
|
|
146
|
+
setExporting: (value) => set({ isExporting: value }),
|
|
147
|
+
itemLoadFailures: {},
|
|
148
|
+
reportItemLoadFailure: (nodeId, url) => set((state) => state.itemLoadFailures[nodeId] === url
|
|
149
|
+
? state
|
|
150
|
+
: { itemLoadFailures: { ...state.itemLoadFailures, [nodeId]: url } }),
|
|
151
|
+
clearItemLoadFailure: (nodeId) => set((state) => {
|
|
152
|
+
if (!(nodeId in state.itemLoadFailures))
|
|
153
|
+
return state;
|
|
154
|
+
const next = { ...state.itemLoadFailures };
|
|
155
|
+
delete next[nodeId];
|
|
156
|
+
return { itemLoadFailures: next };
|
|
157
|
+
}),
|
|
158
|
+
renderPaused: false,
|
|
159
|
+
setRenderPaused: (value) => set({ renderPaused: value }),
|
|
18
160
|
shading: 'rendered',
|
|
19
161
|
shadingByContext: {},
|
|
20
162
|
setShading: (shading) => set((state) => ({
|
|
@@ -29,8 +171,11 @@ const useViewer = create()(persist((set) => ({
|
|
|
29
171
|
setEdges: (edges) => set({ edges }),
|
|
30
172
|
shadows: true,
|
|
31
173
|
setShadows: (shadows) => set({ shadows }),
|
|
32
|
-
unit:
|
|
33
|
-
|
|
174
|
+
unit: detectDefaultUnit(),
|
|
175
|
+
metricNotation: 'meters',
|
|
176
|
+
unitExplicit: false,
|
|
177
|
+
setUnit: (unit) => set({ unit, unitExplicit: true }),
|
|
178
|
+
setMetricNotation: (metricNotation) => set({ unit: 'metric', metricNotation, unitExplicit: true }),
|
|
34
179
|
levelMode: 'stacked',
|
|
35
180
|
setLevelMode: (mode) => set({ levelMode: mode }),
|
|
36
181
|
wallMode: 'up',
|
|
@@ -68,6 +213,23 @@ const useViewer = create()(persist((set) => ({
|
|
|
68
213
|
}
|
|
69
214
|
return { showGrid: show, projectPreferences };
|
|
70
215
|
}),
|
|
216
|
+
showMeasurements: true,
|
|
217
|
+
setShowMeasurements: (show) => set((state) => {
|
|
218
|
+
const projectPreferences = { ...(state.projectPreferences || {}) };
|
|
219
|
+
if (state.projectId) {
|
|
220
|
+
projectPreferences[state.projectId] = {
|
|
221
|
+
...(projectPreferences[state.projectId] || {}),
|
|
222
|
+
showMeasurements: show,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return { showMeasurements: show, projectPreferences };
|
|
226
|
+
}),
|
|
227
|
+
showZones: true,
|
|
228
|
+
setShowZones: (show) => set({ showZones: show }),
|
|
229
|
+
transparentBackground: false,
|
|
230
|
+
setTransparentBackground: (transparent) => set({ transparentBackground: transparent }),
|
|
231
|
+
inkOpacity: null,
|
|
232
|
+
setInkOpacity: (opacity) => set({ inkOpacity: opacity }),
|
|
71
233
|
projectId: null,
|
|
72
234
|
setProjectId: (id) => set((state) => {
|
|
73
235
|
if (!id)
|
|
@@ -78,6 +240,7 @@ const useViewer = create()(persist((set) => ({
|
|
|
78
240
|
showScans: prefs.showScans ?? true,
|
|
79
241
|
showGuides: prefs.showGuides ?? true,
|
|
80
242
|
showGrid: prefs.showGrid ?? true,
|
|
243
|
+
showMeasurements: prefs.showMeasurements ?? true,
|
|
81
244
|
};
|
|
82
245
|
}),
|
|
83
246
|
projectPreferences: {},
|
|
@@ -114,18 +277,26 @@ const useViewer = create()(persist((set) => ({
|
|
|
114
277
|
previewSelectedIds: [],
|
|
115
278
|
}),
|
|
116
279
|
outliner: { selectedObjects: [], hoveredObjects: [] },
|
|
280
|
+
geometryRevision: 0,
|
|
281
|
+
bumpGeometryRevision: () => set((state) => ({ geometryRevision: state.geometryRevision + 1 })),
|
|
117
282
|
exportScene: null,
|
|
118
283
|
setExportScene: (fn) => set({ exportScene: fn }),
|
|
119
284
|
debugColors: false,
|
|
120
285
|
setDebugColors: (enabled) => set({ debugColors: enabled }),
|
|
121
286
|
walkthroughMode: false,
|
|
122
|
-
setWalkthroughMode: (mode) => set({ walkthroughMode: mode }),
|
|
287
|
+
setWalkthroughMode: (mode) => set({ walkthroughMode: mode, walkthroughSuspended: false }),
|
|
288
|
+
walkthroughSuspended: false,
|
|
289
|
+
setWalkthroughSuspended: (suspended) => set({ walkthroughSuspended: suspended }),
|
|
123
290
|
cameraDragging: false,
|
|
124
291
|
setCameraDragging: (dragging) => set({ cameraDragging: dragging }),
|
|
125
292
|
inputDragging: false,
|
|
126
293
|
setInputDragging: (dragging) => set({ inputDragging: dragging }),
|
|
127
294
|
}), {
|
|
128
295
|
name: 'viewer-preferences',
|
|
296
|
+
merge: (persistedState, currentState) => ({
|
|
297
|
+
...currentState,
|
|
298
|
+
...normalizePersistedViewerState(persistedState),
|
|
299
|
+
}),
|
|
129
300
|
partialize: (state) => ({
|
|
130
301
|
cameraMode: state.cameraMode,
|
|
131
302
|
sceneTheme: state.sceneTheme,
|
|
@@ -134,10 +305,26 @@ const useViewer = create()(persist((set) => ({
|
|
|
134
305
|
colorPreset: state.colorPreset,
|
|
135
306
|
edges: state.edges,
|
|
136
307
|
shadows: state.shadows,
|
|
137
|
-
unit: state.unit,
|
|
308
|
+
...(state.unitExplicit ? { unit: state.unit } : {}),
|
|
309
|
+
metricNotation: state.metricNotation,
|
|
138
310
|
levelMode: state.levelMode,
|
|
139
311
|
wallMode: state.wallMode,
|
|
140
312
|
projectPreferences: state.projectPreferences,
|
|
141
313
|
}),
|
|
142
314
|
}));
|
|
315
|
+
/** Apply an authoritative country code (e.g. IP-derived by the host app) as
|
|
316
|
+
* the unit default. Stronger signal than the timezone heuristic used at store
|
|
317
|
+
* creation, but still a default: it never overrides an explicit user choice
|
|
318
|
+
* and is not persisted (the unit only sticks once the user touches the
|
|
319
|
+
* toggle). */
|
|
320
|
+
export function applyCountryUnitDefault(country) {
|
|
321
|
+
if (!country)
|
|
322
|
+
return;
|
|
323
|
+
const state = useViewer.getState();
|
|
324
|
+
if (state.unitExplicit)
|
|
325
|
+
return;
|
|
326
|
+
const unit = IMPERIAL_REGIONS.includes(country.toUpperCase()) ? 'imperial' : 'metric';
|
|
327
|
+
if (state.unit !== unit)
|
|
328
|
+
useViewer.setState({ unit });
|
|
329
|
+
}
|
|
143
330
|
export default useViewer;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-viewer.test.d.ts","sourceRoot":"","sources":["../../src/store/use-viewer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// @ts-expect-error — bun:test is provided by the Bun runtime; viewer does not
|
|
2
|
+
// include Bun ambient types in its production declaration build.
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
|
4
|
+
import useViewer from './use-viewer';
|
|
5
|
+
const resetMeasurementPreferences = () => {
|
|
6
|
+
useViewer.setState({
|
|
7
|
+
externalSelectedIds: [],
|
|
8
|
+
projectId: null,
|
|
9
|
+
projectPreferences: {},
|
|
10
|
+
showMeasurements: true,
|
|
11
|
+
unit: 'metric',
|
|
12
|
+
metricNotation: 'meters',
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
beforeEach(resetMeasurementPreferences);
|
|
16
|
+
afterEach(resetMeasurementPreferences);
|
|
17
|
+
describe('measurement display preferences', () => {
|
|
18
|
+
test('stores global visibility per project and defaults new projects to visible', () => {
|
|
19
|
+
const viewer = useViewer.getState();
|
|
20
|
+
viewer.setProjectId('project-a');
|
|
21
|
+
viewer.setShowMeasurements(false);
|
|
22
|
+
expect(useViewer.getState()).toMatchObject({
|
|
23
|
+
projectId: 'project-a',
|
|
24
|
+
showMeasurements: false,
|
|
25
|
+
projectPreferences: {
|
|
26
|
+
'project-a': { showMeasurements: false },
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
useViewer.getState().setProjectId('project-b');
|
|
30
|
+
expect(useViewer.getState().showMeasurements).toBe(true);
|
|
31
|
+
useViewer.getState().setProjectId('project-a');
|
|
32
|
+
expect(useViewer.getState().showMeasurements).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
test('changes display units without changing measurement visibility preferences', () => {
|
|
35
|
+
const viewer = useViewer.getState();
|
|
36
|
+
viewer.setProjectId('project-a');
|
|
37
|
+
viewer.setShowMeasurements(false);
|
|
38
|
+
const preferences = useViewer.getState().projectPreferences;
|
|
39
|
+
useViewer.getState().setUnit('imperial');
|
|
40
|
+
expect(useViewer.getState().unit).toBe('imperial');
|
|
41
|
+
expect(useViewer.getState().projectPreferences).toEqual(preferences);
|
|
42
|
+
expect(useViewer.getState().showMeasurements).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
test('selects millimeters as a metric display notation', () => {
|
|
45
|
+
useViewer.getState().setUnit('imperial');
|
|
46
|
+
useViewer.getState().setMetricNotation('millimeters');
|
|
47
|
+
expect(useViewer.getState()).toMatchObject({
|
|
48
|
+
unit: 'metric',
|
|
49
|
+
metricNotation: 'millimeters',
|
|
50
|
+
unitExplicit: true,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('external selection highlights', () => {
|
|
55
|
+
test('tracks host-owned highlights without changing the local selection', () => {
|
|
56
|
+
const localSelection = useViewer.getState().selection;
|
|
57
|
+
useViewer.getState().setExternalSelectedIds(['wall_remote']);
|
|
58
|
+
expect(useViewer.getState().externalSelectedIds).toEqual(['wall_remote']);
|
|
59
|
+
expect(useViewer.getState().selection).toBe(localSelection);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ceiling-system.d.ts","sourceRoot":"","sources":["../../../src/systems/ceiling/ceiling-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"ceiling-system.d.ts","sourceRoot":"","sources":["../../../src/systems/ceiling/ceiling-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EAOjB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAgB9B,eAAO,MAAM,aAAa,YA4BzB,CAAA;AAgED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,WAAW,EACxB,UAAU,GAAE,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAM,GAC9C,KAAK,CAAC,cAAc,CA6DtB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getEffectiveNode, nodeRegistry, sceneRegistry, useLiveTransforms, useScene, } from '@pascal-app/core';
|
|
1
|
+
import { getEffectiveNode, nodeRegistry, resolveCeilingHeight, sceneRegistry, useLiveTransforms, useScene, } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
3
|
import * as THREE from 'three';
|
|
4
4
|
import { mergeSurfaceHolePolygons } from '../surface-hole-geometry';
|
|
@@ -21,7 +21,7 @@ export const CeilingSystem = () => {
|
|
|
21
21
|
// Process dirty ceilings
|
|
22
22
|
dirtyNodes.forEach((id) => {
|
|
23
23
|
const node = nodes[id];
|
|
24
|
-
if (
|
|
24
|
+
if (node?.type !== 'ceiling')
|
|
25
25
|
return;
|
|
26
26
|
const mesh = sceneRegistry.nodes.get(id);
|
|
27
27
|
if (mesh) {
|
|
@@ -30,7 +30,7 @@ export const CeilingSystem = () => {
|
|
|
30
30
|
// the final value on commit. Mirrors WallSystem / GeometrySystem.
|
|
31
31
|
const effective = getEffectiveNode(node);
|
|
32
32
|
const itemHoles = collectCeilingHoles(effective, nodes);
|
|
33
|
-
updateCeilingGeometry(effective, mesh, itemHoles);
|
|
33
|
+
updateCeilingGeometry(effective, mesh, itemHoles, nodes);
|
|
34
34
|
clearDirty(id);
|
|
35
35
|
}
|
|
36
36
|
// If mesh not found, keep it dirty for next frame
|
|
@@ -64,7 +64,7 @@ function collectCeilingHoles(ceiling, nodes) {
|
|
|
64
64
|
/**
|
|
65
65
|
* Updates the geometry for a single ceiling
|
|
66
66
|
*/
|
|
67
|
-
function updateCeilingGeometry(node, mesh, extraHoles = []) {
|
|
67
|
+
function updateCeilingGeometry(node, mesh, extraHoles = [], nodes = useScene.getState().nodes) {
|
|
68
68
|
const newGeo = generateCeilingGeometry(node, extraHoles);
|
|
69
69
|
mesh.geometry.dispose();
|
|
70
70
|
mesh.geometry = newGeo;
|
|
@@ -81,7 +81,11 @@ function updateCeilingGeometry(node, mesh, extraHoles = []) {
|
|
|
81
81
|
const liveTransform = useLiveTransforms.getState().get(node.id);
|
|
82
82
|
mesh.position.x = liveTransform?.position[0] ?? 0;
|
|
83
83
|
mesh.position.z = liveTransform?.position[2] ?? 0;
|
|
84
|
-
|
|
84
|
+
// Resolved height: explicit when stored, else the level-top bound — so a
|
|
85
|
+
// follows-mode ceiling re-parks under the current plane on every rebuild
|
|
86
|
+
// (level-height edits / covering-slab changes dirty-mark ceilings).
|
|
87
|
+
// Slight offset to avoid z-fighting with upper-level slabs.
|
|
88
|
+
mesh.position.y = resolveCeilingHeight(node, nodes) - 0.01 + (liveTransform?.position[1] ?? 0);
|
|
85
89
|
}
|
|
86
90
|
/**
|
|
87
91
|
* Generates flat ceiling geometry from polygon (no extrusion).
|
|
@@ -101,6 +105,9 @@ export function generateCeilingGeometry(ceilingNode, extraHoles = []) {
|
|
|
101
105
|
// the whole command encoder.
|
|
102
106
|
const degenerate = new THREE.BufferGeometry();
|
|
103
107
|
degenerate.setAttribute('position', new THREE.Float32BufferAttribute(new Float32Array(9), 3));
|
|
108
|
+
degenerate.setAttribute('normal', new THREE.Float32BufferAttribute(new Float32Array(9), 3));
|
|
109
|
+
degenerate.setAttribute('uv', new THREE.Float32BufferAttribute(new Float32Array(6), 2));
|
|
110
|
+
degenerate.setAttribute('uv2', new THREE.Float32BufferAttribute(new Float32Array(6), 2));
|
|
104
111
|
return degenerate;
|
|
105
112
|
}
|
|
106
113
|
// Create shape from polygon
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"door-animation.test.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-animation.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// @ts-expect-error — bun:test is provided by the Bun runtime; viewer does not
|
|
2
|
+
// depend on @types/bun so the import type is unresolved at compile time.
|
|
3
|
+
import { describe, expect, test } from 'bun:test';
|
|
4
|
+
import { DoorNode } from '@pascal-app/core';
|
|
5
|
+
import * as THREE from 'three';
|
|
6
|
+
import { buildDoorPreviewMesh, poseDoorMovingParts } from '../../index';
|
|
7
|
+
// The named moving group each operation-door builder emits at the closed pose.
|
|
8
|
+
const MOVING_GROUP = {
|
|
9
|
+
sliding: 'door-sliding-active',
|
|
10
|
+
pocket: 'door-pocket-leaf',
|
|
11
|
+
barn: 'door-barn-leaf',
|
|
12
|
+
'garage-tiltup': 'door-tiltup-leaf',
|
|
13
|
+
folding: 'door-fold-0',
|
|
14
|
+
'garage-sectional': 'door-sectional-0',
|
|
15
|
+
'garage-rollup': 'door-rollup-curtain',
|
|
16
|
+
};
|
|
17
|
+
function worldPosition(object) {
|
|
18
|
+
object.updateMatrixWorld(true);
|
|
19
|
+
return object.getWorldPosition(new THREE.Vector3());
|
|
20
|
+
}
|
|
21
|
+
describe('operation door open kinematics', () => {
|
|
22
|
+
// Each operation door builds its moving parts in a named group at the closed
|
|
23
|
+
// pose; `poseDoorMovingParts` then drives the open motion (the single source
|
|
24
|
+
// of truth shared by the live system and the GLB exporter). This guards the
|
|
25
|
+
// build-once + pose-at-t contract: the group exists, rests closed, and moves.
|
|
26
|
+
for (const doorType of Object.keys(MOVING_GROUP)) {
|
|
27
|
+
test(`${doorType} builds a moving group that opens`, () => {
|
|
28
|
+
const node = DoorNode.parse({ id: `door_${doorType}`, doorType, operationState: 0 });
|
|
29
|
+
const mesh = buildDoorPreviewMesh(node);
|
|
30
|
+
const group = mesh.getObjectByName(MOVING_GROUP[doorType]);
|
|
31
|
+
expect(group).toBeDefined();
|
|
32
|
+
// Closed snapshot.
|
|
33
|
+
poseDoorMovingParts(node, mesh, 0);
|
|
34
|
+
const closedPos = worldPosition(group);
|
|
35
|
+
const closedScaleY = group.scale.y;
|
|
36
|
+
const closedRotX = group.rotation.x;
|
|
37
|
+
const closedRotY = group.rotation.y;
|
|
38
|
+
// Open snapshot — at least one of translation / rotation / scale changes.
|
|
39
|
+
poseDoorMovingParts(node, mesh, 1);
|
|
40
|
+
const openPos = worldPosition(group);
|
|
41
|
+
const moved = closedPos.distanceTo(openPos) > 0.05 ||
|
|
42
|
+
Math.abs(closedScaleY - group.scale.y) > 0.1 ||
|
|
43
|
+
Math.abs(closedRotX - group.rotation.x) > 0.05 ||
|
|
44
|
+
Math.abs(closedRotY - group.rotation.y) > 0.05;
|
|
45
|
+
expect(moved).toBe(true);
|
|
46
|
+
// Posing back to closed is the identity of the rest pose.
|
|
47
|
+
poseDoorMovingParts(node, mesh, 0);
|
|
48
|
+
expect(worldPosition(group).distanceTo(closedPos)).toBeLessThan(1e-6);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"door-floor-alignment.test.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-floor-alignment.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// @ts-expect-error — bun:test is provided by the Bun runtime; viewer does not
|
|
2
|
+
// depend on @types/bun so the import type is unresolved at compile time.
|
|
3
|
+
import { describe, expect, test } from 'bun:test';
|
|
4
|
+
import { DoorNode } from '@pascal-app/core';
|
|
5
|
+
import * as THREE from 'three';
|
|
6
|
+
import { buildDoorPreviewMesh } from '../../index';
|
|
7
|
+
const DOOR_TYPES = [
|
|
8
|
+
'hinged',
|
|
9
|
+
'double',
|
|
10
|
+
'french',
|
|
11
|
+
'folding',
|
|
12
|
+
'pocket',
|
|
13
|
+
'barn',
|
|
14
|
+
'sliding',
|
|
15
|
+
'garage-sectional',
|
|
16
|
+
'garage-rollup',
|
|
17
|
+
'garage-tiltup',
|
|
18
|
+
];
|
|
19
|
+
function visibleBounds(mesh) {
|
|
20
|
+
mesh.updateMatrixWorld(true);
|
|
21
|
+
const bounds = new THREE.Box3();
|
|
22
|
+
for (const child of mesh.children) {
|
|
23
|
+
if (child.name === 'cutout')
|
|
24
|
+
continue;
|
|
25
|
+
bounds.expandByObject(child, true);
|
|
26
|
+
}
|
|
27
|
+
return bounds;
|
|
28
|
+
}
|
|
29
|
+
describe('door floor alignment', () => {
|
|
30
|
+
for (const doorType of DOOR_TYPES) {
|
|
31
|
+
test(`${doorType} does not extend below the opening floor`, () => {
|
|
32
|
+
const node = DoorNode.parse({
|
|
33
|
+
id: `door_floor-alignment-${doorType}`,
|
|
34
|
+
doorType,
|
|
35
|
+
operationState: 0,
|
|
36
|
+
threshold: true,
|
|
37
|
+
});
|
|
38
|
+
const mesh = buildDoorPreviewMesh(node);
|
|
39
|
+
const bounds = visibleBounds(mesh);
|
|
40
|
+
expect(bounds.min.y).toBeGreaterThanOrEqual(-node.height / 2 - 1e-6);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
test('keeps the wall cutout bottom locked to the opening floor', () => {
|
|
44
|
+
const node = DoorNode.parse({ id: 'door_floor-alignment-cutout' });
|
|
45
|
+
const mesh = buildDoorPreviewMesh(node);
|
|
46
|
+
const cutout = mesh.getObjectByName('cutout');
|
|
47
|
+
cutout.geometry.computeBoundingBox();
|
|
48
|
+
expect(cutout.geometry.boundingBox?.min.y).toBeCloseTo(-node.height / 2, 6);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -1,2 +1,26 @@
|
|
|
1
|
+
import { type DoorNode } from '@pascal-app/core';
|
|
2
|
+
import * as THREE from 'three';
|
|
1
3
|
export declare const DoorSystem: () => null;
|
|
4
|
+
/**
|
|
5
|
+
* Pose an operation door's moving parts (sliding/pocket/barn leaf, tilt-up and
|
|
6
|
+
* sectional panels, folding chain, roll-up curtain) at `value` (0 = closed,
|
|
7
|
+
* 1 = open) by transforming the named groups its builder emitted at the closed
|
|
8
|
+
* pose. Returns true when the door type has a pose path and its groups exist.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors `poseWindowMovingParts`: the live door system calls it after a
|
|
11
|
+
* (re)build so `operationState` is reflected, and the GLB exporter calls it on a
|
|
12
|
+
* clone to sample keyframes. Swing doors are not handled here — their leaf group
|
|
13
|
+
* carries a `pascalSwingLeaf` marker the exporter reads directly.
|
|
14
|
+
*
|
|
15
|
+
* Roll-up is the one type whose live geometry changes (slats vanish onto a
|
|
16
|
+
* drum), which a glTF clip can't express; the baked approximation scales the
|
|
17
|
+
* curtain up into the lintel. The live roll-up animation keeps its full-detail
|
|
18
|
+
* rebuild and is intentionally NOT routed through this scale.
|
|
19
|
+
*/
|
|
20
|
+
export declare function poseDoorMovingParts(node: DoorNode, mesh: THREE.Object3D | undefined, value: number): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Build a fresh door mesh for preview/ghost rendering.
|
|
23
|
+
* Returns a mesh with an invisible hitbox root and visible children (frame, panels, hardware).
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildDoorPreviewMesh(node: DoorNode): THREE.Mesh;
|
|
2
26
|
//# sourceMappingURL=door-system.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAYd,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA0D9B,eAAO,MAAM,UAAU,YA6GtB,CAAA;AAupCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,KAAK,CAAC,QAAQ,GAAG,SAAS,EAChC,KAAK,EAAE,MAAM,GACZ,OAAO,CA6GT;AAyuCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC,IAAI,CAI/D"}
|