import React, { useRef } from 'react'; import * as THREE from 'three'; import { useFrame } from '@react-three/fiber'; import { Text, Line } from '@react-three/drei'; import type { ShelterCondition, OpeningPosition } 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; viewMode: '3d' | 'elevation'; } const WindArrow: React.FC<{ start: [number, number, number]; end: [number, number, number]; color?: string; speed?: number; }> = ({ start, end, color = '#38bdf8', speed = 1.5 }) => { const arrowRef = useRef(null); useFrame(({ clock }) => { if (arrowRef.current) { const t = (clock.getElapsedTime() * speed) % 1; const x = start[0] + (end[0] - start[0]) * t; const y = start[1] + (end[1] - start[1]) * t; const z = start[2] + (end[2] - start[2]) * t; arrowRef.current.position.set(x, y, z); } }); const dir = new THREE.Vector3(...end).sub(new THREE.Vector3(...start)); const len = dir.length(); const angleZ = Math.atan2(dir.y, dir.x); return ( ); }; export const Shelter3D: React.FC = ({ condition, depth, width, height, theta, backClosure, leftClosure, rightClosure, openingPos, viewMode, }) => { const isElevation = viewMode === 'elevation'; // Geometria básica const halfW = width / 2; const halfD = depth / 2; const thetaRad = (theta * Math.PI) / 180; const slopeRise = depth * Math.tan(thetaRad); // Paredes com abertura no topo ou na base const renderWallWithGap = ( w: number, h: number, closure: number, pos: OpeningPosition, rotY = 0, position: [number, number, number] = [0, 0, 0] ) => { if (closure <= 0) return null; const op = closure / 100; const wallH = h * op; let yPos = position[1] + wallH / 2; if (pos === 'top' && op < 1) { // Parede na parte de baixo, fresta no topo yPos = position[1] + wallH / 2; } else if (pos === 'bottom' && op < 1) { // Parede suspensa na parte superior, vão na base yPos = position[1] + h - wallH / 2; } return ( {/* Linha de contorno */} ); }; return ( {/* Plano do Solo */} {/* 1. Estrutura Metálica Principal (Pilares e Vigas em Balanço - Tubos Quadrados) */} {[-halfW + 0.6, halfW - 0.6].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])} {/* 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])} {/* 4. Linhas de Corrente e Vetores de Vento (Ilustrativo e Didático) */} {/* Vento de entrada (Barlavento) */} {/* Linhas de corrente sobre o telhado */} {/* Fresta na parede de fundo: se houver abertura no topo, mostrar vento escapando pela fresta superior! */} {backClosure > 0 && backClosure < 100 && openingPos === 'top' && ( <> Alívio (Fresta Topo) )} {/* Fresta na base */} {backClosure > 0 && backClosure < 100 && openingPos === 'bottom' && ( <> Vão Livre Inferior )} {/* Textos Informativos 3D */} {isElevation ? 'CORTE A-A (Elevação)' : 'Abrigo / Pórtico (NBR 6123)'} VENTO FRONTAL ); }; export default Shelter3D;