@pascal-app/editor 0.5.1 → 0.7.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 +12 -7
- package/src/components/editor/bottom-sheet.tsx +149 -0
- package/src/components/editor/custom-camera-controls.tsx +75 -7
- package/src/components/editor/editor-layout-mobile.tsx +264 -0
- package/src/components/editor/editor-layout-v2.tsx +29 -0
- package/src/components/editor/first-person/build-collider-world.ts +365 -0
- package/src/components/editor/first-person/bvh-ecctrl.tsx +795 -0
- package/src/components/editor/first-person-controls.tsx +496 -143
- package/src/components/editor/floating-action-menu.tsx +281 -83
- package/src/components/editor/floating-building-action-menu.tsx +4 -3
- package/src/components/editor/floorplan-background-selection.ts +113 -0
- package/src/components/editor/floorplan-panel.tsx +10442 -3275
- package/src/components/editor/index.tsx +270 -20
- package/src/components/editor/node-action-menu.tsx +14 -1
- package/src/components/editor/selection-manager.tsx +766 -12
- package/src/components/editor/site-edge-labels.tsx +9 -3
- package/src/components/editor/thumbnail-generator.tsx +350 -157
- 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 +377 -58
- 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 +119 -0
- package/src/components/editor-2d/renderers/floorplan-marquee-layer.tsx +58 -0
- package/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +197 -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 +272 -0
- package/src/components/systems/roof/roof-edit-system.tsx +5 -5
- package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +1 -0
- package/src/components/tools/ceiling/ceiling-hole-editor.tsx +2 -0
- package/src/components/tools/ceiling/ceiling-tool.tsx +5 -5
- package/src/components/tools/ceiling/move-ceiling-tool.tsx +257 -0
- 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 +19 -0
- package/src/components/tools/door/move-door-tool.tsx +38 -8
- package/src/components/tools/fence/curve-fence-tool.tsx +179 -0
- package/src/components/tools/fence/fence-drafting.ts +27 -8
- package/src/components/tools/fence/fence-tool.tsx +159 -3
- package/src/components/tools/fence/move-fence-endpoint-tool.tsx +438 -0
- package/src/components/tools/fence/move-fence-tool.tsx +102 -27
- package/src/components/tools/item/move-tool.tsx +19 -1
- package/src/components/tools/item/placement-math.ts +44 -7
- package/src/components/tools/item/placement-strategies.ts +111 -33
- package/src/components/tools/item/placement-types.ts +7 -0
- package/src/components/tools/item/use-draft-node.ts +2 -0
- package/src/components/tools/item/use-placement-coordinator.tsx +701 -61
- package/src/components/tools/roof/move-roof-tool.tsx +111 -43
- package/src/components/tools/shared/polygon-editor.tsx +244 -29
- package/src/components/tools/shared/segment-angle.ts +156 -0
- package/src/components/tools/slab/move-slab-tool.tsx +182 -0
- package/src/components/tools/slab/slab-boundary-editor.tsx +1 -0
- package/src/components/tools/slab/slab-hole-editor.tsx +2 -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/stair/stair-tool.tsx +11 -3
- package/src/components/tools/tool-manager.tsx +30 -3
- package/src/components/tools/wall/curve-wall-tool.tsx +176 -0
- package/src/components/tools/wall/move-wall-endpoint-tool.tsx +423 -0
- package/src/components/tools/wall/move-wall-tool.tsx +356 -0
- package/src/components/tools/wall/wall-drafting.ts +348 -17
- package/src/components/tools/wall/wall-tool.tsx +134 -2
- package/src/components/tools/window/move-window-tool.tsx +28 -0
- package/src/components/tools/window/window-tool.tsx +17 -0
- package/src/components/ui/action-menu/camera-actions.tsx +37 -33
- package/src/components/ui/action-menu/control-modes.tsx +37 -5
- package/src/components/ui/action-menu/index.tsx +91 -1
- package/src/components/ui/action-menu/structure-tools.tsx +2 -0
- package/src/components/ui/action-menu/view-toggles.tsx +424 -35
- package/src/components/ui/command-palette/editor-commands.tsx +27 -5
- package/src/components/ui/command-palette/index.tsx +0 -1
- package/src/components/ui/controls/material-picker.tsx +189 -169
- package/src/components/ui/controls/slider-control.tsx +88 -26
- package/src/components/ui/floating-level-selector.tsx +286 -55
- package/src/components/ui/helpers/helper-manager.tsx +5 -0
- package/src/components/ui/item-catalog/catalog-items.tsx +1121 -1219
- package/src/components/ui/item-catalog/item-catalog.tsx +42 -175
- package/src/components/ui/level-duplicate-dialog.tsx +115 -0
- package/src/components/ui/panels/ceiling-panel.tsx +47 -27
- package/src/components/ui/panels/column-panel.tsx +715 -0
- package/src/components/ui/panels/door-panel.tsx +986 -294
- package/src/components/ui/panels/fence-panel.tsx +55 -12
- package/src/components/ui/panels/item-panel.tsx +5 -5
- 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 +138 -0
- package/src/components/ui/panels/panel-manager.tsx +241 -30
- package/src/components/ui/panels/panel-wrapper.tsx +48 -39
- package/src/components/ui/panels/reference-panel.tsx +243 -9
- package/src/components/ui/panels/roof-panel.tsx +30 -62
- package/src/components/ui/panels/roof-segment-panel.tsx +8 -23
- package/src/components/ui/panels/slab-panel.tsx +46 -24
- package/src/components/ui/panels/spawn-panel.tsx +155 -0
- package/src/components/ui/panels/stair-panel.tsx +117 -69
- package/src/components/ui/panels/stair-segment-panel.tsx +13 -27
- package/src/components/ui/panels/wall-panel.tsx +71 -17
- package/src/components/ui/panels/window-panel.tsx +665 -146
- package/src/components/ui/sidebar/mobile-tab-bar.tsx +46 -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 +9 -5
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +7 -3
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +77 -0
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +7 -3
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +7 -3
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +138 -56
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +7 -3
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +9 -5
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +7 -3
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +7 -3
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +82 -0
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +7 -3
- package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +3 -3
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +12 -6
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +7 -3
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +7 -3
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +15 -8
- package/src/components/ui/sidebar/tab-bar.tsx +3 -0
- package/src/components/ui/viewer-toolbar.tsx +96 -2
- package/src/components/viewer-overlay.tsx +25 -19
- package/src/hooks/use-auto-frame.ts +45 -0
- package/src/hooks/use-contextual-tools.ts +14 -13
- package/src/hooks/use-keyboard.ts +67 -9
- package/src/hooks/use-mobile.ts +12 -12
- package/src/index.tsx +2 -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/history.ts +20 -0
- package/src/lib/level-duplication.test.ts +72 -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/sfx-player.ts +96 -13
- package/src/lib/stair-duplication.ts +126 -0
- package/src/lib/window-interaction.ts +86 -0
- package/src/store/use-editor.tsx +279 -15
package/src/store/use-editor.tsx
CHANGED
|
@@ -1,23 +1,40 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import type { AssetInput } from '@pascal-app/core'
|
|
4
3
|
import {
|
|
4
|
+
type AnyNodeId,
|
|
5
|
+
type AssetInput,
|
|
5
6
|
type BuildingNode,
|
|
7
|
+
type CeilingNode,
|
|
8
|
+
type ColumnNode,
|
|
6
9
|
type DoorNode,
|
|
7
10
|
type FenceNode,
|
|
8
11
|
type ItemNode,
|
|
9
12
|
type LevelNode,
|
|
10
13
|
type RoofNode,
|
|
11
14
|
type RoofSegmentNode,
|
|
15
|
+
type RoofSurfaceMaterialRole,
|
|
16
|
+
type SlabNode,
|
|
12
17
|
type Space,
|
|
18
|
+
type SpawnNode,
|
|
13
19
|
type StairNode,
|
|
14
20
|
type StairSegmentNode,
|
|
21
|
+
type StairSurfaceMaterialRole,
|
|
15
22
|
useScene,
|
|
23
|
+
type WallNode,
|
|
24
|
+
type WallSurfaceSide,
|
|
16
25
|
type WindowNode,
|
|
17
26
|
} from '@pascal-app/core'
|
|
18
27
|
import { useViewer } from '@pascal-app/viewer'
|
|
19
28
|
import { create } from 'zustand'
|
|
20
29
|
import { persist } from 'zustand/middleware'
|
|
30
|
+
import { getDefaultCatalogItem } from '../components/ui/item-catalog/catalog-items'
|
|
31
|
+
import {
|
|
32
|
+
type ActivePaintMaterial,
|
|
33
|
+
type PaintableMaterialTarget,
|
|
34
|
+
resolveActivePaintMaterialFromSelection,
|
|
35
|
+
resolvePaintTargetFromSelection,
|
|
36
|
+
type SingleSurfaceMaterialRole,
|
|
37
|
+
} from '../lib/material-paint'
|
|
21
38
|
|
|
22
39
|
const DEFAULT_ACTIVE_SIDEBAR_PANEL = 'site'
|
|
23
40
|
const DEFAULT_FLOORPLAN_PANE_RATIO = 0.5
|
|
@@ -29,7 +46,7 @@ export type SplitOrientation = 'horizontal' | 'vertical'
|
|
|
29
46
|
|
|
30
47
|
export type Phase = 'site' | 'structure' | 'furnish'
|
|
31
48
|
|
|
32
|
-
export type Mode = 'select' | 'edit' | 'delete' | 'build'
|
|
49
|
+
export type Mode = 'select' | 'edit' | 'delete' | 'build' | 'material-paint'
|
|
33
50
|
|
|
34
51
|
// Structure mode tools (building elements)
|
|
35
52
|
export type StructureTool =
|
|
@@ -44,6 +61,7 @@ export type StructureTool =
|
|
|
44
61
|
| 'stair'
|
|
45
62
|
| 'item'
|
|
46
63
|
| 'zone'
|
|
64
|
+
| 'spawn'
|
|
47
65
|
| 'window'
|
|
48
66
|
| 'door'
|
|
49
67
|
|
|
@@ -66,10 +84,43 @@ export type CatalogCategory =
|
|
|
66
84
|
export type StructureLayer = 'zones' | 'elements'
|
|
67
85
|
|
|
68
86
|
export type FloorplanSelectionTool = 'click' | 'marquee'
|
|
87
|
+
export type GridSnapStep = 0.5 | 0.25 | 0.1 | 0.05
|
|
69
88
|
|
|
70
89
|
// Combined tool type
|
|
71
90
|
export type Tool = SiteTool | StructureTool | FurnishTool
|
|
72
91
|
|
|
92
|
+
export type MovingWallEndpoint = {
|
|
93
|
+
wall: WallNode
|
|
94
|
+
endpoint: 'start' | 'end'
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export type MovingFenceEndpoint = {
|
|
98
|
+
fence: FenceNode
|
|
99
|
+
endpoint: 'start' | 'end'
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type MaterialTargetRole =
|
|
103
|
+
| WallSurfaceSide
|
|
104
|
+
| StairSurfaceMaterialRole
|
|
105
|
+
| RoofSurfaceMaterialRole
|
|
106
|
+
| SingleSurfaceMaterialRole
|
|
107
|
+
|
|
108
|
+
export type SelectedMaterialTarget = {
|
|
109
|
+
nodeId: AnyNodeId
|
|
110
|
+
role: MaterialTargetRole
|
|
111
|
+
}
|
|
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
|
+
|
|
73
124
|
type EditorState = {
|
|
74
125
|
phase: Phase
|
|
75
126
|
setPhase: (phase: Phase) => void
|
|
@@ -88,8 +139,13 @@ type EditorState = {
|
|
|
88
139
|
| WindowNode
|
|
89
140
|
| DoorNode
|
|
90
141
|
| FenceNode
|
|
142
|
+
| CeilingNode
|
|
143
|
+
| ColumnNode
|
|
144
|
+
| SlabNode
|
|
145
|
+
| WallNode
|
|
91
146
|
| RoofNode
|
|
92
147
|
| RoofSegmentNode
|
|
148
|
+
| SpawnNode
|
|
93
149
|
| StairNode
|
|
94
150
|
| StairSegmentNode
|
|
95
151
|
| BuildingNode
|
|
@@ -100,15 +156,43 @@ type EditorState = {
|
|
|
100
156
|
| WindowNode
|
|
101
157
|
| DoorNode
|
|
102
158
|
| FenceNode
|
|
159
|
+
| CeilingNode
|
|
160
|
+
| ColumnNode
|
|
161
|
+
| SlabNode
|
|
162
|
+
| WallNode
|
|
103
163
|
| RoofNode
|
|
104
164
|
| RoofSegmentNode
|
|
165
|
+
| SpawnNode
|
|
105
166
|
| StairNode
|
|
106
167
|
| StairSegmentNode
|
|
107
168
|
| BuildingNode
|
|
108
169
|
| null,
|
|
109
170
|
) => void
|
|
171
|
+
movingWallEndpoint: MovingWallEndpoint | null
|
|
172
|
+
setMovingWallEndpoint: (value: MovingWallEndpoint | null) => void
|
|
173
|
+
movingFenceEndpoint: MovingFenceEndpoint | null
|
|
174
|
+
setMovingFenceEndpoint: (value: MovingFenceEndpoint | null) => void
|
|
175
|
+
curvingWall: WallNode | null
|
|
176
|
+
setCurvingWall: (wall: WallNode | null) => void
|
|
177
|
+
curvingFence: FenceNode | null
|
|
178
|
+
setCurvingFence: (fence: FenceNode | null) => void
|
|
179
|
+
selectedMaterialTarget: SelectedMaterialTarget | null
|
|
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
|
|
110
190
|
selectedReferenceId: string | null
|
|
111
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
|
|
112
196
|
// Space detection for cutaway mode
|
|
113
197
|
spaces: Record<string, Space>
|
|
114
198
|
setSpaces: (spaces: Record<string, Space>) => void
|
|
@@ -131,6 +215,15 @@ type EditorState = {
|
|
|
131
215
|
setFloorplanHovered: (hovered: boolean) => void
|
|
132
216
|
floorplanSelectionTool: FloorplanSelectionTool
|
|
133
217
|
setFloorplanSelectionTool: (tool: FloorplanSelectionTool) => void
|
|
218
|
+
gridSnapStep: GridSnapStep
|
|
219
|
+
setGridSnapStep: (step: GridSnapStep) => void
|
|
220
|
+
showReferenceFloor: boolean
|
|
221
|
+
toggleReferenceFloor: () => void
|
|
222
|
+
setShowReferenceFloor: (show: boolean) => void
|
|
223
|
+
referenceFloorOffset: number
|
|
224
|
+
setReferenceFloorOffset: (offset: number) => void
|
|
225
|
+
referenceFloorOpacity: number
|
|
226
|
+
setReferenceFloorOpacity: (opacity: number) => void
|
|
134
227
|
// First-person walkthrough mode (street view)
|
|
135
228
|
isFirstPersonMode: boolean
|
|
136
229
|
_viewModeBeforeFirstPerson: ViewMode | null
|
|
@@ -140,6 +233,10 @@ type EditorState = {
|
|
|
140
233
|
setAllowUndergroundCamera: (enabled: boolean) => void
|
|
141
234
|
activeSidebarPanel: string
|
|
142
235
|
setActiveSidebarPanel: (id: string) => void
|
|
236
|
+
mobilePanelSheetHeight: number
|
|
237
|
+
setMobilePanelSheetHeight: (height: number) => void
|
|
238
|
+
isCaptureMode: boolean
|
|
239
|
+
setIsCaptureMode: (enabled: boolean) => void
|
|
143
240
|
floorplanPaneRatio: number
|
|
144
241
|
setFloorplanPaneRatio: (ratio: number) => void
|
|
145
242
|
}
|
|
@@ -151,7 +248,14 @@ export type PersistedEditorUiState = Pick<
|
|
|
151
248
|
|
|
152
249
|
type PersistedEditorLayoutState = Pick<
|
|
153
250
|
EditorState,
|
|
154
|
-
|
|
251
|
+
| 'activeSidebarPanel'
|
|
252
|
+
| 'floorplanPaneRatio'
|
|
253
|
+
| 'splitOrientation'
|
|
254
|
+
| 'floorplanSelectionTool'
|
|
255
|
+
| 'gridSnapStep'
|
|
256
|
+
| 'showReferenceFloor'
|
|
257
|
+
| 'referenceFloorOffset'
|
|
258
|
+
| 'referenceFloorOpacity'
|
|
155
259
|
>
|
|
156
260
|
type PersistedEditorState = PersistedEditorUiState & PersistedEditorLayoutState
|
|
157
261
|
|
|
@@ -170,14 +274,20 @@ export const DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE: PersistedEditorLayoutState =
|
|
|
170
274
|
floorplanPaneRatio: DEFAULT_FLOORPLAN_PANE_RATIO,
|
|
171
275
|
splitOrientation: 'horizontal',
|
|
172
276
|
floorplanSelectionTool: 'click',
|
|
277
|
+
gridSnapStep: 0.5,
|
|
278
|
+
showReferenceFloor: false,
|
|
279
|
+
referenceFloorOffset: 1,
|
|
280
|
+
referenceFloorOpacity: 0.35,
|
|
173
281
|
}
|
|
174
282
|
|
|
283
|
+
const GRID_SNAP_STEPS: GridSnapStep[] = [0.5, 0.25, 0.1, 0.05]
|
|
284
|
+
|
|
175
285
|
function normalizeModeForPhase(phase: Phase, mode: Mode | undefined): Mode {
|
|
176
286
|
if (phase === 'site') {
|
|
177
287
|
return 'select'
|
|
178
288
|
}
|
|
179
289
|
|
|
180
|
-
return mode === 'build' || mode === 'delete' ? mode : 'select'
|
|
290
|
+
return mode === 'build' || mode === 'delete' || mode === 'material-paint' ? mode : 'select'
|
|
181
291
|
}
|
|
182
292
|
|
|
183
293
|
function normalizeFloorplanPaneRatio(value: unknown): number {
|
|
@@ -274,6 +384,19 @@ function normalizePersistedEditorLayoutState(
|
|
|
274
384
|
floorplanPaneRatio: normalizeFloorplanPaneRatio(state?.floorplanPaneRatio),
|
|
275
385
|
splitOrientation: state?.splitOrientation === 'vertical' ? 'vertical' : 'horizontal',
|
|
276
386
|
floorplanSelectionTool: state?.floorplanSelectionTool === 'marquee' ? 'marquee' : 'click',
|
|
387
|
+
gridSnapStep: GRID_SNAP_STEPS.includes(state?.gridSnapStep as GridSnapStep)
|
|
388
|
+
? (state?.gridSnapStep as GridSnapStep)
|
|
389
|
+
: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.gridSnapStep,
|
|
390
|
+
showReferenceFloor: state?.showReferenceFloor === true,
|
|
391
|
+
referenceFloorOffset:
|
|
392
|
+
typeof state?.referenceFloorOffset === 'number' && state.referenceFloorOffset >= 1
|
|
393
|
+
? Math.floor(state.referenceFloorOffset)
|
|
394
|
+
: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.referenceFloorOffset,
|
|
395
|
+
referenceFloorOpacity:
|
|
396
|
+
typeof state?.referenceFloorOpacity === 'number' &&
|
|
397
|
+
Number.isFinite(state.referenceFloorOpacity)
|
|
398
|
+
? Math.min(0.8, Math.max(0.1, state.referenceFloorOpacity))
|
|
399
|
+
: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.referenceFloorOpacity,
|
|
277
400
|
}
|
|
278
401
|
}
|
|
279
402
|
|
|
@@ -333,6 +456,10 @@ export function selectDefaultBuildingAndLevel() {
|
|
|
333
456
|
}
|
|
334
457
|
}
|
|
335
458
|
|
|
459
|
+
function getDefaultSelectedItemForCategory(category: CatalogCategory | null): AssetInput | null {
|
|
460
|
+
return getDefaultCatalogItem(category)
|
|
461
|
+
}
|
|
462
|
+
|
|
336
463
|
const useEditor = create<EditorState>()(
|
|
337
464
|
persist(
|
|
338
465
|
(set, get) => ({
|
|
@@ -354,7 +481,11 @@ const useEditor = create<EditorState>()(
|
|
|
354
481
|
} else if (phase === 'structure') {
|
|
355
482
|
set({ tool: 'wall', catalogCategory: null })
|
|
356
483
|
} else if (phase === 'furnish') {
|
|
357
|
-
set({
|
|
484
|
+
set({
|
|
485
|
+
tool: 'item',
|
|
486
|
+
catalogCategory: 'furniture',
|
|
487
|
+
selectedItem: getDefaultSelectedItemForCategory('furniture'),
|
|
488
|
+
})
|
|
358
489
|
}
|
|
359
490
|
} else {
|
|
360
491
|
// Reset to select mode and clear tool/catalog when switching phases
|
|
@@ -394,9 +525,18 @@ const useEditor = create<EditorState>()(
|
|
|
394
525
|
} else if (phase === 'structure' && structureLayer === 'elements') {
|
|
395
526
|
set({ tool: 'wall' })
|
|
396
527
|
} else if (phase === 'furnish') {
|
|
397
|
-
set({
|
|
528
|
+
set({
|
|
529
|
+
tool: 'item',
|
|
530
|
+
catalogCategory: 'furniture',
|
|
531
|
+
selectedItem: getDefaultSelectedItemForCategory('furniture'),
|
|
532
|
+
})
|
|
398
533
|
}
|
|
534
|
+
} else if (phase === 'furnish' && tool === 'item' && !get().selectedItem) {
|
|
535
|
+
const category = get().catalogCategory ?? 'furniture'
|
|
536
|
+
set({ selectedItem: getDefaultSelectedItemForCategory(category) })
|
|
399
537
|
}
|
|
538
|
+
} else if (mode === 'material-paint') {
|
|
539
|
+
get().primeMaterialPaintFromSelection()
|
|
400
540
|
}
|
|
401
541
|
// When leaving build mode, clear tool
|
|
402
542
|
else if (tool) {
|
|
@@ -423,13 +563,27 @@ const useEditor = create<EditorState>()(
|
|
|
423
563
|
})
|
|
424
564
|
},
|
|
425
565
|
catalogCategory: DEFAULT_PERSISTED_EDITOR_UI_STATE.catalogCategory,
|
|
426
|
-
setCatalogCategory: (category) =>
|
|
566
|
+
setCatalogCategory: (category) =>
|
|
567
|
+
set((state) => ({
|
|
568
|
+
catalogCategory: category,
|
|
569
|
+
selectedItem:
|
|
570
|
+
category !== null &&
|
|
571
|
+
state.phase === 'furnish' &&
|
|
572
|
+
state.mode === 'build' &&
|
|
573
|
+
state.tool === 'item'
|
|
574
|
+
? getDefaultSelectedItemForCategory(category)
|
|
575
|
+
: state.selectedItem,
|
|
576
|
+
})),
|
|
427
577
|
selectedItem: null,
|
|
428
578
|
setSelectedItem: (item) => set({ selectedItem: item }),
|
|
429
579
|
movingNode: null as
|
|
430
580
|
| ItemNode
|
|
431
581
|
| WindowNode
|
|
432
582
|
| DoorNode
|
|
583
|
+
| FenceNode
|
|
584
|
+
| CeilingNode
|
|
585
|
+
| SlabNode
|
|
586
|
+
| WallNode
|
|
433
587
|
| RoofNode
|
|
434
588
|
| RoofSegmentNode
|
|
435
589
|
| StairNode
|
|
@@ -437,8 +591,89 @@ const useEditor = create<EditorState>()(
|
|
|
437
591
|
| BuildingNode
|
|
438
592
|
| null,
|
|
439
593
|
setMovingNode: (node) => set({ movingNode: node }),
|
|
594
|
+
movingWallEndpoint: null,
|
|
595
|
+
setMovingWallEndpoint: (value) => set({ movingWallEndpoint: value }),
|
|
596
|
+
movingFenceEndpoint: null,
|
|
597
|
+
setMovingFenceEndpoint: (value) => set({ movingFenceEndpoint: value }),
|
|
598
|
+
curvingWall: null,
|
|
599
|
+
setCurvingWall: (wall) => set({ curvingWall: wall }),
|
|
600
|
+
curvingFence: null,
|
|
601
|
+
setCurvingFence: (fence) => set({ curvingFence: fence }),
|
|
602
|
+
selectedMaterialTarget: null,
|
|
603
|
+
setSelectedMaterialTarget: (target) => set({ selectedMaterialTarget: target }),
|
|
604
|
+
activePaintMaterial: null,
|
|
605
|
+
setActivePaintMaterial: (material) => set({ activePaintMaterial: material }),
|
|
606
|
+
activePaintTarget: 'wall',
|
|
607
|
+
setActivePaintTarget: (target) =>
|
|
608
|
+
set((state) =>
|
|
609
|
+
state.activePaintTarget === target ? state : { activePaintTarget: target },
|
|
610
|
+
),
|
|
611
|
+
primeMaterialPaintFromSelection: () => {
|
|
612
|
+
const selectedId =
|
|
613
|
+
useViewer.getState().selection.selectedIds.length === 1
|
|
614
|
+
? (useViewer.getState().selection.selectedIds[0] ?? null)
|
|
615
|
+
: null
|
|
616
|
+
const activePaintTarget =
|
|
617
|
+
resolvePaintTargetFromSelection({
|
|
618
|
+
nodes: useScene.getState().nodes,
|
|
619
|
+
selectedId,
|
|
620
|
+
}) ?? get().activePaintTarget
|
|
621
|
+
const activePaintMaterial = resolveActivePaintMaterialFromSelection({
|
|
622
|
+
nodes: useScene.getState().nodes,
|
|
623
|
+
selectedId,
|
|
624
|
+
selectedMaterialTarget: get().selectedMaterialTarget,
|
|
625
|
+
})
|
|
626
|
+
|
|
627
|
+
set({
|
|
628
|
+
activePaintTarget,
|
|
629
|
+
...(activePaintMaterial ? { activePaintMaterial } : {}),
|
|
630
|
+
})
|
|
631
|
+
|
|
632
|
+
return {
|
|
633
|
+
selectedId,
|
|
634
|
+
activePaintTarget,
|
|
635
|
+
activePaintMaterial: activePaintMaterial ?? get().activePaintMaterial,
|
|
636
|
+
}
|
|
637
|
+
},
|
|
638
|
+
hoveredPaintTarget: null,
|
|
639
|
+
setHoveredPaintTarget: (target) =>
|
|
640
|
+
set((state) =>
|
|
641
|
+
state.hoveredPaintTarget === target ? state : { hoveredPaintTarget: target },
|
|
642
|
+
),
|
|
643
|
+
isPaintPanelOpen: false,
|
|
644
|
+
setPaintPanelOpen: (open) => set({ isPaintPanelOpen: open }),
|
|
440
645
|
selectedReferenceId: null,
|
|
441
646
|
setSelectedReferenceId: (id) => set({ selectedReferenceId: id }),
|
|
647
|
+
guideUi: {},
|
|
648
|
+
setGuideLocked: (guideId, locked) =>
|
|
649
|
+
set((state) => ({
|
|
650
|
+
guideUi: {
|
|
651
|
+
...state.guideUi,
|
|
652
|
+
[guideId]: {
|
|
653
|
+
...state.guideUi[guideId],
|
|
654
|
+
locked,
|
|
655
|
+
},
|
|
656
|
+
},
|
|
657
|
+
})),
|
|
658
|
+
setGuideScaleReferenceVisible: (guideId, visible) =>
|
|
659
|
+
set((state) => ({
|
|
660
|
+
guideUi: {
|
|
661
|
+
...state.guideUi,
|
|
662
|
+
[guideId]: {
|
|
663
|
+
...state.guideUi[guideId],
|
|
664
|
+
scaleReferenceVisible: visible,
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
})),
|
|
668
|
+
clearGuideUi: (guideId) =>
|
|
669
|
+
set((state) => {
|
|
670
|
+
if (!state.guideUi[guideId]) {
|
|
671
|
+
return state
|
|
672
|
+
}
|
|
673
|
+
const guideUi = { ...state.guideUi }
|
|
674
|
+
delete guideUi[guideId]
|
|
675
|
+
return { guideUi }
|
|
676
|
+
}),
|
|
442
677
|
spaces: {},
|
|
443
678
|
setSpaces: (spaces) => set({ spaces }),
|
|
444
679
|
editingHole: null,
|
|
@@ -468,6 +703,18 @@ const useEditor = create<EditorState>()(
|
|
|
468
703
|
setFloorplanHovered: (hovered) => set({ isFloorplanHovered: hovered }),
|
|
469
704
|
floorplanSelectionTool: 'click' as FloorplanSelectionTool,
|
|
470
705
|
setFloorplanSelectionTool: (tool) => set({ floorplanSelectionTool: tool }),
|
|
706
|
+
gridSnapStep: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.gridSnapStep,
|
|
707
|
+
setGridSnapStep: (step) => set({ gridSnapStep: step }),
|
|
708
|
+
showReferenceFloor: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.showReferenceFloor,
|
|
709
|
+
toggleReferenceFloor: () =>
|
|
710
|
+
set((state) => ({ showReferenceFloor: !state.showReferenceFloor })),
|
|
711
|
+
setShowReferenceFloor: (show) => set({ showReferenceFloor: show }),
|
|
712
|
+
referenceFloorOffset: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.referenceFloorOffset,
|
|
713
|
+
setReferenceFloorOffset: (offset) =>
|
|
714
|
+
set({ referenceFloorOffset: Math.max(1, Math.floor(offset)) }),
|
|
715
|
+
referenceFloorOpacity: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.referenceFloorOpacity,
|
|
716
|
+
setReferenceFloorOpacity: (opacity) =>
|
|
717
|
+
set({ referenceFloorOpacity: Math.min(0.8, Math.max(0.1, opacity)) }),
|
|
471
718
|
allowUndergroundCamera: false,
|
|
472
719
|
setAllowUndergroundCamera: (enabled) => set({ allowUndergroundCamera: enabled }),
|
|
473
720
|
isFirstPersonMode: false,
|
|
@@ -475,8 +722,6 @@ const useEditor = create<EditorState>()(
|
|
|
475
722
|
setFirstPersonMode: (enabled) => {
|
|
476
723
|
if (enabled) {
|
|
477
724
|
const currentViewMode = get().viewMode
|
|
478
|
-
useViewer.getState().setCameraMode('perspective')
|
|
479
|
-
useViewer.getState().setWallMode('up')
|
|
480
725
|
set({
|
|
481
726
|
isFirstPersonMode: true,
|
|
482
727
|
_viewModeBeforeFirstPerson: currentViewMode,
|
|
@@ -486,7 +731,6 @@ const useEditor = create<EditorState>()(
|
|
|
486
731
|
tool: null,
|
|
487
732
|
catalogCategory: null,
|
|
488
733
|
})
|
|
489
|
-
useViewer.getState().setSelection({ selectedIds: [], zoneId: null })
|
|
490
734
|
} else {
|
|
491
735
|
const prevMode = get()._viewModeBeforeFirstPerson
|
|
492
736
|
set({
|
|
@@ -498,17 +742,33 @@ const useEditor = create<EditorState>()(
|
|
|
498
742
|
},
|
|
499
743
|
activeSidebarPanel: DEFAULT_ACTIVE_SIDEBAR_PANEL,
|
|
500
744
|
setActiveSidebarPanel: (id) => set({ activeSidebarPanel: id }),
|
|
745
|
+
mobilePanelSheetHeight: 0,
|
|
746
|
+
setMobilePanelSheetHeight: (height) => set({ mobilePanelSheetHeight: height }),
|
|
747
|
+
isCaptureMode: false,
|
|
748
|
+
setIsCaptureMode: (enabled) => set({ isCaptureMode: enabled }),
|
|
501
749
|
floorplanPaneRatio: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.floorplanPaneRatio,
|
|
502
750
|
setFloorplanPaneRatio: (ratio) =>
|
|
503
751
|
set({ floorplanPaneRatio: normalizeFloorplanPaneRatio(ratio) }),
|
|
504
752
|
}),
|
|
505
753
|
{
|
|
506
754
|
name: 'pascal-editor-ui-preferences',
|
|
507
|
-
merge: (persistedState, currentState) =>
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
755
|
+
merge: (persistedState, currentState) => {
|
|
756
|
+
const mergedState = {
|
|
757
|
+
...currentState,
|
|
758
|
+
...normalizePersistedEditorUiState(persistedState as Partial<PersistedEditorState>),
|
|
759
|
+
...normalizePersistedEditorLayoutState(persistedState as Partial<PersistedEditorState>),
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
return {
|
|
763
|
+
...mergedState,
|
|
764
|
+
selectedItem:
|
|
765
|
+
mergedState.phase === 'furnish' &&
|
|
766
|
+
mergedState.mode === 'build' &&
|
|
767
|
+
mergedState.tool === 'item'
|
|
768
|
+
? getDefaultSelectedItemForCategory(mergedState.catalogCategory ?? 'furniture')
|
|
769
|
+
: currentState.selectedItem,
|
|
770
|
+
}
|
|
771
|
+
},
|
|
512
772
|
partialize: (state) => ({
|
|
513
773
|
phase: state.phase,
|
|
514
774
|
mode: state.mode,
|
|
@@ -521,6 +781,10 @@ const useEditor = create<EditorState>()(
|
|
|
521
781
|
floorplanPaneRatio: state.floorplanPaneRatio,
|
|
522
782
|
splitOrientation: state.splitOrientation,
|
|
523
783
|
floorplanSelectionTool: state.floorplanSelectionTool,
|
|
784
|
+
gridSnapStep: state.gridSnapStep,
|
|
785
|
+
showReferenceFloor: state.showReferenceFloor,
|
|
786
|
+
referenceFloorOffset: state.referenceFloorOffset,
|
|
787
|
+
referenceFloorOpacity: state.referenceFloorOpacity,
|
|
524
788
|
}),
|
|
525
789
|
},
|
|
526
790
|
),
|