import { Grid, Environment } from '@react-three/drei'; import { ViewerOrbitControls as OrbitControls } from './ViewerOrbitControls'; import SceneCanvas from '../SceneCanvas'; import { WindArrow } from './WindArrow'; import { useCanvasTheme } from '@/lib/theme'; import type { PipeDef } from '../../lib/modules/piperack'; import FallbackDiagram from '../FallbackDiagram'; export interface Piperack3DInput { width: number; height: number; elevation: number; spacing: number; numFrames: number; pipes: PipeDef[]; phiTotal: number; eta: number; } function PiperackModel({ width, height, elevation, spacing, numFrames, pipes, phiTotal, }: Piperack3DInput) { const theme = useCanvasTheme(); const isDark = theme === 'dark'; const halfW = width / 2; const rackTop = elevation + height / 2; const steelColor = isDark ? '#94a3b8' : '#64748b'; const pipeColors = ['#ef4444', '#3b82f6', '#f59e0b', '#10b981', '#8b5cf6']; // Gera os pórticos longitudinais (as fileiras de colunas) const frames = []; const totalSpacing = spacing * (numFrames > 1 ? numFrames - 1 : 0); const zOffset = totalSpacing / 2; for (let i = 0; i < numFrames; i++) { const z = -zOffset + i * spacing; // Desenha apenas 2 colunas nas extremidades da largura do pórtico (conforme solicitado pelo usuário) const numCols = 2; for (let c = 0; c < numCols; c++) { const colX = -halfW + (c / (numCols - 1)) * width; frames.push( ); } // Viga Longitudinal da fileira (representando a face reticulada do pórtico) frames.push( {/* Opacidade ajustada para atingir 90% quando a solidez for 1.0 */} ); } // Gera as tubulações correndo longitudinalmente (ao longo de X) // Agrupa tubos pela mesma elevação para evitar colisões e distribuí-los lado a lado (ao longo de Z) const pipeMeshes: any[] = []; const groups: Record = {}; pipes.forEach((pipe, idx) => { // Arredondamos a elevação para agrupar valores muito próximos const key = Math.round(pipe.elevationOffset * 100) / 100; if (!groups[key]) groups[key] = []; groups[key].push({pipe, idx}); }); Object.values(groups).forEach(group => { const gap = 0.2; // 20cm de espaçamento entre tubos na mesma elevação const totalDiam = group.reduce((sum, item) => sum + item.pipe.diameter, 0); const totalGroupWidth = totalDiam + gap * (group.length - 1); // Inicia a distribuição centralizada em Z=0 let currentZ = -totalGroupWidth / 2; group.forEach((item) => { const { pipe, idx } = item; const zPos = currentZ + pipe.diameter / 2; currentZ += pipe.diameter + gap; const yPos = (elevation - height/2) + pipe.elevationOffset + pipe.diameter/2; const color = pipeColors[idx % pipeColors.length]; pipeMeshes.push( ); }); }); return ( {frames} {pipeMeshes} {/* Vento batendo lateralmente (transversal ao Pipe-rack) */} ); } export function Piperack3D(props: Piperack3DInput) { const isDark = useCanvasTheme() === 'dark'; const fallback = ( ); return ( ); }