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 };
}