import { useMemo } from 'react'; import * as THREE from 'three'; import type { Wall, WallOpening } from '@house-plan-maker/shared'; import { splitWallAroundOpenings, wallRotationY, wallSegmentCenter3D, type WallSegment, } from './utils/wallGeometry'; interface WallMeshProps { readonly wall: Wall; readonly openings: readonly WallOpening[]; readonly wallHeight: number; readonly wallColor?: string; readonly selectedIds: ReadonlySet; readonly onSelect?: (id: string) => void; } const DEFAULT_WALL_COLOR = '#f0ebe3'; const WALL_SELECTED_COLOR = '#b8d4e3'; // ── Wall material cache ── const wallMaterialCache = new Map(); function getWallMaterial(color: string): THREE.MeshStandardMaterial { let mat = wallMaterialCache.get(color); if (!mat) { mat = new THREE.MeshStandardMaterial({ color, roughness: 0.7, side: THREE.DoubleSide }); wallMaterialCache.set(color, mat); } return mat; } const wallSelectedMaterial = new THREE.MeshStandardMaterial({ color: WALL_SELECTED_COLOR, roughness: 0.7, side: THREE.DoubleSide, }); function WallSegmentMesh({ wall, segment, thickness, wallColor, isSelected, onSelect, }: { readonly wall: Wall; readonly segment: WallSegment; readonly thickness: number; readonly wallColor: string; readonly isSelected: boolean; readonly onSelect?: (id: string) => void; }) { const segmentWidth = segment.endAlongWall - segment.startAlongWall; const segmentHeight = segment.topY - segment.bottomY; const center = useMemo( () => wallSegmentCenter3D(wall, segment), [wall, segment], ); const rotY = useMemo(() => wallRotationY(wall), [wall]); if (segmentWidth <= 0 || segmentHeight <= 0) return null; return ( { e.stopPropagation(); onSelect(wall.id); } : undefined} > ); } export function WallMesh({ wall, openings, wallHeight, wallColor = DEFAULT_WALL_COLOR, selectedIds, onSelect }: WallMeshProps) { const segments = useMemo( () => splitWallAroundOpenings(wall, openings, wallHeight), [wall, openings, wallHeight], ); const isSelected = selectedIds.has(wall.id); return ( {segments.map((segment, i) => ( ))} ); }