import { Text, Grid, Environment } from '@react-three/drei'; import { WindArrow as GlobalWindArrow } from './WindArrow'; import { ViewerOrbitControls as OrbitControls } from './ViewerOrbitControls'; import SceneCanvas from '../SceneCanvas'; import { useCanvasTheme } from '@/lib/theme'; import { ShelterAirflowSystem } from './ShelterAirflowSystem'; import type { ShelterCondition, OpeningPosition, ShelterWindAngle } from '@/lib/nbr-tables/table-shelters'; export interface Shelter3DProps { condition: ShelterCondition; depth: number; width: number; height: number; theta: number; backClosure: number; leftClosure: number; rightClosure: number; openingPos: OpeningPosition; backRange?: [number, number]; numColumns?: number; windAngle?: ShelterWindAngle; viewMode: '3d' | 'elevation' | 'airflow'; } function ShelterModel({ condition: _condition, depth, width, height, theta, backClosure, leftClosure, rightClosure, openingPos, backRange, numColumns = 2, windAngle: _windAngle = 0, viewMode, }: Shelter3DProps) { const isElevation = viewMode === 'elevation'; const theme = useCanvasTheme(); const isDark = theme === 'dark'; // Geometria básica const halfW = width / 2; const halfD = depth / 2; const isAirflow = viewMode === 'airflow'; const thetaRad = (theta * Math.PI) / 180; const slopeRise = depth * Math.tan(thetaRad); // Paredes com abertura no topo ou na base (sem caixas/linhas brancas deslocadas) const renderWallWithGap = ( w: number, h: number, closure: number, pos: OpeningPosition, rotY = 0, position: [number, number, number] = [0, 0, 0], customRange?: [number, number] ) => { if (closure <= 0) return null; let op = closure / 100; let wallH = h * op; let yPos = position[1] + wallH / 2; if (customRange && closure < 100) { const bottomPct = Math.max(0, Math.min(100, customRange[0])) / 100; const topPct = Math.max(0, Math.min(100, customRange[1])) / 100; op = Math.max(0.01, topPct - bottomPct); wallH = h * op; yPos = position[1] + h * bottomPct + wallH / 2; } else if (pos === 'top' && closure < 100) { // Parede na parte de baixo, fresta no topo yPos = position[1] + wallH / 2; } else if (pos === 'bottom' && closure < 100) { // Parede suspensa na parte superior, vão na base yPos = position[1] + h - wallH / 2; } return ( ); }; const overhang = 0.6; const spanWidth = Math.max(0.1, width - 2 * overhang); const spacing = spanWidth / Math.max(1, numColumns - 1); const columnPositions = Array.from({ length: numColumns }).map( (_, i) => -halfW + overhang + i * spacing ); return ( {/* 1. Estrutura Metálica Principal (Pilares e Vigas em Balanço - Tubos Quadrados) */} {columnPositions.map((xPos, idx) => ( {/* Pilar traseiro (lado direito na elevação) */} {/* Viga horizontal em balanço (TUBO QUAD. 150x150) */} ))} {/* 2. Cobertura / Marquise */} {/* 3. Fechamentos de Fundo e Laterais (com suporte a fresta/abertura) */} {/* Parede de Fundo (Traseira Z = +halfD) */} {renderWallWithGap(width, height, backClosure, openingPos, 0, [0, 0, halfD], backRange)} {/* Parede Lateral Esquerda (X = -halfW) */} {renderWallWithGap(depth, height, leftClosure, 'distributed', Math.PI / 2, [-halfW, 0, 0])} {/* Parede Lateral Direita (X = +halfW) */} {renderWallWithGap(depth, height, rightClosure, 'distributed', Math.PI / 2, [halfW, 0, 0])} {/* Seta de Vento Global (Sempre visível para não pular de posição) */} Vento {/* Partículas de Ar */} {isAirflow && ( )} {/* Textos Informativos 3D - Virados para quem olha da frente (-Z) */} {!isAirflow && ( <> {isElevation ? 'CORTE A-A (Elevação)' : 'Abrigo / Pórtico (NBR 6123)'} Arrancamento [+Fz ↑] Empuxo no Fundo [+Fx →] )} ); } export function Shelter3D(props: Shelter3DProps) { const theme = useCanvasTheme(); const isDark = theme === 'dark'; const { width, depth, height, viewMode } = props; const dist = Math.max(width, depth) * 1.8 + 8; return ( 3D indisponível} > ); } export default Shelter3D;