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