Remove material pickers from side panels
This commit is contained in:
@@ -1,13 +1,12 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { type AnyNode, type CeilingNode, type MaterialSchema, useScene } from '@pascal-app/core'
|
import { type AnyNode, type CeilingNode, useScene } from '@pascal-app/core'
|
||||||
import { useViewer } from '@pascal-app/viewer'
|
import { useViewer } from '@pascal-app/viewer'
|
||||||
import { Edit, Move, Plus, Trash2 } from 'lucide-react'
|
import { Edit, Move, Plus, Trash2 } from 'lucide-react'
|
||||||
import { useCallback, useEffect } from 'react'
|
import { useCallback, useEffect } from 'react'
|
||||||
import { sfxEmitter } from '../../../lib/sfx-bus'
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
||||||
import useEditor from '../../../store/use-editor'
|
import useEditor from '../../../store/use-editor'
|
||||||
import { ActionButton, ActionGroup } from '../controls/action-button'
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
||||||
import { MaterialPicker } from '../controls/material-picker'
|
|
||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SliderControl } from '../controls/slider-control'
|
import { SliderControl } from '../controls/slider-control'
|
||||||
import { PanelWrapper } from './panel-wrapper'
|
import { PanelWrapper } from './panel-wrapper'
|
||||||
@@ -32,20 +31,6 @@ export function CeilingPanel() {
|
|||||||
[selectedId, updateNode],
|
[selectedId, updateNode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleMaterialChange = useCallback(
|
|
||||||
(material: MaterialSchema) => {
|
|
||||||
handleUpdate({ material, materialPreset: undefined })
|
|
||||||
},
|
|
||||||
[handleUpdate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleMaterialPresetChange = useCallback(
|
|
||||||
(materialPreset: string) => {
|
|
||||||
handleUpdate({ materialPreset, material: undefined })
|
|
||||||
},
|
|
||||||
[handleUpdate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setSelection({ selectedIds: [] })
|
setSelection({ selectedIds: [] })
|
||||||
setEditingHole(null)
|
setEditingHole(null)
|
||||||
@@ -257,15 +242,6 @@ export function CeilingPanel() {
|
|||||||
</div>
|
</div>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
|
|
||||||
<PanelSection title="Material">
|
|
||||||
<MaterialPicker
|
|
||||||
nodeType="ceiling"
|
|
||||||
onChange={handleMaterialChange}
|
|
||||||
onSelectMaterialPreset={handleMaterialPresetChange}
|
|
||||||
selectedMaterialPreset={node.materialPreset}
|
|
||||||
value={node.material}
|
|
||||||
/>
|
|
||||||
</PanelSection>
|
|
||||||
<ActionGroup>
|
<ActionGroup>
|
||||||
<ActionButton icon={<Move className="h-3.5 w-3.5" />} label="Move" onClick={handleMove} />
|
<ActionButton icon={<Move className="h-3.5 w-3.5" />} label="Move" onClick={handleMove} />
|
||||||
</ActionGroup>
|
</ActionGroup>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
DoorNode,
|
DoorNode,
|
||||||
emitter,
|
emitter,
|
||||||
type MaterialSchema,
|
|
||||||
useScene,
|
useScene,
|
||||||
} from '@pascal-app/core'
|
} from '@pascal-app/core'
|
||||||
import { useViewer } from '@pascal-app/viewer'
|
import { useViewer } from '@pascal-app/viewer'
|
||||||
@@ -15,7 +14,6 @@ import { usePresetsAdapter } from '../../../contexts/presets-context'
|
|||||||
import { sfxEmitter } from '../../../lib/sfx-bus'
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
||||||
import useEditor from '../../../store/use-editor'
|
import useEditor from '../../../store/use-editor'
|
||||||
import { ActionButton, ActionGroup } from '../controls/action-button'
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
||||||
import { MaterialPicker } from '../controls/material-picker'
|
|
||||||
import { MetricControl } from '../controls/metric-control'
|
import { MetricControl } from '../controls/metric-control'
|
||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SegmentedControl } from '../controls/segmented-control'
|
import { SegmentedControl } from '../controls/segmented-control'
|
||||||
@@ -46,13 +44,6 @@ export function DoorPanel() {
|
|||||||
[selectedId, updateNode],
|
[selectedId, updateNode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleMaterialChange = useCallback(
|
|
||||||
(material: MaterialSchema) => {
|
|
||||||
handleUpdate({ material })
|
|
||||||
},
|
|
||||||
[handleUpdate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setSelection({ selectedIds: [] })
|
setSelection({ selectedIds: [] })
|
||||||
}, [setSelection])
|
}, [setSelection])
|
||||||
@@ -592,9 +583,6 @@ export function DoorPanel() {
|
|||||||
/>
|
/>
|
||||||
</ActionGroup>
|
</ActionGroup>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
<PanelSection title="Material">
|
|
||||||
<MaterialPicker onChange={handleMaterialChange} value={node.material} />
|
|
||||||
</PanelSection>
|
|
||||||
</PanelWrapper>
|
</PanelWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { type AnyNode, type AnyNodeId, type FenceNode, type MaterialSchema, useScene } from '@pascal-app/core'
|
import { type AnyNode, type AnyNodeId, type FenceNode, useScene } from '@pascal-app/core'
|
||||||
import { useViewer } from '@pascal-app/viewer'
|
import { useViewer } from '@pascal-app/viewer'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { MaterialPicker } from '../controls/material-picker'
|
|
||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SegmentedControl } from '../controls/segmented-control'
|
import { SegmentedControl } from '../controls/segmented-control'
|
||||||
import { SliderControl } from '../controls/slider-control'
|
import { SliderControl } from '../controls/slider-control'
|
||||||
@@ -67,20 +66,6 @@ export function FencePanel() {
|
|||||||
setSelection({ selectedIds: [] })
|
setSelection({ selectedIds: [] })
|
||||||
}, [setSelection])
|
}, [setSelection])
|
||||||
|
|
||||||
const handleMaterialPresetChange = useCallback(
|
|
||||||
(materialPreset: string) => {
|
|
||||||
handleUpdate({ materialPreset, material: undefined })
|
|
||||||
},
|
|
||||||
[handleUpdate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleCustomMaterialChange = useCallback(
|
|
||||||
(material: MaterialSchema) => {
|
|
||||||
handleUpdate({ material, materialPreset: undefined })
|
|
||||||
},
|
|
||||||
[handleUpdate],
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!(node && node.type === 'fence' && selectedId && selectedCount === 1)) return null
|
if (!(node && node.type === 'fence' && selectedId && selectedCount === 1)) return null
|
||||||
|
|
||||||
const dx = node.end[0] - node.start[0]
|
const dx = node.end[0] - node.start[0]
|
||||||
@@ -204,15 +189,6 @@ export function FencePanel() {
|
|||||||
/>
|
/>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
|
|
||||||
<PanelSection title="Material">
|
|
||||||
<MaterialPicker
|
|
||||||
nodeType="fence"
|
|
||||||
onChange={handleCustomMaterialChange}
|
|
||||||
onSelectMaterialPreset={handleMaterialPresetChange}
|
|
||||||
selectedMaterialPreset={node.materialPreset}
|
|
||||||
value={node.material}
|
|
||||||
/>
|
|
||||||
</PanelSection>
|
|
||||||
</PanelWrapper>
|
</PanelWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
import {
|
import {
|
||||||
type AnyNode,
|
type AnyNode,
|
||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
getEffectiveRoofSurfaceMaterial,
|
|
||||||
type MaterialSchema,
|
|
||||||
type RoofNode,
|
type RoofNode,
|
||||||
RoofNode as RoofNodeSchema,
|
RoofNode as RoofNodeSchema,
|
||||||
type RoofSegmentNode,
|
type RoofSegmentNode,
|
||||||
@@ -15,11 +13,9 @@ import { useViewer } from '@pascal-app/viewer'
|
|||||||
import { Copy, Move, Plus, Trash2 } from 'lucide-react'
|
import { Copy, Move, Plus, Trash2 } from 'lucide-react'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { useShallow } from 'zustand/react/shallow'
|
import { useShallow } from 'zustand/react/shallow'
|
||||||
import { buildRoofSurfaceMaterialPatch } from '../../../lib/material-paint'
|
|
||||||
import { sfxEmitter } from '../../../lib/sfx-bus'
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
||||||
import useEditor from '../../../store/use-editor'
|
import useEditor from '../../../store/use-editor'
|
||||||
import { ActionButton, ActionGroup } from '../controls/action-button'
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
||||||
import { MaterialPicker } from '../controls/material-picker'
|
|
||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SliderControl } from '../controls/slider-control'
|
import { SliderControl } from '../controls/slider-control'
|
||||||
import { PanelWrapper } from './panel-wrapper'
|
import { PanelWrapper } from './panel-wrapper'
|
||||||
@@ -30,9 +26,6 @@ export function RoofPanel() {
|
|||||||
const updateNode = useScene((s) => s.updateNode)
|
const updateNode = useScene((s) => s.updateNode)
|
||||||
const createNode = useScene((s) => s.createNode)
|
const createNode = useScene((s) => s.createNode)
|
||||||
const setMovingNode = useEditor((s) => s.setMovingNode)
|
const setMovingNode = useEditor((s) => s.setMovingNode)
|
||||||
const selectedMaterialTarget = useEditor((s) => s.selectedMaterialTarget)
|
|
||||||
const setActivePaintMaterial = useEditor((s) => s.setActivePaintMaterial)
|
|
||||||
const setActivePaintTarget = useEditor((s) => s.setActivePaintTarget)
|
|
||||||
|
|
||||||
const node = useScene((s) =>
|
const node = useScene((s) =>
|
||||||
selectedId ? (s.nodes[selectedId as AnyNode['id']] as RoofNode | undefined) : undefined,
|
selectedId ? (s.nodes[selectedId as AnyNode['id']] as RoofNode | undefined) : undefined,
|
||||||
@@ -55,37 +48,6 @@ export function RoofPanel() {
|
|||||||
[selectedId, updateNode],
|
[selectedId, updateNode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const materialTargetRole =
|
|
||||||
selectedMaterialTarget &&
|
|
||||||
selectedMaterialTarget.nodeId === node?.id &&
|
|
||||||
(selectedMaterialTarget.role === 'top' ||
|
|
||||||
selectedMaterialTarget.role === 'edge' ||
|
|
||||||
selectedMaterialTarget.role === 'wall')
|
|
||||||
? selectedMaterialTarget.role
|
|
||||||
: null
|
|
||||||
const materialPickerValue =
|
|
||||||
node && materialTargetRole ? getEffectiveRoofSurfaceMaterial(node, materialTargetRole) : {}
|
|
||||||
|
|
||||||
const handleTargetedMaterialChange = useCallback(
|
|
||||||
(material: MaterialSchema) => {
|
|
||||||
if (!node || !materialTargetRole) return
|
|
||||||
setActivePaintTarget('roof')
|
|
||||||
setActivePaintMaterial({ material, sourceTarget: 'roof' })
|
|
||||||
handleUpdate(buildRoofSurfaceMaterialPatch(node, materialTargetRole, material, undefined))
|
|
||||||
},
|
|
||||||
[handleUpdate, materialTargetRole, node, setActivePaintMaterial, setActivePaintTarget],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleTargetedMaterialPresetChange = useCallback(
|
|
||||||
(materialPreset: string) => {
|
|
||||||
if (!node || !materialTargetRole) return
|
|
||||||
setActivePaintTarget('roof')
|
|
||||||
setActivePaintMaterial({ materialPreset, sourceTarget: 'roof' })
|
|
||||||
handleUpdate(buildRoofSurfaceMaterialPatch(node, materialTargetRole, undefined, materialPreset))
|
|
||||||
},
|
|
||||||
[handleUpdate, materialTargetRole, node, setActivePaintMaterial, setActivePaintTarget],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setSelection({ selectedIds: [] })
|
setSelection({ selectedIds: [] })
|
||||||
}, [setSelection])
|
}, [setSelection])
|
||||||
@@ -290,22 +252,6 @@ export function RoofPanel() {
|
|||||||
/>
|
/>
|
||||||
</ActionGroup>
|
</ActionGroup>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
<PanelSection title="Material">
|
|
||||||
{!materialTargetRole ? (
|
|
||||||
<div className="mb-3 rounded-lg border border-border/50 bg-[#2C2C2E] px-3 py-2 text-[11px] text-muted-foreground">
|
|
||||||
Click the roof surface you want to edit. Materials apply to one target at a time.
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<MaterialPicker
|
|
||||||
disabled={!materialTargetRole}
|
|
||||||
hideSideControl
|
|
||||||
nodeType="roof"
|
|
||||||
onChange={handleTargetedMaterialChange}
|
|
||||||
onSelectMaterialPreset={handleTargetedMaterialPresetChange}
|
|
||||||
selectedMaterialPreset={materialPickerValue.materialPreset}
|
|
||||||
value={materialPickerValue.material}
|
|
||||||
/>
|
|
||||||
</PanelSection>
|
|
||||||
</PanelWrapper>
|
</PanelWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
import {
|
import {
|
||||||
type AnyNode,
|
type AnyNode,
|
||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
type MaterialSchema,
|
|
||||||
type RoofSegmentNode,
|
type RoofSegmentNode,
|
||||||
RoofSegmentNode as RoofSegmentNodeSchema,
|
RoofSegmentNode as RoofSegmentNodeSchema,
|
||||||
type RoofType,
|
type RoofType,
|
||||||
@@ -15,7 +14,6 @@ import { useCallback } from 'react'
|
|||||||
import { sfxEmitter } from '../../../lib/sfx-bus'
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
||||||
import useEditor from '../../../store/use-editor'
|
import useEditor from '../../../store/use-editor'
|
||||||
import { ActionButton, ActionGroup } from '../controls/action-button'
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
||||||
import { MaterialPicker } from '../controls/material-picker'
|
|
||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SegmentedControl } from '../controls/segmented-control'
|
import { SegmentedControl } from '../controls/segmented-control'
|
||||||
import { SliderControl } from '../controls/slider-control'
|
import { SliderControl } from '../controls/slider-control'
|
||||||
@@ -52,20 +50,6 @@ export function RoofSegmentPanel() {
|
|||||||
[selectedId, updateNode],
|
[selectedId, updateNode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleMaterialChange = useCallback(
|
|
||||||
(material: MaterialSchema) => {
|
|
||||||
handleUpdate({ material, materialPreset: undefined })
|
|
||||||
},
|
|
||||||
[handleUpdate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleMaterialPresetChange = useCallback(
|
|
||||||
(materialPreset: string) => {
|
|
||||||
handleUpdate({ materialPreset, material: undefined })
|
|
||||||
},
|
|
||||||
[handleUpdate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setSelection({ selectedIds: [] })
|
setSelection({ selectedIds: [] })
|
||||||
}, [setSelection])
|
}, [setSelection])
|
||||||
@@ -322,15 +306,6 @@ export function RoofSegmentPanel() {
|
|||||||
/>
|
/>
|
||||||
</ActionGroup>
|
</ActionGroup>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
<PanelSection title="Material">
|
|
||||||
<MaterialPicker
|
|
||||||
nodeType="roof-segment"
|
|
||||||
onChange={handleMaterialChange}
|
|
||||||
onSelectMaterialPreset={handleMaterialPresetChange}
|
|
||||||
selectedMaterialPreset={node.materialPreset}
|
|
||||||
value={node.material}
|
|
||||||
/>
|
|
||||||
</PanelSection>
|
|
||||||
</PanelWrapper>
|
</PanelWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { type AnyNode, type MaterialSchema, type SlabNode, useScene } from '@pascal-app/core'
|
import { type AnyNode, type SlabNode, useScene } from '@pascal-app/core'
|
||||||
import { useViewer } from '@pascal-app/viewer'
|
import { useViewer } from '@pascal-app/viewer'
|
||||||
import { Edit, Move, Plus, Trash2 } from 'lucide-react'
|
import { Edit, Move, Plus, Trash2 } from 'lucide-react'
|
||||||
import { useCallback, useEffect } from 'react'
|
import { useCallback, useEffect } from 'react'
|
||||||
import { sfxEmitter } from '../../../lib/sfx-bus'
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
||||||
import useEditor from '../../../store/use-editor'
|
import useEditor from '../../../store/use-editor'
|
||||||
import { ActionButton, ActionGroup } from '../controls/action-button'
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
||||||
import { MaterialPicker } from '../controls/material-picker'
|
|
||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SliderControl } from '../controls/slider-control'
|
import { SliderControl } from '../controls/slider-control'
|
||||||
import { PanelWrapper } from './panel-wrapper'
|
import { PanelWrapper } from './panel-wrapper'
|
||||||
@@ -32,20 +31,6 @@ export function SlabPanel() {
|
|||||||
[selectedId, updateNode],
|
[selectedId, updateNode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleMaterialPresetChange = useCallback(
|
|
||||||
(materialPreset: string) => {
|
|
||||||
handleUpdate({ materialPreset, material: undefined })
|
|
||||||
},
|
|
||||||
[handleUpdate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleCustomMaterialChange = useCallback(
|
|
||||||
(material: MaterialSchema) => {
|
|
||||||
handleUpdate({ material, materialPreset: undefined })
|
|
||||||
},
|
|
||||||
[handleUpdate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setSelection({ selectedIds: [] })
|
setSelection({ selectedIds: [] })
|
||||||
setEditingHole(null)
|
setEditingHole(null)
|
||||||
@@ -257,15 +242,6 @@ export function SlabPanel() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
<PanelSection title="Material">
|
|
||||||
<MaterialPicker
|
|
||||||
nodeType="slab"
|
|
||||||
onChange={handleCustomMaterialChange}
|
|
||||||
onSelectMaterialPreset={handleMaterialPresetChange}
|
|
||||||
selectedMaterialPreset={node.materialPreset}
|
|
||||||
value={node.material}
|
|
||||||
/>
|
|
||||||
</PanelSection>
|
|
||||||
<ActionGroup>
|
<ActionGroup>
|
||||||
<ActionButton icon={<Move className="h-3.5 w-3.5" />} label="Move" onClick={handleMove} />
|
<ActionButton icon={<Move className="h-3.5 w-3.5" />} label="Move" onClick={handleMove} />
|
||||||
</ActionGroup>
|
</ActionGroup>
|
||||||
|
|||||||
@@ -3,9 +3,7 @@
|
|||||||
import {
|
import {
|
||||||
type AnyNode,
|
type AnyNode,
|
||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
getEffectiveStairSurfaceMaterial,
|
|
||||||
type LevelNode,
|
type LevelNode,
|
||||||
type MaterialSchema,
|
|
||||||
type StairNode,
|
type StairNode,
|
||||||
type StairRailingMode,
|
type StairRailingMode,
|
||||||
type StairSlabOpeningMode,
|
type StairSlabOpeningMode,
|
||||||
@@ -20,12 +18,10 @@ import { useViewer } from '@pascal-app/viewer'
|
|||||||
import { Copy, Move, Plus, Trash2 } from 'lucide-react'
|
import { Copy, Move, Plus, Trash2 } from 'lucide-react'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { useShallow } from 'zustand/react/shallow'
|
import { useShallow } from 'zustand/react/shallow'
|
||||||
import { buildStairSurfaceMaterialPatch } from '../../../lib/material-paint'
|
|
||||||
import { sfxEmitter } from '../../../lib/sfx-bus'
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
||||||
import useEditor from '../../../store/use-editor'
|
import useEditor from '../../../store/use-editor'
|
||||||
import { DEFAULT_SPIRAL_STAIR_SWEEP_ANGLE } from '../../tools/stair/stair-defaults'
|
import { DEFAULT_SPIRAL_STAIR_SWEEP_ANGLE } from '../../tools/stair/stair-defaults'
|
||||||
import { ActionButton, ActionGroup } from '../controls/action-button'
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
||||||
import { MaterialPicker } from '../controls/material-picker'
|
|
||||||
import { MetricControl } from '../controls/metric-control'
|
import { MetricControl } from '../controls/metric-control'
|
||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SegmentedControl } from '../controls/segmented-control'
|
import { SegmentedControl } from '../controls/segmented-control'
|
||||||
@@ -64,9 +60,6 @@ export function StairPanel() {
|
|||||||
const createNode = useScene((s) => s.createNode)
|
const createNode = useScene((s) => s.createNode)
|
||||||
const createNodes = useScene((s) => s.createNodes)
|
const createNodes = useScene((s) => s.createNodes)
|
||||||
const setMovingNode = useEditor((s) => s.setMovingNode)
|
const setMovingNode = useEditor((s) => s.setMovingNode)
|
||||||
const selectedMaterialTarget = useEditor((s) => s.selectedMaterialTarget)
|
|
||||||
const setActivePaintMaterial = useEditor((s) => s.setActivePaintMaterial)
|
|
||||||
const setActivePaintTarget = useEditor((s) => s.setActivePaintTarget)
|
|
||||||
|
|
||||||
const node = useScene((s) =>
|
const node = useScene((s) =>
|
||||||
selectedId ? (s.nodes[selectedId as AnyNode['id']] as StairNode | undefined) : undefined,
|
selectedId ? (s.nodes[selectedId as AnyNode['id']] as StairNode | undefined) : undefined,
|
||||||
@@ -97,37 +90,6 @@ export function StairPanel() {
|
|||||||
[selectedId, updateNode],
|
[selectedId, updateNode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const materialTargetRole =
|
|
||||||
selectedMaterialTarget &&
|
|
||||||
selectedMaterialTarget.nodeId === node?.id &&
|
|
||||||
(selectedMaterialTarget.role === 'railing' ||
|
|
||||||
selectedMaterialTarget.role === 'tread' ||
|
|
||||||
selectedMaterialTarget.role === 'side')
|
|
||||||
? selectedMaterialTarget.role
|
|
||||||
: null
|
|
||||||
const materialPickerValue =
|
|
||||||
node && materialTargetRole ? getEffectiveStairSurfaceMaterial(node, materialTargetRole) : {}
|
|
||||||
|
|
||||||
const handleTargetedMaterialChange = useCallback(
|
|
||||||
(material: MaterialSchema) => {
|
|
||||||
if (!node || !materialTargetRole) return
|
|
||||||
setActivePaintTarget('stair')
|
|
||||||
setActivePaintMaterial({ material, sourceTarget: 'stair' })
|
|
||||||
handleUpdate(buildStairSurfaceMaterialPatch(node, materialTargetRole, material, undefined))
|
|
||||||
},
|
|
||||||
[handleUpdate, materialTargetRole, node, setActivePaintMaterial, setActivePaintTarget],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleTargetedMaterialPresetChange = useCallback(
|
|
||||||
(materialPreset: string) => {
|
|
||||||
if (!node || !materialTargetRole) return
|
|
||||||
setActivePaintTarget('stair')
|
|
||||||
setActivePaintMaterial({ materialPreset, sourceTarget: 'stair' })
|
|
||||||
handleUpdate(buildStairSurfaceMaterialPatch(node, materialTargetRole, undefined, materialPreset))
|
|
||||||
},
|
|
||||||
[handleUpdate, materialTargetRole, node, setActivePaintMaterial, setActivePaintTarget],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setSelection({ selectedIds: [] })
|
setSelection({ selectedIds: [] })
|
||||||
}, [setSelection])
|
}, [setSelection])
|
||||||
@@ -590,22 +552,6 @@ export function StairPanel() {
|
|||||||
/>
|
/>
|
||||||
</ActionGroup>
|
</ActionGroup>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
<PanelSection title="Material">
|
|
||||||
{!materialTargetRole ? (
|
|
||||||
<div className="mb-3 rounded-lg border border-border/50 bg-[#2C2C2E] px-3 py-2 text-[11px] text-muted-foreground">
|
|
||||||
Click the stair surface you want to edit. Materials apply to one target at a time.
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<MaterialPicker
|
|
||||||
disabled={!materialTargetRole}
|
|
||||||
hideSideControl
|
|
||||||
nodeType="stair"
|
|
||||||
onChange={handleTargetedMaterialChange}
|
|
||||||
onSelectMaterialPreset={handleTargetedMaterialPresetChange}
|
|
||||||
selectedMaterialPreset={materialPickerValue.materialPreset}
|
|
||||||
value={materialPickerValue.material}
|
|
||||||
/>
|
|
||||||
</PanelSection>
|
|
||||||
</PanelWrapper>
|
</PanelWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
type AnyNode,
|
type AnyNode,
|
||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
type AttachmentSide,
|
type AttachmentSide,
|
||||||
type MaterialSchema,
|
|
||||||
type StairSegmentNode,
|
type StairSegmentNode,
|
||||||
StairSegmentNode as StairSegmentNodeSchema,
|
StairSegmentNode as StairSegmentNodeSchema,
|
||||||
type StairSegmentType,
|
type StairSegmentType,
|
||||||
@@ -16,7 +15,6 @@ import { useCallback } from 'react'
|
|||||||
import { sfxEmitter } from '../../../lib/sfx-bus'
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
||||||
import useEditor from '../../../store/use-editor'
|
import useEditor from '../../../store/use-editor'
|
||||||
import { ActionButton, ActionGroup } from '../controls/action-button'
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
||||||
import { MaterialPicker } from '../controls/material-picker'
|
|
||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SegmentedControl } from '../controls/segmented-control'
|
import { SegmentedControl } from '../controls/segmented-control'
|
||||||
import { SliderControl } from '../controls/slider-control'
|
import { SliderControl } from '../controls/slider-control'
|
||||||
@@ -61,20 +59,6 @@ export function StairSegmentPanel() {
|
|||||||
[selectedId, updateNode],
|
[selectedId, updateNode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleMaterialChange = useCallback(
|
|
||||||
(material: MaterialSchema) => {
|
|
||||||
handleUpdate({ material, materialPreset: undefined })
|
|
||||||
},
|
|
||||||
[handleUpdate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleMaterialPresetChange = useCallback(
|
|
||||||
(materialPreset: string) => {
|
|
||||||
handleUpdate({ materialPreset, material: undefined })
|
|
||||||
},
|
|
||||||
[handleUpdate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setSelection({ selectedIds: [] })
|
setSelection({ selectedIds: [] })
|
||||||
}, [setSelection])
|
}, [setSelection])
|
||||||
@@ -336,15 +320,6 @@ export function StairSegmentPanel() {
|
|||||||
/>
|
/>
|
||||||
</ActionGroup>
|
</ActionGroup>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
<PanelSection title="Material">
|
|
||||||
<MaterialPicker
|
|
||||||
nodeType="stair-segment"
|
|
||||||
onChange={handleMaterialChange}
|
|
||||||
onSelectMaterialPreset={handleMaterialPresetChange}
|
|
||||||
selectedMaterialPreset={node.materialPreset}
|
|
||||||
value={node.material}
|
|
||||||
/>
|
|
||||||
</PanelSection>
|
|
||||||
</PanelWrapper>
|
</PanelWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,19 @@
|
|||||||
import {
|
import {
|
||||||
type AnyNode,
|
type AnyNode,
|
||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
getEffectiveWallSurfaceMaterial,
|
|
||||||
getClampedWallCurveOffset,
|
getClampedWallCurveOffset,
|
||||||
getMaxWallCurveOffset,
|
getMaxWallCurveOffset,
|
||||||
getWallCurveLength,
|
getWallCurveLength,
|
||||||
getWallSurfaceMaterialSignature,
|
|
||||||
normalizeWallCurveOffset,
|
normalizeWallCurveOffset,
|
||||||
type MaterialSchema,
|
|
||||||
useScene,
|
useScene,
|
||||||
type WallNode,
|
type WallNode,
|
||||||
} from '@pascal-app/core'
|
} from '@pascal-app/core'
|
||||||
import { useViewer } from '@pascal-app/viewer'
|
import { useViewer } from '@pascal-app/viewer'
|
||||||
import { Move, Spline } from 'lucide-react'
|
import { Move, Spline } from 'lucide-react'
|
||||||
import { useCallback, useMemo } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { sfxEmitter } from '../../../lib/sfx-bus'
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
||||||
import { buildWallSurfaceMaterialPatch } from '../../../lib/material-paint'
|
|
||||||
import useEditor from '../../../store/use-editor'
|
import useEditor from '../../../store/use-editor'
|
||||||
import { ActionButton, ActionGroup } from '../controls/action-button'
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
||||||
import { MaterialPicker } from '../controls/material-picker'
|
|
||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SliderControl } from '../controls/slider-control'
|
import { SliderControl } from '../controls/slider-control'
|
||||||
import { PanelWrapper } from './panel-wrapper'
|
import { PanelWrapper } from './panel-wrapper'
|
||||||
@@ -31,9 +26,6 @@ export function WallPanel() {
|
|||||||
const updateNode = useScene((s) => s.updateNode)
|
const updateNode = useScene((s) => s.updateNode)
|
||||||
const setMovingNode = useEditor((s) => s.setMovingNode)
|
const setMovingNode = useEditor((s) => s.setMovingNode)
|
||||||
const setCurvingWall = useEditor((s) => s.setCurvingWall)
|
const setCurvingWall = useEditor((s) => s.setCurvingWall)
|
||||||
const selectedMaterialTarget = useEditor((s) => s.selectedMaterialTarget)
|
|
||||||
const setActivePaintMaterial = useEditor((s) => s.setActivePaintMaterial)
|
|
||||||
const setActivePaintTarget = useEditor((s) => s.setActivePaintTarget)
|
|
||||||
|
|
||||||
const node = useScene((s) =>
|
const node = useScene((s) =>
|
||||||
selectedId ? (s.nodes[selectedId as AnyNode['id']] as WallNode | undefined) : undefined,
|
selectedId ? (s.nodes[selectedId as AnyNode['id']] as WallNode | undefined) : undefined,
|
||||||
@@ -64,35 +56,6 @@ export function WallPanel() {
|
|||||||
[selectedId, updateNode],
|
[selectedId, updateNode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const effectiveInteriorMaterial = useMemo(
|
|
||||||
() => (node ? getEffectiveWallSurfaceMaterial(node, 'interior') : {}),
|
|
||||||
[node],
|
|
||||||
)
|
|
||||||
const effectiveExteriorMaterial = useMemo(
|
|
||||||
() => (node ? getEffectiveWallSurfaceMaterial(node, 'exterior') : {}),
|
|
||||||
[node],
|
|
||||||
)
|
|
||||||
const surfaceMaterialsMatch = useMemo(
|
|
||||||
() =>
|
|
||||||
getWallSurfaceMaterialSignature(effectiveInteriorMaterial) ===
|
|
||||||
getWallSurfaceMaterialSignature(effectiveExteriorMaterial),
|
|
||||||
[effectiveExteriorMaterial, effectiveInteriorMaterial],
|
|
||||||
)
|
|
||||||
const materialTargetSide =
|
|
||||||
selectedMaterialTarget &&
|
|
||||||
selectedMaterialTarget.nodeId === node?.id &&
|
|
||||||
(selectedMaterialTarget.role === 'interior' || selectedMaterialTarget.role === 'exterior')
|
|
||||||
? selectedMaterialTarget.role
|
|
||||||
: null
|
|
||||||
const materialPickerValue =
|
|
||||||
materialTargetSide === 'interior'
|
|
||||||
? effectiveInteriorMaterial
|
|
||||||
: materialTargetSide === 'exterior'
|
|
||||||
? effectiveExteriorMaterial
|
|
||||||
: surfaceMaterialsMatch
|
|
||||||
? effectiveInteriorMaterial
|
|
||||||
: {}
|
|
||||||
|
|
||||||
const handleUpdateLength = useCallback(
|
const handleUpdateLength = useCallback(
|
||||||
(newLength: number) => {
|
(newLength: number) => {
|
||||||
if (!node || newLength <= 0) return
|
if (!node || newLength <= 0) return
|
||||||
@@ -116,26 +79,6 @@ export function WallPanel() {
|
|||||||
[node, handleUpdate],
|
[node, handleUpdate],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleMaterialPresetChange = useCallback(
|
|
||||||
(materialPreset: string) => {
|
|
||||||
if (!node || !materialTargetSide) return
|
|
||||||
setActivePaintTarget('wall')
|
|
||||||
setActivePaintMaterial({ materialPreset, sourceTarget: 'wall' })
|
|
||||||
handleUpdate(buildWallSurfaceMaterialPatch(node, materialTargetSide, undefined, materialPreset))
|
|
||||||
},
|
|
||||||
[handleUpdate, materialTargetSide, node, setActivePaintMaterial, setActivePaintTarget],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleCustomMaterialChange = useCallback(
|
|
||||||
(material: MaterialSchema) => {
|
|
||||||
if (!node || !materialTargetSide) return
|
|
||||||
setActivePaintTarget('wall')
|
|
||||||
setActivePaintMaterial({ material, sourceTarget: 'wall' })
|
|
||||||
handleUpdate(buildWallSurfaceMaterialPatch(node, materialTargetSide, material, undefined))
|
|
||||||
},
|
|
||||||
[handleUpdate, materialTargetSide, node, setActivePaintMaterial, setActivePaintTarget],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setSelection({ selectedIds: [] })
|
setSelection({ selectedIds: [] })
|
||||||
}, [setSelection])
|
}, [setSelection])
|
||||||
@@ -217,23 +160,6 @@ export function WallPanel() {
|
|||||||
)}
|
)}
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
|
|
||||||
<PanelSection title="Material">
|
|
||||||
{!materialTargetSide ? (
|
|
||||||
<div className="mb-3 rounded-lg border border-border/50 bg-[#2C2C2E] px-3 py-2 text-[11px] text-muted-foreground">
|
|
||||||
Click the wall face you want to edit. Materials now apply to one side at a time.
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<MaterialPicker
|
|
||||||
disabled={!materialTargetSide}
|
|
||||||
hideSideControl
|
|
||||||
nodeType="wall"
|
|
||||||
onChange={handleCustomMaterialChange}
|
|
||||||
onSelectMaterialPreset={handleMaterialPresetChange}
|
|
||||||
selectedMaterialPreset={materialPickerValue.materialPreset}
|
|
||||||
value={materialPickerValue.material}
|
|
||||||
/>
|
|
||||||
</PanelSection>
|
|
||||||
|
|
||||||
<PanelSection title="Actions">
|
<PanelSection title="Actions">
|
||||||
<ActionGroup>
|
<ActionGroup>
|
||||||
<ActionButton icon={<Move className="h-3.5 w-3.5" />} label="Move" onClick={handleMove} />
|
<ActionButton icon={<Move className="h-3.5 w-3.5" />} label="Move" onClick={handleMove} />
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
type AnyNode,
|
type AnyNode,
|
||||||
type AnyNodeId,
|
type AnyNodeId,
|
||||||
emitter,
|
emitter,
|
||||||
type MaterialSchema,
|
|
||||||
useScene,
|
useScene,
|
||||||
WindowNode,
|
WindowNode,
|
||||||
} from '@pascal-app/core'
|
} from '@pascal-app/core'
|
||||||
@@ -15,7 +14,6 @@ import { usePresetsAdapter } from '../../../contexts/presets-context'
|
|||||||
import { sfxEmitter } from '../../../lib/sfx-bus'
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
||||||
import useEditor from '../../../store/use-editor'
|
import useEditor from '../../../store/use-editor'
|
||||||
import { ActionButton, ActionGroup } from '../controls/action-button'
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
||||||
import { MaterialPicker } from '../controls/material-picker'
|
|
||||||
import { MetricControl } from '../controls/metric-control'
|
import { MetricControl } from '../controls/metric-control'
|
||||||
import { PanelSection } from '../controls/panel-section'
|
import { PanelSection } from '../controls/panel-section'
|
||||||
import { SliderControl } from '../controls/slider-control'
|
import { SliderControl } from '../controls/slider-control'
|
||||||
@@ -45,13 +43,6 @@ export function WindowPanel() {
|
|||||||
[selectedId, updateNode],
|
[selectedId, updateNode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleMaterialChange = useCallback(
|
|
||||||
(material: MaterialSchema) => {
|
|
||||||
handleUpdate({ material })
|
|
||||||
},
|
|
||||||
[handleUpdate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setSelection({ selectedIds: [] })
|
setSelection({ selectedIds: [] })
|
||||||
}, [setSelection])
|
}, [setSelection])
|
||||||
@@ -431,9 +422,6 @@ export function WindowPanel() {
|
|||||||
/>
|
/>
|
||||||
</ActionGroup>
|
</ActionGroup>
|
||||||
</PanelSection>
|
</PanelSection>
|
||||||
<PanelSection title="Material">
|
|
||||||
<MaterialPicker onChange={handleMaterialChange} value={node.material} />
|
|
||||||
</PanelSection>
|
|
||||||
</PanelWrapper>
|
</PanelWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user