import { useMemo } from 'react';
import { OrbitControls, Grid, Environment, Text } from '@react-three/drei';
import * as THREE from 'three';
import { useGalpaoStore } from '../store/galpaoStore';
import { useWindStore } from '../store/appStore';
import SceneCanvas from './SceneCanvas';
import FallbackDiagram from './FallbackDiagram';
function pressureColor(cpe: number, cpi: number): THREE.Color {
const p = cpe - cpi;
const intensity = Math.min(1, Math.abs(p) / 1.2);
if (p > 0) {
const h = 215 - intensity * 10;
const s = 70 + intensity * 25;
const l = Math.max(35, 65 - intensity * 25);
return new THREE.Color(`hsl(${h}, ${s}%, ${l}%)`);
}
const h = 0;
const s = 70 + intensity * 25;
const l = Math.max(40, 65 - intensity * 20);
return new THREE.Color(`hsl(${h}, ${s}%, ${l}%)`);
}
function PressureArrow({
center,
normal,
p,
}: {
center: [number, number, number];
normal: [number, number, number];
p: number;
}) {
const { q } = useWindStore();
const force = p * q; // kN/m2
const normVec = useMemo(() => new THREE.Vector3(...normal).normalize(), [normal]);
const centerVec = useMemo(() => new THREE.Vector3(...center), [center]);
const isPressure = p > 0;
const dir = useMemo(() => isPressure ? normVec.clone().negate() : normVec.clone(), [isPressure, normVec]);
const quat = useMemo(() => {
const q = new THREE.Quaternion();
q.setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir);
return new THREE.Euler().setFromQuaternion(q);
}, [dir]);
if (Math.abs(force) < 0.05) return null;
const length = Math.max(0.6, Math.min(3.0, Math.abs(force) * 1.5));
const color = isPressure ? '#3b82f6' : '#ef4444';
const start = isPressure ? centerVec.clone().sub(dir.clone().multiplyScalar(length)) : centerVec;
const end = isPressure ? centerVec : centerVec.clone().add(dir.clone().multiplyScalar(length));
const mid = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5);
const headLen = Math.min(0.4, length * 0.4);
return (
{length - headLen > 0 && (
)}
);
}
export function WarehouseModel() {
const { width, length, height, roofPitch, wallCpe, roofCpe } = useGalpaoStore();
const { windAngle, cpi } = useWindStore();
const roofHeight = (width / 2) * Math.tan((roofPitch * Math.PI) / 180);
const theta = (roofPitch * Math.PI) / 180;
const widthSlope = width / 2 / Math.cos(theta);
const wallAColor = useMemo(() => pressureColor(wallCpe.A, cpi), [wallCpe.A, cpi]);
const wallBColor = useMemo(() => pressureColor(wallCpe.B, cpi), [wallCpe.B, cpi]);
const wallCColor = useMemo(() => pressureColor(wallCpe.C, cpi), [wallCpe.C, cpi]);
const wallDColor = useMemo(() => pressureColor(wallCpe.D, cpi), [wallCpe.D, cpi]);
const roofEColor = useMemo(() => pressureColor(roofCpe.E, cpi), [roofCpe.E, cpi]);
const roofFColor = useMemo(() => pressureColor(roofCpe.F, cpi), [roofCpe.F, cpi]);
const roofGColor = useMemo(() => pressureColor(roofCpe.G, cpi), [roofCpe.G, cpi]);
const roofHColor = useMemo(() => pressureColor(roofCpe.H, cpi), [roofCpe.H, cpi]);
const wallThickness = 0.15;
const isParallel = windAngle === 90;
// Shapes para os oitões (gables)
const leftShape = useMemo(() => {
const s = new THREE.Shape();
s.moveTo(-width / 2, 0);
s.lineTo(0, roofHeight);
s.lineTo(0, 0);
s.closePath();
return s;
}, [width, roofHeight]);
const rightShape = useMemo(() => {
const s = new THREE.Shape();
s.moveTo(0, 0);
s.lineTo(0, roofHeight);
s.lineTo(width / 2, 0);
s.closePath();
return s;
}, [width, roofHeight]);
return (
{/* === PAREDES === */}
{/* Lateral Esquerda (X = -width/2) */}
{/* Lateral Direita (X = width/2) */}
{/* Parede Traseira (Z = -length/2) */}
{/* Parede Frontal (Z = length/2) */}
{/* === OITÕES (GABLES) === */}
{/* Oitão Frontal (Z = length/2) */}
{/* Oitão Traseiro (Z = -length/2) */}
{/* === TELHADO (DUAS ÁGUAS) === */}
{/* Água Esquerda (X < 0) */}
{/* Seg 1 (Traseiro-Fim) */}
{/* Seg 2 (Traseiro-Meio) */}
{/* Seg 3 (Frontal-Meio) */}
{/* Seg 4 (Frontal-Fim) */}
{isParallel ? 'Telhado (Zonas E/F/G/H)' : 'Telhado E/F (Barlavento)'}
{/* Água Direita (X > 0) */}
{/* Seg 1 (Traseiro-Fim) */}
{/* Seg 2 (Traseiro-Meio) */}
{/* Seg 3 (Frontal-Meio) */}
{/* Seg 4 (Frontal-Fim) */}
{isParallel ? 'Telhado (Zonas E/F/G/H)' : 'Telhado G/H (Sotavento)'}
{/* Cumeeira */}
{/* Bordas Laterais do Telhado (Beirais) */}
{/* === RÓTULOS 3D === */}
{/* Rótulo Parede Frontal */}
{isParallel ? 'Parede B (Sotavento)' : 'Parede C (Barlavento)'}
{/* Rótulo Parede Traseira */}
{isParallel ? 'Parede A (Barlavento)' : 'Parede D (Sotavento)'}
{/* Rótulo Parede Esquerda */}
{isParallel ? 'Parede C (Lateral)' : 'Parede A (Lateral)'}
{/* Rótulo Parede Direita */}
{isParallel ? 'Parede D (Lateral)' : 'Parede B (Lateral)'}
);
}
export default function Warehouse3DViewer() {
const { width, length, height, roofPitch, wallCpe, roofCpe } = useGalpaoStore();
const { windAngle, cpi } = useWindStore();
const fallback = (
);
const maxDimension = Math.max(width, length, height);
return (
);
}