import { useMemo } from 'react';
import { Grid, Environment, Text } from '@react-three/drei';
import { ViewerOrbitControls as OrbitControls } from './ViewerOrbitControls';
import * as THREE from 'three';
import SceneCanvas from '../SceneCanvas';
import FallbackDiagram from '../FallbackDiagram';
import { WindArrow } from './WindArrow';
import { useCanvasTheme } from '@/lib/theme';
export interface IsolatedRoof3DInput {
/** Tipo de cobertura: 'shed' (uma água) ou 'gable' (duas águas) */
type: 'shed' | 'gable';
/** Inclinação θ (graus) */
theta: number;
/** Altura livre dos suportes (m) */
height: number;
/** Largura da cobertura (m) — dimensão transversal (b) */
width: number;
/** Profundidade da cobertura (m) — dimensão longitudinal (l) */
depth: number;
/** Cpe barlavento (sobre a face exposta ao vento) */
cpeWindward: number;
/** Cpe sotavento (face oposta) */
cpeLeeward: number;
/** Cpe sob a face superior (sucção) */
cpeTop: number;
/** Força resultante na cobertura (kN) */
forceKN: number;
}
function pressureColor(cpe: number, isDark: boolean): THREE.Color {
if (Math.abs(cpe) < 0.05) return new THREE.Color(isDark ? '#475569' : '#cbd5e1');
if (cpe > 0) {
// Sucção (Vermelho)
const intensity = Math.min(1, cpe / 2.0);
return new THREE.Color(`hsl(0, ${60 + intensity * 30}%, ${50 - intensity * 10}%)`);
} else {
// Pressão (Azul)
const intensity = Math.min(1, Math.abs(cpe) / 2.0);
return new THREE.Color(`hsl(210, ${60 + intensity * 30}%, ${50 - intensity * 10}%)`);
}
}
function IsolatedRoofModel({
type,
theta,
height,
width,
depth,
cpeWindward,
cpeLeeward,
forceKN,
}: IsolatedRoof3DInput) {
const theme = useCanvasTheme();
const isDark = theme === 'dark';
const labelColor = isDark ? '#cbd5e1' : '#475569';
const textColor = isDark ? '#93c5fd' : '#1a202c';
const lineMaterialColor = isDark ? '#94a3b8' : '#64748b';
const pillarColor = isDark ? '#cbd5e1' : '#475569';
const thetaRad = (theta * Math.PI) / 180;
const halfDepth = depth / 2;
const halfWidth = width / 2;
const windwardColor = useMemo(() => pressureColor(cpeWindward, isDark), [cpeWindward, isDark]);
const leewardColor = useMemo(() => pressureColor(cpeLeeward, isDark), [cpeLeeward, isDark]);
const h_diff = depth * Math.tan(thetaRad);
const h_half = (depth / 2) * Math.tan(thetaRad);
// Altura média da cobertura no centro geométrico
const centerY = type === 'shed' ? height + h_diff / 2 : height + h_half / 2;
// Definição das colunas de suporte (pilares)
const pillars = useMemo(() => {
const list: { pos: [number, number, number]; h: number }[] = [];
if (type === 'shed') {
list.push(
{ pos: [-halfWidth, height / 2, -halfDepth], h: height },
{ pos: [halfWidth, height / 2, -halfDepth], h: height },
{ pos: [-halfWidth, (height + h_diff) / 2, halfDepth], h: height + h_diff },
{ pos: [halfWidth, (height + h_diff) / 2, halfDepth], h: height + h_diff },
);
} else {
list.push(
{ pos: [-halfWidth, height / 2, -halfDepth], h: height },
{ pos: [halfWidth, height / 2, -halfDepth], h: height },
{ pos: [-halfWidth, height / 2, halfDepth], h: height },
{ pos: [halfWidth, height / 2, halfDepth], h: height },
{ pos: [-halfWidth, (height + h_half) / 2, 0], h: height + h_half },
{ pos: [halfWidth, (height + h_half) / 2, 0], h: height + h_half },
);
}
return list;
}, [type, depth, width, height, h_diff, h_half, halfDepth, halfWidth]);
return (
{/* Solo translúcido */}
{/* === COBERTURA (PAINÉIS 3D SÓLIDOS) === */}
{type === 'shed' ? (
// Uma água (Shed): dividida em metade barlavento e metade sotavento
{/* Metade Barlavento (Z < 0) */}
{/* Metade Sotavento (Z > 0) */}
) : (
// Duas águas (Gable)
{/* Água Esquerda / Barlavento (Z < 0) */}
{/* Água Direita / Sotavento (Z > 0) */}
)}
{/* Pilares de Suporte */}
{pillars.map((p, i) => (
))}
{/* Vetor de vento (Seta verde/azul batendo no elemento) */}
{/* === LINHAS DE COTA (CAD-Style) === */}
{/* Cota de Altura (h) */}
h = {height}m
{/* Cota de Profundidade/Span (d) */}
d = {depth}m
{/* Cota de Largura (b) */}
b = {width}m
{/* Rótulo Superior */}
θ={theta}° | F = {forceKN.toFixed(1)} kN
);
}
export default function IsolatedRoof3DViewer({
type,
theta,
height,
width,
depth,
cpeWindward,
cpeLeeward,
cpeTop,
forceKN,
}: IsolatedRoof3DInput) {
const cameraDistance = Math.max(Math.max(width, depth) * 1.3, height * 1.5, 8);
const fallback = (
);
return (
);
}