Simplify paint material compatibility logic

This commit is contained in:
sudhir
2026-04-25 00:15:29 +05:30
parent cb268509ea
commit 275a42cc7f
7 changed files with 25 additions and 112 deletions
-1
View File
@@ -40,7 +40,6 @@ export {
export { baseMaterial, glassMaterial } from './materials' export { baseMaterial, glassMaterial } from './materials'
export { export {
getCatalogMaterialById, getCatalogMaterialById,
getMaterialCategoryTargets,
getLibraryMaterialIdFromRef, getLibraryMaterialIdFromRef,
getMaterialPresetByRef, getMaterialPresetByRef,
getMaterialsForCategory, getMaterialsForCategory,
-40
View File
@@ -1,7 +1,5 @@
import { import {
type MaterialPresetPayload, type MaterialPresetPayload,
type MaterialTarget,
MaterialTarget as MaterialTargetSchema,
} from './schema/material' } from './schema/material'
export type MaterialCatalogItem = { export type MaterialCatalogItem = {
@@ -24,40 +22,6 @@ export const MATERIAL_CATEGORIES = [
] as const ] as const
export type MaterialCategory = (typeof MATERIAL_CATEGORIES)[number] export type MaterialCategory = (typeof MATERIAL_CATEGORIES)[number]
export const MATERIAL_CATEGORY_TARGETS: Record<MaterialCategory, MaterialTarget[]> = {
wood: [
MaterialTargetSchema.enum.wall,
MaterialTargetSchema.enum.slab,
MaterialTargetSchema.enum.stair,
MaterialTargetSchema.enum['stair-segment'],
MaterialTargetSchema.enum.fence,
MaterialTargetSchema.enum.roof,
MaterialTargetSchema.enum['roof-segment'],
],
wallpaper: [MaterialTargetSchema.enum.wall],
parquet: [
MaterialTargetSchema.enum.slab,
MaterialTargetSchema.enum.stair,
MaterialTargetSchema.enum['stair-segment'],
],
granite: [MaterialTargetSchema.enum.slab],
marble: [
MaterialTargetSchema.enum.slab,
MaterialTargetSchema.enum.stair,
MaterialTargetSchema.enum['stair-segment'],
],
other: [
MaterialTargetSchema.enum.wall,
MaterialTargetSchema.enum.roof,
MaterialTargetSchema.enum['roof-segment'],
MaterialTargetSchema.enum.stair,
MaterialTargetSchema.enum['stair-segment'],
MaterialTargetSchema.enum.fence,
MaterialTargetSchema.enum.slab,
MaterialTargetSchema.enum.ceiling,
],
}
export const MATERIAL_CATALOG: MaterialCatalogItem[] = [ export const MATERIAL_CATALOG: MaterialCatalogItem[] = [
{ {
id: 'wall-wood1', id: 'wall-wood1',
@@ -609,10 +573,6 @@ export function getMaterialsForCategory(category: MaterialCategory): MaterialCat
return MATERIAL_CATALOG.filter((item) => item.category === category) return MATERIAL_CATALOG.filter((item) => item.category === category)
} }
export function getMaterialCategoryTargets(category: MaterialCategory): MaterialTarget[] {
return MATERIAL_CATEGORY_TARGETS[category]
}
export function getCatalogMaterialById(id?: string): MaterialCatalogItem | undefined { export function getCatalogMaterialById(id?: string): MaterialCatalogItem | undefined {
if (!id) return undefined if (!id) return undefined
return MATERIAL_CATALOG.find((item) => item.id === id) return MATERIAL_CATALOG.find((item) => item.id === id)
@@ -44,7 +44,6 @@ import {
buildStairSurfaceMaterialPatch, buildStairSurfaceMaterialPatch,
buildWallSurfaceMaterialPatch, buildWallSurfaceMaterialPatch,
hasActivePaintMaterial, hasActivePaintMaterial,
isActivePaintMaterialCompatible,
resolveActivePaintMaterialFromSelection, resolveActivePaintMaterialFromSelection,
} from '../../lib/material-paint' } from '../../lib/material-paint'
import { sfxEmitter } from '../../lib/sfx-bus' import { sfxEmitter } from '../../lib/sfx-bus'
@@ -741,8 +740,7 @@ export const SelectionManager = () => {
if (node.type === 'wall') { if (node.type === 'wall') {
const role = resolveWallMaterialTarget(event as WallEvent) const role = resolveWallMaterialTarget(event as WallEvent)
const compatible = const compatible = role !== null && hasActivePaintMaterial(activePaintMaterial)
role !== null && isActivePaintMaterialCompatible(activePaintMaterial, 'wall')
return { return {
key: `wall:${node.id}:${role ?? 'unsupported'}`, key: `wall:${node.id}:${role ?? 'unsupported'}`,
hoveredId: node.id as AnyNodeId, hoveredId: node.id as AnyNodeId,
@@ -783,8 +781,7 @@ export const SelectionManager = () => {
if (!roofNode || roofNode.type !== 'roof') return null if (!roofNode || roofNode.type !== 'roof') return null
const role = resolveRoofMaterialTarget(event as RoofEvent | RoofSegmentEvent) const role = resolveRoofMaterialTarget(event as RoofEvent | RoofSegmentEvent)
const compatible = const compatible = role !== null && hasActivePaintMaterial(activePaintMaterial)
role !== null && isActivePaintMaterialCompatible(activePaintMaterial, 'roof')
return { return {
key: `roof:${roofNode.id}:${role ?? 'unsupported'}`, key: `roof:${roofNode.id}:${role ?? 'unsupported'}`,
hoveredId: roofNode.id as AnyNodeId, hoveredId: roofNode.id as AnyNodeId,
@@ -825,8 +822,7 @@ export const SelectionManager = () => {
if (!stairNode || stairNode.type !== 'stair') return null if (!stairNode || stairNode.type !== 'stair') return null
const role = resolveStairMaterialTarget(event as StairEvent | StairSegmentEvent) const role = resolveStairMaterialTarget(event as StairEvent | StairSegmentEvent)
const compatible = const compatible = role !== null && hasActivePaintMaterial(activePaintMaterial)
role !== null && isActivePaintMaterialCompatible(activePaintMaterial, 'stair')
return { return {
key: `stair:${stairNode.id}:${role ?? 'unsupported'}`, key: `stair:${stairNode.id}:${role ?? 'unsupported'}`,
hoveredId: stairNode.id as AnyNodeId, hoveredId: stairNode.id as AnyNodeId,
@@ -858,13 +854,10 @@ export const SelectionManager = () => {
} }
if (node.type === 'fence' || node.type === 'slab' || node.type === 'ceiling') { if (node.type === 'fence' || node.type === 'slab' || node.type === 'ceiling') {
const target = node.type const compatible = hasActivePaintMaterial(activePaintMaterial)
const compatible =
isActivePaintMaterialCompatible(activePaintMaterial, target) &&
hasActivePaintMaterial(activePaintMaterial)
return { return {
key: `${target}:${node.id}:surface`, key: `${node.type}:${node.id}:surface`,
hoveredId: node.id as AnyNodeId, hoveredId: node.id as AnyNodeId,
hoverMode: compatible ? 'paint-ready' : 'paint-disabled', hoverMode: compatible ? 'paint-ready' : 'paint-disabled',
apply: compatible apply: compatible
@@ -40,11 +40,11 @@ function PaintMaterialTray() {
return ( return (
<div className="w-[42rem] max-w-[calc(100vw-2rem)]"> <div className="w-[42rem] max-w-[calc(100vw-2rem)]">
<MaterialPicker <MaterialPicker
onChange={(material, category) => { onChange={(material) => {
setActivePaintMaterial({ material, category, sourceTarget: activePaintTarget }) setActivePaintMaterial({ material, sourceTarget: activePaintTarget })
}} }}
onSelectMaterialPreset={(materialPreset, category) => { onSelectMaterialPreset={(materialPreset) => {
setActivePaintMaterial({ materialPreset, category, sourceTarget: activePaintTarget }) setActivePaintMaterial({ materialPreset, sourceTarget: activePaintTarget })
}} }}
selectedMaterialPreset={activePaintMaterial?.materialPreset} selectedMaterialPreset={activePaintMaterial?.materialPreset}
value={activePaintMaterial?.material} value={activePaintMaterial?.material}
@@ -6,7 +6,6 @@ import {
getMaterialsForCategory, getMaterialsForCategory,
MATERIAL_CATEGORIES, MATERIAL_CATEGORIES,
toLibraryMaterialRef, toLibraryMaterialRef,
type MaterialCategory,
type MaterialSchema, type MaterialSchema,
} from '@pascal-app/core' } from '@pascal-app/core'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
@@ -15,8 +14,8 @@ import useEditor from '../../../store/use-editor'
type MaterialPickerProps = { type MaterialPickerProps = {
value?: MaterialSchema value?: MaterialSchema
selectedMaterialPreset?: string selectedMaterialPreset?: string
onChange?: (material: MaterialSchema, category: MaterialCategory) => void onChange?: (material: MaterialSchema) => void
onSelectMaterialPreset?: (materialPreset: string, category: MaterialCategory) => void onSelectMaterialPreset?: (materialPreset: string) => void
disabled?: boolean disabled?: boolean
} }
@@ -29,7 +28,9 @@ export function MaterialPicker({
}: MaterialPickerProps) { }: MaterialPickerProps) {
const setPaintPanelOpen = useEditor((state) => state.setPaintPanelOpen) const setPaintPanelOpen = useEditor((state) => state.setPaintPanelOpen)
const [showCustom, setShowCustom] = useState<boolean>(!!value?.properties) const [showCustom, setShowCustom] = useState<boolean>(!!value?.properties)
const [selectedCategory, setSelectedCategory] = useState<MaterialCategory>(MATERIAL_CATEGORIES[0]) const [selectedCategory, setSelectedCategory] = useState<(typeof MATERIAL_CATEGORIES)[number]>(
MATERIAL_CATEGORIES[0],
)
const catalogScrollRef = useRef<HTMLDivElement>(null) const catalogScrollRef = useRef<HTMLDivElement>(null)
const categoryScrollRef = useRef<HTMLDivElement>(null) const categoryScrollRef = useRef<HTMLDivElement>(null)
const catalogItems = const catalogItems =
@@ -62,9 +63,7 @@ export function MaterialPicker({
if (disabled) return if (disabled) return
setShowCustom(false) setShowCustom(false)
setPaintPanelOpen(false) setPaintPanelOpen(false)
const category = getCatalogMaterialById(materialId)?.category onSelectMaterialPreset?.(toLibraryMaterialRef(materialId))
if (!category) return
onSelectMaterialPreset?.(toLibraryMaterialRef(materialId), category)
} }
useEffect(() => { useEffect(() => {
@@ -113,20 +112,17 @@ export function MaterialPicker({
if (disabled) return if (disabled) return
setShowCustom(true) setShowCustom(true)
setPaintPanelOpen(true) setPaintPanelOpen(true)
onChange?.( onChange?.({
{ preset: 'custom',
preset: 'custom', properties: {
properties: { color: value?.properties?.color || '#ffffff',
color: value?.properties?.color || '#ffffff', roughness: value?.properties?.roughness ?? 0.5,
roughness: value?.properties?.roughness ?? 0.5, metalness: value?.properties?.metalness ?? 0,
metalness: value?.properties?.metalness ?? 0, opacity: value?.properties?.opacity ?? 1,
opacity: value?.properties?.opacity ?? 1, transparent: value?.properties?.transparent ?? false,
transparent: value?.properties?.transparent ?? false, side: value?.properties?.side ?? 'front',
side: value?.properties?.side ?? 'front',
},
}, },
'other', })
)
} }
return ( return (
@@ -47,7 +47,6 @@ export function PaintPanel() {
transparent: nextTransparent, transparent: nextTransparent,
}, },
}, },
category: 'other',
sourceTarget: activePaintMaterial?.sourceTarget ?? activePaintTarget, sourceTarget: activePaintMaterial?.sourceTarget ?? activePaintTarget,
}) })
} }
-34
View File
@@ -8,8 +8,6 @@ import {
getEffectiveStairSurfaceMaterial, getEffectiveStairSurfaceMaterial,
getEffectiveWallSurfaceMaterial, getEffectiveWallSurfaceMaterial,
getLibraryMaterialIdFromRef, getLibraryMaterialIdFromRef,
getMaterialCategoryTargets,
type MaterialCategory,
type MaterialSchema, type MaterialSchema,
type MaterialTarget, type MaterialTarget,
type RoofNode, type RoofNode,
@@ -31,7 +29,6 @@ export type SingleSurfaceMaterialRole = 'surface'
export type ActivePaintMaterial = { export type ActivePaintMaterial = {
material?: MaterialSchema material?: MaterialSchema
materialPreset?: string materialPreset?: string
category?: MaterialCategory
sourceTarget: PaintableMaterialTarget sourceTarget: PaintableMaterialTarget
} }
@@ -50,34 +47,10 @@ function getCatalogEntryForActivePaintMaterial(material: ActivePaintMaterial | n
return getCatalogMaterialById(catalogId) return getCatalogMaterialById(catalogId)
} }
function resolvePaintMaterialCategory(
material: MaterialSchema | undefined,
materialPreset: string | undefined,
): MaterialCategory | undefined {
const catalogId = getLibraryMaterialIdFromRef(materialPreset) ?? material?.id ?? undefined
const catalogEntry = getCatalogMaterialById(catalogId)
if (catalogEntry?.category) return catalogEntry.category
if (material || materialPreset === undefined) return 'other'
return undefined
}
export function getActivePaintMaterialLabel(material: ActivePaintMaterial | null | undefined) { export function getActivePaintMaterialLabel(material: ActivePaintMaterial | null | undefined) {
return getCatalogEntryForActivePaintMaterial(material)?.label ?? 'Custom' return getCatalogEntryForActivePaintMaterial(material)?.label ?? 'Custom'
} }
export function isActivePaintMaterialCompatible(
material: ActivePaintMaterial | null | undefined,
target: PaintableMaterialTarget,
) {
if (!hasActivePaintMaterial(material)) return false
const catalogEntry = getCatalogEntryForActivePaintMaterial(material)
const category = material?.category ?? catalogEntry?.category
if (!category) return false
return getMaterialCategoryTargets(category).includes(target)
}
export function buildWallSurfaceMaterialPatch( export function buildWallSurfaceMaterialPatch(
node: WallNode, node: WallNode,
targetSide: WallSurfaceSide, targetSide: WallSurfaceSide,
@@ -200,7 +173,6 @@ export function resolveActivePaintMaterialFromSelection(params: {
? { ? {
material: surface.material, material: surface.material,
materialPreset: surface.materialPreset, materialPreset: surface.materialPreset,
category: resolvePaintMaterialCategory(surface.material, surface.materialPreset),
sourceTarget: 'wall', sourceTarget: 'wall',
} }
: null : null
@@ -221,7 +193,6 @@ export function resolveActivePaintMaterialFromSelection(params: {
? { ? {
material: surface.material, material: surface.material,
materialPreset: surface.materialPreset, materialPreset: surface.materialPreset,
category: resolvePaintMaterialCategory(surface.material, surface.materialPreset),
sourceTarget: 'roof', sourceTarget: 'roof',
} }
: null : null
@@ -242,7 +213,6 @@ export function resolveActivePaintMaterialFromSelection(params: {
? { ? {
material: surface.material, material: surface.material,
materialPreset: surface.materialPreset, materialPreset: surface.materialPreset,
category: resolvePaintMaterialCategory(surface.material, surface.materialPreset),
sourceTarget: 'stair', sourceTarget: 'stair',
} }
: null : null
@@ -263,10 +233,6 @@ export function resolveActivePaintMaterialFromSelection(params: {
? { ? {
material: selectedNode.material, material: selectedNode.material,
materialPreset: selectedNode.materialPreset, materialPreset: selectedNode.materialPreset,
category: resolvePaintMaterialCategory(
selectedNode.material,
selectedNode.materialPreset,
),
sourceTarget: target, sourceTarget: target,
} }
: null : null