import { useMemo } from 'react'; import * as THREE from 'three'; /** * Room3D component - Renders a 3D room based on AI analysis * Used to display the wardrobe in context of the user's actual room */ // Floor material based on type function FloorMaterial({ material, color }) { const texture = useMemo(() => { // In a real implementation, you'd load actual textures // For now, we use colors with some roughness variation return null; }, [material]); return ( ); } // Get material properties from wall material type function getWallMaterialProps(material, texture) { const props = { roughness: 0.8, metalness: 0.02 }; switch (material) { case 'paint': props.roughness = texture === 'glossy' ? 0.3 : texture === 'smooth' ? 0.7 : 0.8; break; case 'wallpaper': props.roughness = 0.85; break; case 'brick': props.roughness = 0.95; props.metalness = 0.0; break; case 'wood_panel': props.roughness = 0.65; props.metalness = 0.05; break; case 'concrete': props.roughness = 0.9; props.metalness = 0.01; break; case 'plaster': props.roughness = 0.85; break; case 'stone': props.roughness = 0.92; props.metalness = 0.0; break; case 'tile': props.roughness = 0.3; props.metalness = 0.05; break; default: break; } return props; } // Wall with optional window/door cutouts function Wall({ position, rotation, size, color, material, texture, windows = [], doors = [] }) { const [width, height] = size; const matProps = getWallMaterialProps(material, texture); // Create wall geometry with cutouts (simplified - just overlays for now) return ( {/* Main wall */} {/* Windows */} {windows.map((win, i) => { const winWidth = width * win.width; const winHeight = height * win.height; const winX = (win.x - 0.5) * width; const winY = (win.y - 0.5) * height; return ( {/* Window glass */} {/* Window frame */} ); })} {/* Doors */} {doors.map((door, i) => { const doorWidth = width * door.width; const doorHeight = height * door.height; const doorX = (door.x - 0.5) * width; const doorY = -(height / 2) + (doorHeight / 2); return ( {/* Door opening (darker rectangle) */} {/* Door frame */} ); })} ); } export default function Room3D({ roomConfig, wardrobeConfig }) { if (!roomConfig) return null; const { width, length, height, walls, windows, doors, floor, ceiling, wardrobePlacement } = roomConfig; // Calculate wall positions const halfWidth = width / 2; const halfLength = length / 2; const halfHeight = height / 2; // Get windows/doors for each wall const backWindows = windows.filter(w => w.wall === 'back').map(w => ({ x: w.x, y: w.y, width: w.width, height: w.height })); const leftWindows = windows.filter(w => w.wall === 'left').map(w => ({ x: w.x, y: w.y, width: w.width, height: w.height })); const rightWindows = windows.filter(w => w.wall === 'right').map(w => ({ x: w.x, y: w.y, width: w.width, height: w.height })); const backDoors = doors.filter(d => d.wall === 'back').map(d => ({ x: d.x, width: d.width, height: d.height })); const leftDoors = doors.filter(d => d.wall === 'left').map(d => ({ x: d.x, width: d.width, height: d.height })); const rightDoors = doors.filter(d => d.wall === 'right').map(d => ({ x: d.x, width: d.width, height: d.height })); // Calculate wardrobe position based on placement const getWardrobePosition = () => { if (!wardrobeConfig || !wardrobePlacement) return [0, 0, 0]; const wardrobeWidth = (wardrobeConfig.widthMm || 2400) / 1000; const wardrobeDepth = (wardrobeConfig.depthMm || 600) / 1000; switch (wardrobePlacement.wall) { case 'back': return [ ((wardrobePlacement.xPercent / 100) - 0.5) * width + wardrobeWidth / 2, 0, -halfLength + wardrobeDepth / 2 + 0.05 ]; case 'left': return [ -halfWidth + wardrobeDepth / 2 + 0.05, 0, ((wardrobePlacement.xPercent / 100) - 0.5) * length + wardrobeWidth / 2 ]; case 'right': return [ halfWidth - wardrobeDepth / 2 - 0.05, 0, ((wardrobePlacement.xPercent / 100) - 0.5) * length + wardrobeWidth / 2 ]; default: return [0, 0, -halfLength + wardrobeDepth / 2 + 0.05]; } }; const wardrobePosition = getWardrobePosition(); const wardrobeRotation = wardrobePlacement?.wall === 'left' ? [0, Math.PI / 2, 0] : wardrobePlacement?.wall === 'right' ? [0, -Math.PI / 2, 0] : [0, 0, 0]; return ( {/* Floor */} {/* Back wall */} {walls?.back?.visible !== false && ( )} {/* Left wall */} {walls?.left?.visible !== false && ( )} {/* Right wall */} {walls?.right?.visible !== false && ( )} {/* Ceiling (plafond) */} {ceiling?.visible !== false && (() => { const ceilingColor = ceiling?.color || '#ffffff'; const ceilingType = ceiling?.type || 'flat'; const slopeAngle = ceiling?.slopeAngleDeg || 0; const ceilingMatProps = getWallMaterialProps(ceiling?.material || 'paint', 'smooth'); if (ceilingType === 'sloped' && slopeAngle > 0) { // Sloped ceiling - render as a tilted plane const slopeRad = (slopeAngle * Math.PI) / 180; const slopeDir = ceiling?.slopeDirection || 'front_to_back'; const slopeRotX = slopeDir === 'front_to_back' ? Math.PI / 2 + slopeRad * 0.5 : Math.PI / 2; const slopeRotZ = slopeDir === 'left_to_right' ? slopeRad * 0.5 : 0; const slopeW = slopeDir === 'left_to_right' ? width / Math.cos(slopeRad * 0.5) : width; const slopeL = slopeDir === 'front_to_back' ? length / Math.cos(slopeRad * 0.5) : length; return ( ); } // Flat ceiling return ( ); })()} {/* Wardrobe position indicator (for debugging/visualization) */} {wardrobePlacement && ( )} ); } // Export position calculator for use by parent export function calculateWardrobeTransform(roomConfig, wardrobeConfig) { if (!roomConfig || !roomConfig.wardrobePlacement) { return { position: [0, 0, 0], rotation: [0, 0, 0] }; } const { width, length, wardrobePlacement } = roomConfig; const halfWidth = width / 2; const halfLength = length / 2; const wardrobeWidth = (wardrobeConfig?.widthMm || 2400) / 1000; const wardrobeDepth = (wardrobeConfig?.depthMm || 600) / 1000; let position, rotation; switch (wardrobePlacement.wall) { case 'back': position = [ ((wardrobePlacement.xPercent / 100) - 0.5) * width + wardrobeWidth / 4, 0, -halfLength + wardrobeDepth / 2 + 0.05 ]; rotation = [0, 0, 0]; break; case 'left': position = [ -halfWidth + wardrobeDepth / 2 + 0.05, 0, ((wardrobePlacement.xPercent / 100) - 0.5) * length ]; rotation = [0, Math.PI / 2, 0]; break; case 'right': position = [ halfWidth - wardrobeDepth / 2 - 0.05, 0, ((wardrobePlacement.xPercent / 100) - 0.5) * length ]; rotation = [0, -Math.PI / 2, 0]; break; default: position = [0, 0, -halfLength + wardrobeDepth / 2 + 0.05]; rotation = [0, 0, 0]; } return { position, rotation }; }