@pascal-app/editor 0.6.0 → 0.8.0
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 +13 -9
- package/src/components/editor/bottom-sheet.tsx +149 -0
- package/src/components/editor/custom-camera-controls.tsx +74 -5
- package/src/components/editor/editor-layout-mobile.tsx +264 -0
- package/src/components/editor/editor-layout-v2.tsx +24 -3
- package/src/components/editor/first-person/build-collider-world.ts +363 -0
- package/src/components/editor/first-person/bvh-ecctrl.tsx +860 -0
- package/src/components/editor/first-person-controls.tsx +496 -143
- package/src/components/editor/floating-action-menu.tsx +32 -55
- package/src/components/editor/floorplan-background-selection.ts +113 -0
- package/src/components/editor/floorplan-panel.tsx +9861 -3297
- package/src/components/editor/index.tsx +295 -32
- package/src/components/editor/selection-manager.tsx +575 -13
- package/src/components/editor/snapshot-capture-overlay.tsx +465 -0
- package/src/components/editor/thumbnail-generator.tsx +56 -68
- package/src/components/editor/use-floorplan-background-placement.ts +257 -0
- package/src/components/editor/use-floorplan-hit-testing.ts +171 -0
- package/src/components/editor/use-floorplan-scene-data.ts +189 -0
- package/src/components/editor/wall-measurement-label.tsx +267 -36
- package/src/components/editor-2d/floorplan-action-menu-layer.tsx +95 -0
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +160 -0
- package/src/components/editor-2d/floorplan-hotkey-handlers.tsx +92 -0
- package/src/components/editor-2d/renderers/floorplan-draft-layer.tsx +124 -0
- package/src/components/editor-2d/renderers/floorplan-marquee-layer.tsx +58 -0
- package/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +202 -0
- package/src/components/editor-2d/renderers/floorplan-roof-layer.tsx +113 -0
- package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +474 -0
- package/src/components/editor-2d/svg-paths.ts +119 -0
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +10 -12
- package/src/components/systems/roof/roof-edit-system.tsx +1 -1
- package/src/components/systems/stair/stair-edit-system.tsx +1 -1
- package/src/components/systems/zone/zone-label-editor-system.tsx +0 -0
- package/src/components/systems/zone/zone-system.tsx +0 -0
- package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +1 -0
- package/src/components/tools/ceiling/ceiling-hole-editor.tsx +1 -0
- package/src/components/tools/ceiling/ceiling-tool.tsx +5 -5
- package/src/components/tools/ceiling/move-ceiling-tool.tsx +9 -2
- package/src/components/tools/column/column-tool.tsx +97 -0
- package/src/components/tools/column/move-column-tool.tsx +105 -0
- package/src/components/tools/door/door-tool.tsx +7 -0
- package/src/components/tools/door/move-door-tool.tsx +28 -8
- package/src/components/tools/fence/curve-fence-tool.tsx +4 -5
- package/src/components/tools/fence/fence-drafting.ts +10 -3
- package/src/components/tools/fence/fence-tool.tsx +160 -4
- package/src/components/tools/fence/move-fence-endpoint-tool.tsx +139 -25
- package/src/components/tools/fence/move-fence-tool.tsx +111 -40
- package/src/components/tools/item/move-tool.tsx +7 -1
- package/src/components/tools/item/placement-math.ts +32 -5
- package/src/components/tools/item/placement-strategies.ts +110 -31
- package/src/components/tools/item/placement-types.ts +7 -0
- package/src/components/tools/item/use-draft-node.ts +1 -0
- package/src/components/tools/item/use-placement-coordinator.tsx +558 -52
- package/src/components/tools/roof/move-roof-tool.tsx +29 -17
- package/src/components/tools/select/box-select-tool.tsx +12 -17
- package/src/components/tools/shared/polygon-editor.tsx +153 -28
- package/src/components/tools/shared/segment-angle.ts +156 -0
- package/src/components/tools/slab/slab-boundary-editor.tsx +1 -0
- package/src/components/tools/slab/slab-hole-editor.tsx +1 -0
- package/src/components/tools/spawn/move-spawn-tool.tsx +101 -0
- package/src/components/tools/spawn/spawn-tool.tsx +130 -0
- package/src/components/tools/tool-manager.tsx +20 -5
- package/src/components/tools/wall/curve-wall-tool.tsx +8 -6
- package/src/components/tools/wall/move-wall-endpoint-tool.tsx +131 -27
- package/src/components/tools/wall/move-wall-tool.tsx +6 -4
- package/src/components/tools/wall/wall-drafting.ts +18 -9
- package/src/components/tools/wall/wall-tool.tsx +136 -4
- package/src/components/tools/window/move-window-tool.tsx +18 -0
- package/src/components/tools/window/window-tool.tsx +5 -0
- package/src/components/tools/zone/zone-tool.tsx +20 -5
- package/src/components/ui/action-menu/camera-actions.tsx +37 -33
- package/src/components/ui/action-menu/control-modes.tsx +34 -1
- package/src/components/ui/action-menu/furnish-tools.tsx +6 -92
- package/src/components/ui/action-menu/index.tsx +98 -59
- package/src/components/ui/action-menu/structure-tools.tsx +2 -0
- package/src/components/ui/action-menu/view-toggles.tsx +418 -41
- package/src/components/ui/command-palette/editor-commands.tsx +24 -5
- package/src/components/ui/command-palette/index.tsx +4 -255
- package/src/components/ui/controls/material-picker.tsx +154 -164
- package/src/components/ui/controls/slider-control.tsx +66 -18
- package/src/components/ui/floating-level-selector.tsx +286 -55
- package/src/components/ui/helpers/helper-manager.tsx +10 -0
- package/src/components/ui/item-catalog/catalog-items.tsx +2563 -1239
- package/src/components/ui/item-catalog/item-catalog.tsx +96 -187
- package/src/components/ui/level-duplicate-dialog.tsx +113 -0
- package/src/components/ui/panels/ceiling-panel.tsx +3 -28
- package/src/components/ui/panels/column-panel.tsx +759 -0
- package/src/components/ui/panels/door-panel.tsx +989 -290
- package/src/components/ui/panels/fence-panel.tsx +2 -49
- package/src/components/ui/panels/mobile-panel-sheet.tsx +108 -0
- package/src/components/ui/panels/mobile-selection-bar.tsx +100 -0
- package/src/components/ui/panels/node-display.ts +39 -0
- package/src/components/ui/panels/paint-panel.tsx +163 -0
- package/src/components/ui/panels/panel-manager.tsx +208 -28
- package/src/components/ui/panels/panel-wrapper.tsx +48 -39
- package/src/components/ui/panels/reference-panel.tsx +253 -5
- package/src/components/ui/panels/roof-panel.tsx +13 -64
- package/src/components/ui/panels/roof-segment-panel.tsx +0 -25
- package/src/components/ui/panels/slab-panel.tsx +4 -30
- package/src/components/ui/panels/spawn-panel.tsx +161 -0
- package/src/components/ui/panels/stair-panel.tsx +20 -74
- package/src/components/ui/panels/stair-segment-panel.tsx +0 -25
- package/src/components/ui/panels/wall-panel.tsx +10 -8
- package/src/components/ui/panels/window-panel.tsx +668 -139
- package/src/components/ui/primitives/number-input.tsx +1 -1
- package/src/components/ui/primitives/sidebar.tsx +0 -0
- package/src/components/ui/sidebar/app-sidebar.tsx +0 -0
- package/src/components/ui/sidebar/icon-rail.tsx +0 -0
- package/src/components/ui/sidebar/mobile-tab-bar.tsx +46 -0
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +330 -0
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +0 -0
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +0 -0
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +77 -0
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +105 -22
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +0 -0
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +76 -0
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +11 -3
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +10 -5
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +1 -1
- package/src/components/ui/sidebar/tab-bar.tsx +3 -0
- package/src/components/ui/slider.tsx +1 -1
- package/src/components/viewer-overlay.tsx +0 -0
- package/src/components/viewer-zone-system.tsx +0 -0
- package/src/hooks/use-auto-frame.ts +45 -0
- package/src/hooks/use-auto-save.ts +14 -0
- package/src/hooks/use-keyboard.ts +74 -7
- package/src/hooks/use-mobile.ts +12 -12
- package/src/index.tsx +8 -1
- package/src/lib/door-interaction.ts +88 -0
- package/src/lib/floorplan/geometry.ts +263 -0
- package/src/lib/floorplan/index.ts +38 -0
- package/src/lib/floorplan/items.ts +179 -0
- package/src/lib/floorplan/selection-tool.ts +231 -0
- package/src/lib/floorplan/stairs.ts +478 -0
- package/src/lib/floorplan/types.ts +57 -0
- package/src/lib/floorplan/walls.ts +23 -0
- package/src/lib/guide-events.ts +10 -0
- package/src/lib/level-duplication.test.ts +70 -0
- package/src/lib/level-duplication.ts +153 -0
- package/src/lib/local-guide-image.ts +42 -0
- package/src/lib/material-paint.ts +284 -0
- package/src/lib/roof-duplication.ts +214 -0
- package/src/lib/scene-bounds.test.ts +183 -0
- package/src/lib/scene-bounds.ts +169 -0
- package/src/lib/scene.ts +0 -0
- package/src/lib/sfx-bus.ts +2 -0
- package/src/lib/sfx-player.ts +5 -5
- package/src/lib/stair-duplication.ts +126 -0
- package/src/lib/window-interaction.ts +86 -0
- package/src/store/use-editor.tsx +186 -62
- package/tsconfig.json +2 -1
- package/src/components/feedback-dialog.tsx +0 -265
- package/src/components/pascal-radio.tsx +0 -280
- package/src/components/preview-button.tsx +0 -16
- package/src/components/ui/viewer-toolbar.tsx +0 -395
package/src/store/use-editor.tsx
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import type { AssetInput } from '@pascal-app/core'
|
|
3
4
|
import {
|
|
4
5
|
type AnyNodeId,
|
|
5
|
-
type AssetInput,
|
|
6
6
|
type BuildingNode,
|
|
7
7
|
type CeilingNode,
|
|
8
|
+
type ColumnNode,
|
|
8
9
|
type DoorNode,
|
|
9
10
|
type FenceNode,
|
|
10
11
|
type ItemNode,
|
|
11
12
|
type LevelNode,
|
|
12
|
-
type RoofSurfaceMaterialRole,
|
|
13
13
|
type RoofNode,
|
|
14
14
|
type RoofSegmentNode,
|
|
15
|
+
type RoofSurfaceMaterialRole,
|
|
15
16
|
type SlabNode,
|
|
16
17
|
type Space,
|
|
17
|
-
type
|
|
18
|
+
type SpawnNode,
|
|
18
19
|
type StairNode,
|
|
19
20
|
type StairSegmentNode,
|
|
21
|
+
type StairSurfaceMaterialRole,
|
|
20
22
|
useScene,
|
|
21
23
|
type WallNode,
|
|
22
24
|
type WallSurfaceSide,
|
|
@@ -26,8 +28,15 @@ import { useViewer } from '@pascal-app/viewer'
|
|
|
26
28
|
import { create } from 'zustand'
|
|
27
29
|
import { persist } from 'zustand/middleware'
|
|
28
30
|
import { getDefaultCatalogItem } from '../components/ui/item-catalog/catalog-items'
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
import {
|
|
32
|
+
type ActivePaintMaterial,
|
|
33
|
+
type PaintableMaterialTarget,
|
|
34
|
+
resolveActivePaintMaterialFromSelection,
|
|
35
|
+
resolvePaintTargetFromSelection,
|
|
36
|
+
type SingleSurfaceMaterialRole,
|
|
37
|
+
} from '../lib/material-paint'
|
|
38
|
+
|
|
39
|
+
const DEFAULT_ACTIVE_SIDEBAR_PANEL = 'ai'
|
|
31
40
|
const DEFAULT_FLOORPLAN_PANE_RATIO = 0.5
|
|
32
41
|
const MIN_FLOORPLAN_PANE_RATIO = 0.15
|
|
33
42
|
const MAX_FLOORPLAN_PANE_RATIO = 0.85
|
|
@@ -37,7 +46,7 @@ export type SplitOrientation = 'horizontal' | 'vertical'
|
|
|
37
46
|
|
|
38
47
|
export type Phase = 'site' | 'structure' | 'furnish'
|
|
39
48
|
|
|
40
|
-
export type Mode = 'select' | 'edit' | 'delete' | 'build'
|
|
49
|
+
export type Mode = 'select' | 'edit' | 'delete' | 'build' | 'material-paint'
|
|
41
50
|
|
|
42
51
|
// Structure mode tools (building elements)
|
|
43
52
|
export type StructureTool =
|
|
@@ -52,6 +61,7 @@ export type StructureTool =
|
|
|
52
61
|
| 'stair'
|
|
53
62
|
| 'item'
|
|
54
63
|
| 'zone'
|
|
64
|
+
| 'spawn'
|
|
55
65
|
| 'window'
|
|
56
66
|
| 'door'
|
|
57
67
|
|
|
@@ -89,13 +99,28 @@ export type MovingFenceEndpoint = {
|
|
|
89
99
|
endpoint: 'start' | 'end'
|
|
90
100
|
}
|
|
91
101
|
|
|
92
|
-
export type MaterialTargetRole =
|
|
102
|
+
export type MaterialTargetRole =
|
|
103
|
+
| WallSurfaceSide
|
|
104
|
+
| StairSurfaceMaterialRole
|
|
105
|
+
| RoofSurfaceMaterialRole
|
|
106
|
+
| SingleSurfaceMaterialRole
|
|
93
107
|
|
|
94
108
|
export type SelectedMaterialTarget = {
|
|
95
109
|
nodeId: AnyNodeId
|
|
96
110
|
role: MaterialTargetRole
|
|
97
111
|
}
|
|
98
112
|
|
|
113
|
+
type MaterialPaintSelectionSnapshot = {
|
|
114
|
+
selectedId: string | null
|
|
115
|
+
activePaintTarget: PaintableMaterialTarget
|
|
116
|
+
activePaintMaterial: ActivePaintMaterial | null
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export type GuideUiState = {
|
|
120
|
+
locked?: boolean
|
|
121
|
+
scaleReferenceVisible?: boolean
|
|
122
|
+
}
|
|
123
|
+
|
|
99
124
|
type EditorState = {
|
|
100
125
|
phase: Phase
|
|
101
126
|
setPhase: (phase: Phase) => void
|
|
@@ -113,12 +138,14 @@ type EditorState = {
|
|
|
113
138
|
| ItemNode
|
|
114
139
|
| WindowNode
|
|
115
140
|
| DoorNode
|
|
116
|
-
| FenceNode
|
|
117
141
|
| CeilingNode
|
|
142
|
+
| ColumnNode
|
|
118
143
|
| SlabNode
|
|
119
144
|
| WallNode
|
|
145
|
+
| FenceNode
|
|
120
146
|
| RoofNode
|
|
121
147
|
| RoofSegmentNode
|
|
148
|
+
| SpawnNode
|
|
122
149
|
| StairNode
|
|
123
150
|
| StairSegmentNode
|
|
124
151
|
| BuildingNode
|
|
@@ -128,12 +155,14 @@ type EditorState = {
|
|
|
128
155
|
| ItemNode
|
|
129
156
|
| WindowNode
|
|
130
157
|
| DoorNode
|
|
131
|
-
| FenceNode
|
|
132
158
|
| CeilingNode
|
|
159
|
+
| ColumnNode
|
|
133
160
|
| SlabNode
|
|
134
161
|
| WallNode
|
|
162
|
+
| FenceNode
|
|
135
163
|
| RoofNode
|
|
136
164
|
| RoofSegmentNode
|
|
165
|
+
| SpawnNode
|
|
137
166
|
| StairNode
|
|
138
167
|
| StairSegmentNode
|
|
139
168
|
| BuildingNode
|
|
@@ -149,8 +178,21 @@ type EditorState = {
|
|
|
149
178
|
setCurvingFence: (fence: FenceNode | null) => void
|
|
150
179
|
selectedMaterialTarget: SelectedMaterialTarget | null
|
|
151
180
|
setSelectedMaterialTarget: (target: SelectedMaterialTarget | null) => void
|
|
181
|
+
activePaintMaterial: ActivePaintMaterial | null
|
|
182
|
+
setActivePaintMaterial: (material: ActivePaintMaterial | null) => void
|
|
183
|
+
activePaintTarget: PaintableMaterialTarget
|
|
184
|
+
setActivePaintTarget: (target: PaintableMaterialTarget) => void
|
|
185
|
+
primeMaterialPaintFromSelection: () => MaterialPaintSelectionSnapshot
|
|
186
|
+
hoveredPaintTarget: PaintableMaterialTarget | null
|
|
187
|
+
setHoveredPaintTarget: (target: PaintableMaterialTarget | null) => void
|
|
188
|
+
isPaintPanelOpen: boolean
|
|
189
|
+
setPaintPanelOpen: (open: boolean) => void
|
|
152
190
|
selectedReferenceId: string | null
|
|
153
191
|
setSelectedReferenceId: (id: string | null) => void
|
|
192
|
+
guideUi: Record<string, GuideUiState>
|
|
193
|
+
setGuideLocked: (guideId: string, locked: boolean) => void
|
|
194
|
+
setGuideScaleReferenceVisible: (guideId: string, visible: boolean) => void
|
|
195
|
+
clearGuideUi: (guideId: string) => void
|
|
154
196
|
// Space detection for cutaway mode
|
|
155
197
|
spaces: Record<string, Space>
|
|
156
198
|
setSpaces: (spaces: Record<string, Space>) => void
|
|
@@ -160,6 +202,9 @@ type EditorState = {
|
|
|
160
202
|
// Preview mode (viewer-like experience inside the editor)
|
|
161
203
|
isPreviewMode: boolean
|
|
162
204
|
setPreviewMode: (preview: boolean) => void
|
|
205
|
+
// Capture mode (snapshot toolbar — hides panels for clean framing)
|
|
206
|
+
isCaptureMode: boolean
|
|
207
|
+
setCaptureMode: (active: boolean) => void
|
|
163
208
|
// View mode (3D only, 2D only, or split 2D+3D)
|
|
164
209
|
viewMode: ViewMode
|
|
165
210
|
setViewMode: (mode: ViewMode) => void
|
|
@@ -175,17 +220,29 @@ type EditorState = {
|
|
|
175
220
|
setFloorplanSelectionTool: (tool: FloorplanSelectionTool) => void
|
|
176
221
|
gridSnapStep: GridSnapStep
|
|
177
222
|
setGridSnapStep: (step: GridSnapStep) => void
|
|
223
|
+
showReferenceFloor: boolean
|
|
224
|
+
toggleReferenceFloor: () => void
|
|
225
|
+
setShowReferenceFloor: (show: boolean) => void
|
|
226
|
+
referenceFloorOffset: number
|
|
227
|
+
setReferenceFloorOffset: (offset: number) => void
|
|
228
|
+
referenceFloorOpacity: number
|
|
229
|
+
setReferenceFloorOpacity: (opacity: number) => void
|
|
230
|
+
// Development-only camera debug flag for inspecting underside geometry
|
|
231
|
+
allowUndergroundCamera: boolean
|
|
232
|
+
setAllowUndergroundCamera: (enabled: boolean) => void
|
|
178
233
|
// First-person walkthrough mode (street view)
|
|
179
234
|
isFirstPersonMode: boolean
|
|
180
235
|
_viewModeBeforeFirstPerson: ViewMode | null
|
|
181
236
|
setFirstPersonMode: (enabled: boolean) => void
|
|
182
|
-
// Development-only camera debug flag for inspecting underside geometry
|
|
183
|
-
allowUndergroundCamera: boolean
|
|
184
|
-
setAllowUndergroundCamera: (enabled: boolean) => void
|
|
185
237
|
activeSidebarPanel: string
|
|
186
238
|
setActiveSidebarPanel: (id: string) => void
|
|
239
|
+
setIsCaptureMode: (enabled: boolean) => void
|
|
187
240
|
floorplanPaneRatio: number
|
|
188
241
|
setFloorplanPaneRatio: (ratio: number) => void
|
|
242
|
+
// Mobile-only: pixel height of the secondary panel sheet while open (0 when closed).
|
|
243
|
+
// Read by the mobile layout so the viewer container can shrink to preview edits.
|
|
244
|
+
mobilePanelSheetHeight: number
|
|
245
|
+
setMobilePanelSheetHeight: (px: number) => void
|
|
189
246
|
}
|
|
190
247
|
|
|
191
248
|
export type PersistedEditorUiState = Pick<
|
|
@@ -200,6 +257,9 @@ type PersistedEditorLayoutState = Pick<
|
|
|
200
257
|
| 'splitOrientation'
|
|
201
258
|
| 'floorplanSelectionTool'
|
|
202
259
|
| 'gridSnapStep'
|
|
260
|
+
| 'showReferenceFloor'
|
|
261
|
+
| 'referenceFloorOffset'
|
|
262
|
+
| 'referenceFloorOpacity'
|
|
203
263
|
>
|
|
204
264
|
type PersistedEditorState = PersistedEditorUiState & PersistedEditorLayoutState
|
|
205
265
|
|
|
@@ -219,6 +279,9 @@ export const DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE: PersistedEditorLayoutState =
|
|
|
219
279
|
splitOrientation: 'horizontal',
|
|
220
280
|
floorplanSelectionTool: 'click',
|
|
221
281
|
gridSnapStep: 0.5,
|
|
282
|
+
showReferenceFloor: false,
|
|
283
|
+
referenceFloorOffset: 1,
|
|
284
|
+
referenceFloorOpacity: 0.35,
|
|
222
285
|
}
|
|
223
286
|
|
|
224
287
|
const GRID_SNAP_STEPS: GridSnapStep[] = [0.5, 0.25, 0.1, 0.05]
|
|
@@ -228,7 +291,7 @@ function normalizeModeForPhase(phase: Phase, mode: Mode | undefined): Mode {
|
|
|
228
291
|
return 'select'
|
|
229
292
|
}
|
|
230
293
|
|
|
231
|
-
return mode === 'build' || mode === 'delete' ? mode : 'select'
|
|
294
|
+
return mode === 'build' || mode === 'delete' || mode === 'material-paint' ? mode : 'select'
|
|
232
295
|
}
|
|
233
296
|
|
|
234
297
|
function normalizeFloorplanPaneRatio(value: unknown): number {
|
|
@@ -328,6 +391,16 @@ function normalizePersistedEditorLayoutState(
|
|
|
328
391
|
gridSnapStep: GRID_SNAP_STEPS.includes(state?.gridSnapStep as GridSnapStep)
|
|
329
392
|
? (state?.gridSnapStep as GridSnapStep)
|
|
330
393
|
: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.gridSnapStep,
|
|
394
|
+
showReferenceFloor: state?.showReferenceFloor === true,
|
|
395
|
+
referenceFloorOffset:
|
|
396
|
+
typeof state?.referenceFloorOffset === 'number' && state.referenceFloorOffset >= 1
|
|
397
|
+
? Math.floor(state.referenceFloorOffset)
|
|
398
|
+
: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.referenceFloorOffset,
|
|
399
|
+
referenceFloorOpacity:
|
|
400
|
+
typeof state?.referenceFloorOpacity === 'number' &&
|
|
401
|
+
Number.isFinite(state.referenceFloorOpacity)
|
|
402
|
+
? Math.min(0.8, Math.max(0.1, state.referenceFloorOpacity))
|
|
403
|
+
: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.referenceFloorOpacity,
|
|
331
404
|
}
|
|
332
405
|
}
|
|
333
406
|
|
|
@@ -387,10 +460,6 @@ export function selectDefaultBuildingAndLevel() {
|
|
|
387
460
|
}
|
|
388
461
|
}
|
|
389
462
|
|
|
390
|
-
function getDefaultSelectedItemForCategory(category: CatalogCategory | null): AssetInput | null {
|
|
391
|
-
return getDefaultCatalogItem(category)
|
|
392
|
-
}
|
|
393
|
-
|
|
394
463
|
const useEditor = create<EditorState>()(
|
|
395
464
|
persist(
|
|
396
465
|
(set, get) => ({
|
|
@@ -412,11 +481,7 @@ const useEditor = create<EditorState>()(
|
|
|
412
481
|
} else if (phase === 'structure') {
|
|
413
482
|
set({ tool: 'wall', catalogCategory: null })
|
|
414
483
|
} else if (phase === 'furnish') {
|
|
415
|
-
set({
|
|
416
|
-
tool: 'item',
|
|
417
|
-
catalogCategory: 'furniture',
|
|
418
|
-
selectedItem: getDefaultSelectedItemForCategory('furniture'),
|
|
419
|
-
})
|
|
484
|
+
set({ tool: 'item', catalogCategory: 'furniture' })
|
|
420
485
|
}
|
|
421
486
|
} else {
|
|
422
487
|
// Reset to select mode and clear tool/catalog when switching phases
|
|
@@ -456,16 +521,11 @@ const useEditor = create<EditorState>()(
|
|
|
456
521
|
} else if (phase === 'structure' && structureLayer === 'elements') {
|
|
457
522
|
set({ tool: 'wall' })
|
|
458
523
|
} else if (phase === 'furnish') {
|
|
459
|
-
set({
|
|
460
|
-
tool: 'item',
|
|
461
|
-
catalogCategory: 'furniture',
|
|
462
|
-
selectedItem: getDefaultSelectedItemForCategory('furniture'),
|
|
463
|
-
})
|
|
524
|
+
set({ tool: 'item', catalogCategory: 'furniture' })
|
|
464
525
|
}
|
|
465
|
-
} else if (phase === 'furnish' && tool === 'item' && !get().selectedItem) {
|
|
466
|
-
const category = get().catalogCategory ?? 'furniture'
|
|
467
|
-
set({ selectedItem: getDefaultSelectedItemForCategory(category) })
|
|
468
526
|
}
|
|
527
|
+
} else if (mode === 'material-paint') {
|
|
528
|
+
get().primeMaterialPaintFromSelection()
|
|
469
529
|
}
|
|
470
530
|
// When leaving build mode, clear tool
|
|
471
531
|
else if (tool) {
|
|
@@ -492,27 +552,17 @@ const useEditor = create<EditorState>()(
|
|
|
492
552
|
})
|
|
493
553
|
},
|
|
494
554
|
catalogCategory: DEFAULT_PERSISTED_EDITOR_UI_STATE.catalogCategory,
|
|
495
|
-
setCatalogCategory: (category) =>
|
|
496
|
-
set((state) => ({
|
|
497
|
-
catalogCategory: category,
|
|
498
|
-
selectedItem:
|
|
499
|
-
category !== null &&
|
|
500
|
-
state.phase === 'furnish' &&
|
|
501
|
-
state.mode === 'build' &&
|
|
502
|
-
state.tool === 'item'
|
|
503
|
-
? getDefaultSelectedItemForCategory(category)
|
|
504
|
-
: state.selectedItem,
|
|
505
|
-
})),
|
|
555
|
+
setCatalogCategory: (category) => set({ catalogCategory: category }),
|
|
506
556
|
selectedItem: null,
|
|
507
557
|
setSelectedItem: (item) => set({ selectedItem: item }),
|
|
508
558
|
movingNode: null as
|
|
509
559
|
| ItemNode
|
|
510
560
|
| WindowNode
|
|
511
561
|
| DoorNode
|
|
512
|
-
| FenceNode
|
|
513
562
|
| CeilingNode
|
|
514
563
|
| SlabNode
|
|
515
564
|
| WallNode
|
|
565
|
+
| FenceNode
|
|
516
566
|
| RoofNode
|
|
517
567
|
| RoofSegmentNode
|
|
518
568
|
| StairNode
|
|
@@ -530,8 +580,79 @@ const useEditor = create<EditorState>()(
|
|
|
530
580
|
setCurvingFence: (fence) => set({ curvingFence: fence }),
|
|
531
581
|
selectedMaterialTarget: null,
|
|
532
582
|
setSelectedMaterialTarget: (target) => set({ selectedMaterialTarget: target }),
|
|
583
|
+
activePaintMaterial: null,
|
|
584
|
+
setActivePaintMaterial: (material) => set({ activePaintMaterial: material }),
|
|
585
|
+
activePaintTarget: 'wall',
|
|
586
|
+
setActivePaintTarget: (target) =>
|
|
587
|
+
set((state) =>
|
|
588
|
+
state.activePaintTarget === target ? state : { activePaintTarget: target },
|
|
589
|
+
),
|
|
590
|
+
primeMaterialPaintFromSelection: () => {
|
|
591
|
+
const selectedId =
|
|
592
|
+
useViewer.getState().selection.selectedIds.length === 1
|
|
593
|
+
? (useViewer.getState().selection.selectedIds[0] ?? null)
|
|
594
|
+
: null
|
|
595
|
+
const activePaintTarget =
|
|
596
|
+
resolvePaintTargetFromSelection({
|
|
597
|
+
nodes: useScene.getState().nodes,
|
|
598
|
+
selectedId,
|
|
599
|
+
}) ?? get().activePaintTarget
|
|
600
|
+
const activePaintMaterial = resolveActivePaintMaterialFromSelection({
|
|
601
|
+
nodes: useScene.getState().nodes,
|
|
602
|
+
selectedId,
|
|
603
|
+
selectedMaterialTarget: get().selectedMaterialTarget,
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
set({
|
|
607
|
+
activePaintTarget,
|
|
608
|
+
...(activePaintMaterial ? { activePaintMaterial } : {}),
|
|
609
|
+
})
|
|
610
|
+
|
|
611
|
+
return {
|
|
612
|
+
selectedId,
|
|
613
|
+
activePaintTarget,
|
|
614
|
+
activePaintMaterial: activePaintMaterial ?? get().activePaintMaterial,
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
hoveredPaintTarget: null,
|
|
618
|
+
setHoveredPaintTarget: (target) =>
|
|
619
|
+
set((state) =>
|
|
620
|
+
state.hoveredPaintTarget === target ? state : { hoveredPaintTarget: target },
|
|
621
|
+
),
|
|
622
|
+
isPaintPanelOpen: false,
|
|
623
|
+
setPaintPanelOpen: (open) => set({ isPaintPanelOpen: open }),
|
|
533
624
|
selectedReferenceId: null,
|
|
534
625
|
setSelectedReferenceId: (id) => set({ selectedReferenceId: id }),
|
|
626
|
+
guideUi: {},
|
|
627
|
+
setGuideLocked: (guideId, locked) =>
|
|
628
|
+
set((state) => ({
|
|
629
|
+
guideUi: {
|
|
630
|
+
...state.guideUi,
|
|
631
|
+
[guideId]: {
|
|
632
|
+
...state.guideUi[guideId],
|
|
633
|
+
locked,
|
|
634
|
+
},
|
|
635
|
+
},
|
|
636
|
+
})),
|
|
637
|
+
setGuideScaleReferenceVisible: (guideId, visible) =>
|
|
638
|
+
set((state) => ({
|
|
639
|
+
guideUi: {
|
|
640
|
+
...state.guideUi,
|
|
641
|
+
[guideId]: {
|
|
642
|
+
...state.guideUi[guideId],
|
|
643
|
+
scaleReferenceVisible: visible,
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
})),
|
|
647
|
+
clearGuideUi: (guideId) =>
|
|
648
|
+
set((state) => {
|
|
649
|
+
if (!state.guideUi[guideId]) {
|
|
650
|
+
return state
|
|
651
|
+
}
|
|
652
|
+
const guideUi = { ...state.guideUi }
|
|
653
|
+
delete guideUi[guideId]
|
|
654
|
+
return { guideUi }
|
|
655
|
+
}),
|
|
535
656
|
spaces: {},
|
|
536
657
|
setSpaces: (spaces) => set({ spaces }),
|
|
537
658
|
editingHole: null,
|
|
@@ -546,6 +667,8 @@ const useEditor = create<EditorState>()(
|
|
|
546
667
|
set({ isPreviewMode: false })
|
|
547
668
|
}
|
|
548
669
|
},
|
|
670
|
+
isCaptureMode: false,
|
|
671
|
+
setCaptureMode: (active) => set({ isCaptureMode: active }),
|
|
549
672
|
viewMode: DEFAULT_PERSISTED_EDITOR_UI_STATE.viewMode,
|
|
550
673
|
setViewMode: (mode) => set({ viewMode: mode, isFloorplanOpen: mode !== '3d' }),
|
|
551
674
|
splitOrientation: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.splitOrientation,
|
|
@@ -563,6 +686,16 @@ const useEditor = create<EditorState>()(
|
|
|
563
686
|
setFloorplanSelectionTool: (tool) => set({ floorplanSelectionTool: tool }),
|
|
564
687
|
gridSnapStep: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.gridSnapStep,
|
|
565
688
|
setGridSnapStep: (step) => set({ gridSnapStep: step }),
|
|
689
|
+
showReferenceFloor: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.showReferenceFloor,
|
|
690
|
+
toggleReferenceFloor: () =>
|
|
691
|
+
set((state) => ({ showReferenceFloor: !state.showReferenceFloor })),
|
|
692
|
+
setShowReferenceFloor: (show) => set({ showReferenceFloor: show }),
|
|
693
|
+
referenceFloorOffset: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.referenceFloorOffset,
|
|
694
|
+
setReferenceFloorOffset: (offset) =>
|
|
695
|
+
set({ referenceFloorOffset: Math.max(1, Math.floor(offset)) }),
|
|
696
|
+
referenceFloorOpacity: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.referenceFloorOpacity,
|
|
697
|
+
setReferenceFloorOpacity: (opacity) =>
|
|
698
|
+
set({ referenceFloorOpacity: Math.min(0.8, Math.max(0.1, opacity)) }),
|
|
566
699
|
allowUndergroundCamera: false,
|
|
567
700
|
setAllowUndergroundCamera: (enabled) => set({ allowUndergroundCamera: enabled }),
|
|
568
701
|
isFirstPersonMode: false,
|
|
@@ -570,8 +703,6 @@ const useEditor = create<EditorState>()(
|
|
|
570
703
|
setFirstPersonMode: (enabled) => {
|
|
571
704
|
if (enabled) {
|
|
572
705
|
const currentViewMode = get().viewMode
|
|
573
|
-
useViewer.getState().setCameraMode('perspective')
|
|
574
|
-
useViewer.getState().setWallMode('up')
|
|
575
706
|
set({
|
|
576
707
|
isFirstPersonMode: true,
|
|
577
708
|
_viewModeBeforeFirstPerson: currentViewMode,
|
|
@@ -581,7 +712,6 @@ const useEditor = create<EditorState>()(
|
|
|
581
712
|
tool: null,
|
|
582
713
|
catalogCategory: null,
|
|
583
714
|
})
|
|
584
|
-
useViewer.getState().setSelection({ selectedIds: [], zoneId: null })
|
|
585
715
|
} else {
|
|
586
716
|
const prevMode = get()._viewModeBeforeFirstPerson
|
|
587
717
|
set({
|
|
@@ -593,29 +723,20 @@ const useEditor = create<EditorState>()(
|
|
|
593
723
|
},
|
|
594
724
|
activeSidebarPanel: DEFAULT_ACTIVE_SIDEBAR_PANEL,
|
|
595
725
|
setActiveSidebarPanel: (id) => set({ activeSidebarPanel: id }),
|
|
726
|
+
setIsCaptureMode: (enabled) => set({ isCaptureMode: enabled }),
|
|
596
727
|
floorplanPaneRatio: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.floorplanPaneRatio,
|
|
597
728
|
setFloorplanPaneRatio: (ratio) =>
|
|
598
729
|
set({ floorplanPaneRatio: normalizeFloorplanPaneRatio(ratio) }),
|
|
730
|
+
mobilePanelSheetHeight: 0,
|
|
731
|
+
setMobilePanelSheetHeight: (px) => set({ mobilePanelSheetHeight: Math.max(0, px) }),
|
|
599
732
|
}),
|
|
600
733
|
{
|
|
601
734
|
name: 'pascal-editor-ui-preferences',
|
|
602
|
-
merge: (persistedState, currentState) => {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
return {
|
|
610
|
-
...mergedState,
|
|
611
|
-
selectedItem:
|
|
612
|
-
mergedState.phase === 'furnish' &&
|
|
613
|
-
mergedState.mode === 'build' &&
|
|
614
|
-
mergedState.tool === 'item'
|
|
615
|
-
? getDefaultSelectedItemForCategory(mergedState.catalogCategory ?? 'furniture')
|
|
616
|
-
: currentState.selectedItem,
|
|
617
|
-
}
|
|
618
|
-
},
|
|
735
|
+
merge: (persistedState, currentState) => ({
|
|
736
|
+
...currentState,
|
|
737
|
+
...normalizePersistedEditorUiState(persistedState as Partial<PersistedEditorState>),
|
|
738
|
+
...normalizePersistedEditorLayoutState(persistedState as Partial<PersistedEditorState>),
|
|
739
|
+
}),
|
|
619
740
|
partialize: (state) => ({
|
|
620
741
|
phase: state.phase,
|
|
621
742
|
mode: state.mode,
|
|
@@ -629,6 +750,9 @@ const useEditor = create<EditorState>()(
|
|
|
629
750
|
splitOrientation: state.splitOrientation,
|
|
630
751
|
floorplanSelectionTool: state.floorplanSelectionTool,
|
|
631
752
|
gridSnapStep: state.gridSnapStep,
|
|
753
|
+
showReferenceFloor: state.showReferenceFloor,
|
|
754
|
+
referenceFloorOffset: state.referenceFloorOffset,
|
|
755
|
+
referenceFloorOpacity: state.referenceFloorOpacity,
|
|
632
756
|
}),
|
|
633
757
|
},
|
|
634
758
|
),
|