Enable material paint for fence slab and ceiling nodes

This commit is contained in:
sudhir
2026-04-21 15:19:46 +05:30
parent b925cf1e7a
commit 65399731f7
3 changed files with 97 additions and 3 deletions
@@ -2,13 +2,16 @@ import {
type AnyNode,
type AnyNodeId,
type BuildingNode,
type CeilingNode,
emitter,
type FenceNode,
type ItemNode,
type NodeEvent,
type RoofEvent,
type RoofNode,
type RoofSegmentEvent,
resolveLevelId,
type SlabNode,
type StairEvent,
type StairNode,
type StairSegmentEvent,
@@ -25,6 +28,7 @@ import { useCallback, useEffect, useRef } from 'react'
import { type BufferGeometry, Color, type Material, type Mesh, type Object3D } from 'three'
import {
buildRoofSurfaceMaterialPatch,
buildSingleSurfaceMaterialPatch,
buildStairSurfaceMaterialPatch,
buildWallSurfaceMaterialPatch,
hasActivePaintMaterial,
@@ -609,7 +613,30 @@ export const SelectionManager = () => {
}
}
const disabledNodeTypes = ['fence', 'item', 'slab', 'ceiling', 'window', 'door', 'zone']
if (node.type === 'fence' || node.type === 'slab' || node.type === 'ceiling') {
const target = node.type
const compatible =
isActivePaintMaterialCompatible(activePaintMaterial, target) &&
hasActivePaintMaterial(activePaintMaterial)
return {
apply: compatible
? () => {
useScene
.getState()
.updateNode(
node.id as AnyNodeId,
buildSingleSurfaceMaterialPatch<FenceNode | SlabNode | CeilingNode>(
activePaintMaterial.material,
activePaintMaterial.materialPreset,
),
)
}
: null,
}
}
const disabledNodeTypes = ['item', 'window', 'door', 'zone']
if (disabledNodeTypes.includes(node.type)) {
return {
apply: null,
@@ -776,6 +803,14 @@ export const SelectionManager = () => {
nextMaterialTargetHandled = true
}
if (
(node.type === 'fence' || node.type === 'slab' || node.type === 'ceiling') &&
nodeToSelect.type === node.type
) {
setSelectedMaterialTargetForNode(nodeToSelect, 'surface')
nextMaterialTargetHandled = true
}
if (!nextMaterialTargetHandled && useEditor.getState().selectedMaterialTarget) {
useEditor.getState().setSelectedMaterialTarget(null)
}
@@ -1092,6 +1127,9 @@ const SelectionStateSync = () => {
if (
!selectedNode ||
(selectedNode.type !== 'wall' &&
selectedNode.type !== 'fence' &&
selectedNode.type !== 'slab' &&
selectedNode.type !== 'ceiling' &&
selectedNode.type !== 'stair' &&
selectedNode.type !== 'roof')
) {
+56 -2
View File
@@ -1,6 +1,8 @@
'use client'
import {
type CeilingNode,
type FenceNode,
getCatalogMaterialById,
getEffectiveRoofSurfaceMaterial,
getEffectiveStairSurfaceMaterial,
@@ -10,13 +12,19 @@ import {
type MaterialTarget,
type RoofNode,
type RoofSurfaceMaterialRole,
type SlabNode,
type StairNode,
type StairSurfaceMaterialRole,
type WallNode,
type WallSurfaceSide,
} from '@pascal-app/core'
export type PaintableMaterialTarget = Extract<MaterialTarget, 'wall' | 'roof' | 'stair'>
export type PaintableMaterialTarget = Extract<
MaterialTarget,
'wall' | 'roof' | 'stair' | 'fence' | 'slab' | 'ceiling'
>
export type SingleSurfaceMaterialRole = 'surface'
export type ActivePaintMaterial = {
material?: MaterialSchema
@@ -135,12 +143,26 @@ export function buildStairSurfaceMaterialPatch(
}
}
export function buildSingleSurfaceMaterialPatch<TNode extends FenceNode | SlabNode | CeilingNode>(
material: MaterialSchema | undefined,
materialPreset: string | undefined,
): Partial<TNode> {
return {
material,
materialPreset,
} as Partial<TNode>
}
export function resolveActivePaintMaterialFromSelection(params: {
nodes: Record<string, any>
selectedId: string | null
selectedMaterialTarget: {
nodeId: string
role: WallSurfaceSide | StairSurfaceMaterialRole | RoofSurfaceMaterialRole
role:
| WallSurfaceSide
| StairSurfaceMaterialRole
| RoofSurfaceMaterialRole
| SingleSurfaceMaterialRole
} | null
}): ActivePaintMaterial | null {
const { nodes, selectedId, selectedMaterialTarget } = params
@@ -208,6 +230,26 @@ export function resolveActivePaintMaterialFromSelection(params: {
: null
}
if (
(selectedNode.type === 'fence' ||
selectedNode.type === 'slab' ||
selectedNode.type === 'ceiling') &&
selectedMaterialTarget.role === 'surface'
) {
const target = selectedNode.type
return hasActivePaintMaterial({
material: selectedNode.material,
materialPreset: selectedNode.materialPreset,
sourceTarget: target,
})
? {
material: selectedNode.material,
materialPreset: selectedNode.materialPreset,
sourceTarget: target,
}
: null
}
return null
}
@@ -233,5 +275,17 @@ export function resolvePaintTargetFromSelection(params: {
return 'stair'
}
if (selectedNode.type === 'fence') {
return 'fence'
}
if (selectedNode.type === 'slab') {
return 'slab'
}
if (selectedNode.type === 'ceiling') {
return 'ceiling'
}
return null
}
+2
View File
@@ -31,6 +31,7 @@ import {
type PaintableMaterialTarget,
resolveActivePaintMaterialFromSelection,
resolvePaintTargetFromSelection,
type SingleSurfaceMaterialRole,
} from '../lib/material-paint'
const DEFAULT_ACTIVE_SIDEBAR_PANEL = 'site'
@@ -94,6 +95,7 @@ export type MaterialTargetRole =
| WallSurfaceSide
| StairSurfaceMaterialRole
| RoofSurfaceMaterialRole
| SingleSurfaceMaterialRole
export type SelectedMaterialTarget = {
nodeId: AnyNodeId