🚀 Auto-deploy: BrainWind atualizado em 28/07/2026 11:08:28

This commit is contained in:
2026-07-28 11:08:28 +00:00
parent 24067d61de
commit d83a832138
5 changed files with 221 additions and 48 deletions
+52 -47
View File
@@ -4,6 +4,7 @@ import { Grid, Environment, Text } from '@react-three/drei';
import { ViewerOrbitControls as OrbitControls } from './three/ViewerOrbitControls';
import * as THREE from 'three';
import { useGalpaoStore } from '../store/galpaoStore';
import { AirflowSystem } from './three/AirflowSystem';
import { useWindStore } from '../store/appStore';
import SceneCanvas from './SceneCanvas';
import FallbackDiagram from './FallbackDiagram';
@@ -34,6 +35,8 @@ function PressureArrow({
p: number;
}) {
const { q } = useWindStore();
const { viewMode } = useGalpaoStore();
if (viewMode === 'airflow') return null;
const force = p * q; // kN/m2
const normVec = useMemo(() => new THREE.Vector3(...normal).normalize(), [normal]);
@@ -63,12 +66,12 @@ function PressureArrow({
{length - headLen > 0 && (
<mesh position={mid.toArray()} rotation={[quat.x, quat.y, quat.z]}>
<cylinderGeometry args={[0.08, 0.08, length - headLen, 8]} />
<meshStandardMaterial color={color} />
<meshStandardMaterial color={color} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
)}
<mesh position={end.toArray()} rotation={[quat.x, quat.y, quat.z]}>
<coneGeometry args={[0.2, headLen, 8]} />
<meshStandardMaterial color={color} />
<meshStandardMaterial color={color} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
</group>
);
@@ -131,7 +134,7 @@ export function WarehouseModel() {
const { invalidate } = useThree();
const theme = useCanvasTheme();
const isDark = theme === 'dark';
const { width, length, height, roofPitch, wallCpe, roofCpe, template, extraHeight, skirtHeight } = useGalpaoStore();
const { width, length, height, roofPitch, wallCpe, roofCpe, template, extraHeight, skirtHeight, viewMode } = useGalpaoStore();
const { windAngle, cpi, permeabilityCase } = useWindStore();
useEffect(() => {
@@ -181,26 +184,26 @@ export function WarehouseModel() {
return (
<group>
{/* === PAREDES === */}
{showMainWalls && (
{showMainWalls && viewMode === 'solid' && (
<group>
{/* Lateral Esquerda (X = -width/2, Parede C) */}
<group position={[-width / 2, height / 2, 0]}>
<mesh position={[0, 0, -length / 4]} castShadow receiveShadow>
<boxGeometry args={[wallThickness, height, length / 2]} />
<meshStandardMaterial color={wallCColor} roughness={0.4} />
<meshStandardMaterial color={wallCColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[0, 0, -length / 4]} normal={[-1, 0, 0]} p={wallCpe.C - cpi} />
<mesh position={[0, 0, length / 4]} castShadow receiveShadow>
<boxGeometry args={[wallThickness, height, length / 2]} />
<meshStandardMaterial color={isParallel ? wallDColor : wallCColor} roughness={0.4} />
<meshStandardMaterial color={isParallel ? wallDColor : wallCColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[0, 0, length / 4]} normal={[-1, 0, 0]} p={(isParallel ? wallCpe.D : wallCpe.C) - cpi} />
{hasOpening('C', permeabilityCase, windAngle) && (
<mesh position={[-wallThickness/2 - 0.01, -height/4, 0]}>
<boxGeometry args={[0.05, height * 0.5, length * 0.15]} />
<meshStandardMaterial color="#0f172a" roughness={0.9} />
<meshStandardMaterial color="#0f172a" roughness={0.9} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
)}
</group>
@@ -209,20 +212,20 @@ export function WarehouseModel() {
<group position={[width / 2, height / 2, 0]}>
<mesh position={[0, 0, -length / 4]} castShadow receiveShadow>
<boxGeometry args={[wallThickness, height, length / 2]} />
<meshStandardMaterial color={isParallel ? wallCColor : wallDColor} roughness={0.4} />
<meshStandardMaterial color={isParallel ? wallCColor : wallDColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[0, 0, -length / 4]} normal={[1, 0, 0]} p={(isParallel ? wallCpe.C : wallCpe.D) - cpi} />
<mesh position={[0, 0, length / 4]} castShadow receiveShadow>
<boxGeometry args={[wallThickness, height, length / 2]} />
<meshStandardMaterial color={wallDColor} roughness={0.4} />
<meshStandardMaterial color={wallDColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[0, 0, length / 4]} normal={[1, 0, 0]} p={wallCpe.D - cpi} />
{hasOpening('D', permeabilityCase, windAngle) && (
<mesh position={[wallThickness/2 + 0.01, -height/4, 0]}>
<boxGeometry args={[0.05, height * 0.5, length * 0.15]} />
<meshStandardMaterial color="#0f172a" roughness={0.9} />
<meshStandardMaterial color="#0f172a" roughness={0.9} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
)}
</group>
@@ -231,20 +234,20 @@ export function WarehouseModel() {
<group position={[0, height / 2, -length / 2]}>
<mesh position={[-width / 4, 0, 0]} castShadow receiveShadow>
<boxGeometry args={[width / 2, height, wallThickness]} />
<meshStandardMaterial color={wallAColor} roughness={0.4} />
<meshStandardMaterial color={wallAColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[-width / 4, 0, 0]} normal={[0, 0, -1]} p={wallCpe.A - cpi} />
<mesh position={[width / 4, 0, 0]} castShadow receiveShadow>
<boxGeometry args={[width / 2, height, wallThickness]} />
<meshStandardMaterial color={isParallel ? wallAColor : wallBColor} roughness={0.4} />
<meshStandardMaterial color={isParallel ? wallAColor : wallBColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[width / 4, 0, 0]} normal={[0, 0, -1]} p={(isParallel ? wallCpe.A : wallCpe.B) - cpi} />
{hasOpening('A', permeabilityCase, windAngle) && (
<mesh position={[0, -height/4, -wallThickness/2 - 0.01]}>
<boxGeometry args={[width * 0.3, height * 0.5, 0.05]} />
<meshStandardMaterial color="#0f172a" roughness={0.9} />
<meshStandardMaterial color="#0f172a" roughness={0.9} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
)}
</group>
@@ -253,20 +256,20 @@ export function WarehouseModel() {
<group position={[0, height / 2, length / 2]}>
<mesh position={[-width / 4, 0, 0]} castShadow receiveShadow>
<boxGeometry args={[width / 2, height, wallThickness]} />
<meshStandardMaterial color={isParallel ? wallBColor : wallAColor} roughness={0.4} />
<meshStandardMaterial color={isParallel ? wallBColor : wallAColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[-width / 4, 0, 0]} normal={[0, 0, 1]} p={(isParallel ? wallCpe.B : wallCpe.A) - cpi} />
<mesh position={[width / 4, 0, 0]} castShadow receiveShadow>
<boxGeometry args={[width / 2, height, wallThickness]} />
<meshStandardMaterial color={wallBColor} roughness={0.4} />
<meshStandardMaterial color={wallBColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[width / 4, 0, 0]} normal={[0, 0, 1]} p={wallCpe.B - cpi} />
{hasOpening('B', permeabilityCase, windAngle) && (
<mesh position={[0, -height/4, wallThickness/2 + 0.01]}>
<boxGeometry args={[width * 0.3, height * 0.5, 0.05]} />
<meshStandardMaterial color="#0f172a" roughness={0.9} />
<meshStandardMaterial color="#0f172a" roughness={0.9} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
)}
</group>
@@ -279,22 +282,22 @@ export function WarehouseModel() {
{/* Platibanda Traseira */}
<mesh position={[0, height + extraHeight / 2, -length / 2]} castShadow receiveShadow>
<boxGeometry args={[width, extraHeight, wallThickness]} />
<meshStandardMaterial color={extraColor} roughness={0.4} />
<meshStandardMaterial color={extraColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
{/* Platibanda Frontal */}
<mesh position={[0, height + extraHeight / 2, length / 2]} castShadow receiveShadow>
<boxGeometry args={[width, extraHeight, wallThickness]} />
<meshStandardMaterial color={extraColor} roughness={0.4} />
<meshStandardMaterial color={extraColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
{/* Platibanda Esquerda */}
<mesh position={[-width / 2, height + extraHeight / 2, 0]} castShadow receiveShadow>
<boxGeometry args={[wallThickness, extraHeight, length]} />
<meshStandardMaterial color={extraColor} roughness={0.4} />
<meshStandardMaterial color={extraColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
{/* Platibanda Direita */}
<mesh position={[width / 2, height + extraHeight / 2, 0]} castShadow receiveShadow>
<boxGeometry args={[wallThickness, extraHeight, length]} />
<meshStandardMaterial color={extraColor} roughness={0.4} />
<meshStandardMaterial color={extraColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
</group>
)}
@@ -305,22 +308,22 @@ export function WarehouseModel() {
{/* Saia Traseira */}
<mesh position={[0, height - skirtHeight / 2, -length / 2]} castShadow receiveShadow>
<boxGeometry args={[width, skirtHeight, wallThickness]} />
<meshStandardMaterial color={extraColor} roughness={0.4} />
<meshStandardMaterial color={extraColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
{/* Saia Frontal */}
<mesh position={[0, height - skirtHeight / 2, length / 2]} castShadow receiveShadow>
<boxGeometry args={[width, skirtHeight, wallThickness]} />
<meshStandardMaterial color={extraColor} roughness={0.4} />
<meshStandardMaterial color={extraColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
{/* Saia Esquerda */}
<mesh position={[-width / 2, height - skirtHeight / 2, 0]} castShadow receiveShadow>
<boxGeometry args={[wallThickness, skirtHeight, length]} />
<meshStandardMaterial color={extraColor} roughness={0.4} />
<meshStandardMaterial color={extraColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
{/* Saia Direita */}
<mesh position={[width / 2, height - skirtHeight / 2, 0]} castShadow receiveShadow>
<boxGeometry args={[wallThickness, skirtHeight, length]} />
<meshStandardMaterial color={extraColor} roughness={0.4} />
<meshStandardMaterial color={extraColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
</group>
)}
@@ -330,11 +333,11 @@ export function WarehouseModel() {
<group position={[0, height, length / 2]}>
<mesh>
<shapeGeometry args={[leftShape]} />
<meshStandardMaterial color={isParallel ? wallBColor : wallCColor} side={THREE.DoubleSide} />
<meshStandardMaterial color={isParallel ? wallBColor : wallCColor} side={THREE.DoubleSide} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<mesh>
<shapeGeometry args={[rightShape]} />
<meshStandardMaterial color={isParallel ? wallBColor : wallDColor} side={THREE.DoubleSide} />
<meshStandardMaterial color={isParallel ? wallBColor : wallDColor} side={THREE.DoubleSide} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
</group>
@@ -342,11 +345,11 @@ export function WarehouseModel() {
<group position={[0, height, -length / 2]} rotation={[0, Math.PI, 0]}>
<mesh>
<shapeGeometry args={[leftShape]} />
<meshStandardMaterial color={isParallel ? wallAColor : wallDColor} side={THREE.DoubleSide} />
<meshStandardMaterial color={isParallel ? wallAColor : wallDColor} side={THREE.DoubleSide} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<mesh>
<shapeGeometry args={[rightShape]} />
<meshStandardMaterial color={isParallel ? wallAColor : wallCColor} side={THREE.DoubleSide} />
<meshStandardMaterial color={isParallel ? wallAColor : wallCColor} side={THREE.DoubleSide} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
</group>
@@ -356,47 +359,47 @@ export function WarehouseModel() {
{/* Seg 1 (Traseiro-Fim) */}
<mesh position={[0, 0, -3 * length / 8]} castShadow receiveShadow>
<boxGeometry args={[widthSlope, 0.08, length / 4]} />
<meshStandardMaterial color={isParallel ? roofEColor : roofEColor} roughness={0.4} />
<meshStandardMaterial color={isParallel ? roofEColor : roofEColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[0, 0.04, -3 * length / 8]} normal={[0, 1, 0]} p={roofCpe.E - cpi} />
{/* Seg 2 (Traseiro-Meio) */}
<mesh position={[0, 0, -length / 8]} castShadow receiveShadow>
<boxGeometry args={[widthSlope, 0.08, length / 4]} />
<meshStandardMaterial color={isParallel ? roofFColor : roofFColor} roughness={0.4} />
<meshStandardMaterial color={isParallel ? roofFColor : roofFColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[0, 0.04, -length / 8]} normal={[0, 1, 0]} p={roofCpe.F - cpi} />
{/* Seg 3 (Frontal-Meio) */}
<mesh position={[0, 0, length / 8]} castShadow receiveShadow>
<boxGeometry args={[widthSlope, 0.08, length / 4]} />
<meshStandardMaterial color={isParallel ? roofGColor : roofFColor} roughness={0.4} />
<meshStandardMaterial color={isParallel ? roofGColor : roofFColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[0, 0.04, length / 8]} normal={[0, 1, 0]} p={(isParallel ? roofCpe.G : roofCpe.F) - cpi} />
{/* Seg 4 (Frontal-Fim) */}
<mesh position={[0, 0, 3 * length / 8]} castShadow receiveShadow>
<boxGeometry args={[widthSlope, 0.08, length / 4]} />
<meshStandardMaterial color={isParallel ? roofHColor : roofEColor} roughness={0.4} />
<meshStandardMaterial color={isParallel ? roofHColor : roofEColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[0, 0.04, 3 * length / 8]} normal={[0, 1, 0]} p={(isParallel ? roofCpe.H : roofCpe.E) - cpi} />
{/* Linhas de Divisão das Zonas no Telhado */}
<mesh position={[0, 0.05, -length / 4]}>
<boxGeometry args={[widthSlope, 0.02, 0.06]} />
<meshStandardMaterial color="#0f172a" roughness={0.5} />
<meshStandardMaterial color="#0f172a" roughness={0.5} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<mesh position={[0, 0.05, 0]}>
<boxGeometry args={[widthSlope, 0.02, 0.06]} />
<meshStandardMaterial color="#0f172a" roughness={0.5} />
<meshStandardMaterial color="#0f172a" roughness={0.5} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<mesh position={[0, 0.05, length / 4]}>
<boxGeometry args={[widthSlope, 0.02, 0.06]} />
<meshStandardMaterial color="#0f172a" roughness={0.5} />
<meshStandardMaterial color="#0f172a" roughness={0.5} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<mesh position={[-widthSlope * 0.35, 0.05, 0]}>
<boxGeometry args={[0.06, 0.02, length]} />
<meshStandardMaterial color="#0f172a" roughness={0.5} />
<meshStandardMaterial color="#0f172a" roughness={0.5} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
{/* Rótulo e Título da Água */}
@@ -487,47 +490,47 @@ export function WarehouseModel() {
{/* Seg 1 (Traseiro-Fim) */}
<mesh position={[0, 0, -3 * length / 8]} castShadow receiveShadow>
<boxGeometry args={[widthSlope, 0.08, length / 4]} />
<meshStandardMaterial color={isParallel ? roofEColor : roofGColor} roughness={0.4} />
<meshStandardMaterial color={isParallel ? roofEColor : roofGColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[0, 0.04, -3 * length / 8]} normal={[0, 1, 0]} p={(isParallel ? roofCpe.E : roofCpe.G) - cpi} />
{/* Seg 2 (Traseiro-Meio) */}
<mesh position={[0, 0, -length / 8]} castShadow receiveShadow>
<boxGeometry args={[widthSlope, 0.08, length / 4]} />
<meshStandardMaterial color={isParallel ? roofFColor : roofHColor} roughness={0.4} />
<meshStandardMaterial color={isParallel ? roofFColor : roofHColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[0, 0.04, -length / 8]} normal={[0, 1, 0]} p={(isParallel ? roofCpe.F : roofCpe.H) - cpi} />
{/* Seg 3 (Frontal-Meio) */}
<mesh position={[0, 0, length / 8]} castShadow receiveShadow>
<boxGeometry args={[widthSlope, 0.08, length / 4]} />
<meshStandardMaterial color={isParallel ? roofGColor : roofHColor} roughness={0.4} />
<meshStandardMaterial color={isParallel ? roofGColor : roofHColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[0, 0.04, length / 8]} normal={[0, 1, 0]} p={(isParallel ? roofCpe.G : roofCpe.H) - cpi} />
{/* Seg 4 (Frontal-Fim) */}
<mesh position={[0, 0, 3 * length / 8]} castShadow receiveShadow>
<boxGeometry args={[widthSlope, 0.08, length / 4]} />
<meshStandardMaterial color={isParallel ? roofHColor : roofGColor} roughness={0.4} />
<meshStandardMaterial color={isParallel ? roofHColor : roofGColor} roughness={0.4} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<PressureArrow center={[0, 0.04, 3 * length / 8]} normal={[0, 1, 0]} p={(isParallel ? roofCpe.H : roofCpe.G) - cpi} />
{/* Linhas de Divisão das Zonas no Telhado */}
<mesh position={[0, 0.05, -length / 4]}>
<boxGeometry args={[widthSlope, 0.02, 0.06]} />
<meshStandardMaterial color="#0f172a" roughness={0.5} />
<meshStandardMaterial color="#0f172a" roughness={0.5} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<mesh position={[0, 0.05, 0]}>
<boxGeometry args={[widthSlope, 0.02, 0.06]} />
<meshStandardMaterial color="#0f172a" roughness={0.5} />
<meshStandardMaterial color="#0f172a" roughness={0.5} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<mesh position={[0, 0.05, length / 4]}>
<boxGeometry args={[widthSlope, 0.02, 0.06]} />
<meshStandardMaterial color="#0f172a" roughness={0.5} />
<meshStandardMaterial color="#0f172a" roughness={0.5} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<mesh position={[widthSlope * 0.35, 0.05, 0]}>
<boxGeometry args={[0.06, 0.02, length]} />
<meshStandardMaterial color="#0f172a" roughness={0.5} />
<meshStandardMaterial color="#0f172a" roughness={0.5} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
{/* Rótulo e Título da Água */}
@@ -616,19 +619,21 @@ export function WarehouseModel() {
{/* Cumeeira */}
<mesh position={[0, height + roofHeight + 0.02, 0]}>
<boxGeometry args={[0.08, 0.04, length + 0.1]} />
<meshStandardMaterial color={isDark ? "#475569" : "#2d3748"} roughness={0.5} />
<meshStandardMaterial color={isDark ? "#475569" : "#2d3748"} roughness={0.5} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
{/* Bordas Laterais do Telhado (Beirais) */}
<mesh position={[width / 2 + 0.02, height, 0]}>
<boxGeometry args={[0.04, 0.08, length]} />
<meshStandardMaterial color={isDark ? "#475569" : "#2d3748"} />
<meshStandardMaterial color={isDark ? "#475569" : "#2d3748"} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
<mesh position={[-width / 2 - 0.02, height, 0]}>
<boxGeometry args={[0.04, 0.08, length]} />
<meshStandardMaterial color={isDark ? "#475569" : "#2d3748"} />
<meshStandardMaterial color={isDark ? "#475569" : "#2d3748"} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />
</mesh>
{viewMode === 'airflow' && <AirflowSystem windAngle={windAngle} width={width} length={length} height={height} permeabilityCase={permeabilityCase} />}
{/* === RÓTULOS 3D === */}
{showMainWalls && (
<group>
+105
View File
@@ -0,0 +1,105 @@
import { useRef, useMemo } from 'react';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
interface AirflowSystemProps {
windAngle: number;
width: number;
length: number;
height: number;
permeabilityCase: string;
}
export function AirflowSystem({ windAngle, width, length, height, permeabilityCase }: AirflowSystemProps) {
const count = 300;
const meshRef = useRef<THREE.InstancedMesh>(null);
const particles = useMemo(() => {
const temp = [];
for (let i = 0; i < count; i++) {
temp.push({
position: new THREE.Vector3(
(Math.random() - 0.5) * (width * 3),
Math.random() * height * 2,
(Math.random() - 0.5) * (length * 3)
),
speed: 0.1 + Math.random() * 0.2,
wobbleSpeed: Math.random() * 0.05,
wobbleOffset: Math.random() * Math.PI * 2,
});
}
return temp;
}, [count, width, length, height]);
const dummy = useMemo(() => new THREE.Object3D(), []);
useFrame((state) => {
if (!meshRef.current) return;
const time = state.clock.elapsedTime;
const isParallel = windAngle === 90;
// Wind direction vector
const dir = isParallel ? new THREE.Vector3(0, 0, 1) : new THREE.Vector3(1, 0, 0);
// Bounds
const halfW = width / 2;
const halfL = length / 2;
particles.forEach((p, i) => {
// Move particle
p.position.addScaledVector(dir, p.speed);
// Add slight turbulence
p.position.y += Math.sin(time * p.wobbleSpeed * 10 + p.wobbleOffset) * 0.02;
const isInsideX = p.position.x > -halfW && p.position.x < halfW;
const isInsideZ = p.position.z > -halfL && p.position.z < halfL;
const isInsideY = p.position.y > 0 && p.position.y < height;
const isInsideBuilding = isInsideX && isInsideZ && isInsideY;
if (isInsideBuilding && permeabilityCase === 'airtight') {
// Push particle up to simulate wind going over the roof
p.position.y += 0.2;
}
// Reset if it goes too far
if (isParallel) {
if (p.position.z > halfL + 20) {
p.position.z = -halfL - 20;
p.position.y = Math.random() * height * 1.5;
p.position.x = (Math.random() - 0.5) * (width * 2);
}
} else {
if (p.position.x > halfW + 20) {
p.position.x = -halfW - 20;
p.position.y = Math.random() * height * 1.5;
p.position.z = (Math.random() - 0.5) * (length * 2);
}
}
dummy.position.copy(p.position);
// Orient the particle along the wind direction
dummy.lookAt(p.position.clone().add(dir));
dummy.rotateX(Math.PI / 2); // Cylinder is aligned along Y by default, rotate to face direction
dummy.updateMatrix();
meshRef.current!.setMatrixAt(i, dummy.matrix);
});
meshRef.current.instanceMatrix.needsUpdate = true;
});
return (
<instancedMesh ref={meshRef} args={[undefined, undefined, count]}>
{/* A simple arrow-like or dashed line geometry */}
<cylinderGeometry args={[0.02, 0.02, 1.5, 4]} />
<meshBasicMaterial
color="#a5f3fc"
transparent
opacity={0.6}
blending={THREE.AdditiveBlending}
depthWrite={false}
/>
</instancedMesh>
);
}
+24 -1
View File
@@ -13,7 +13,8 @@ import { Badge } from '@/components/ui/badge';
import { Separator } from '@/components/ui/separator';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { AlertCircle, Lightbulb } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { AlertCircle, Lightbulb, Box, Wind } from 'lucide-react';
import ExportMenu from '../components/ExportMenu';
import { EducationalManual } from '@/components/EducationalManual';
import { PressureLegend } from '@/components/PressureLegend';
@@ -37,6 +38,8 @@ const GalpaoModule: React.FC = () => {
setLength,
setHeight,
setRoofPitch,
viewMode,
setViewMode,
} = useGalpaoStore();
const {
q,
@@ -258,6 +261,26 @@ const GalpaoModule: React.FC = () => {
q = {q.toFixed(3)} kN/m²
</Badge>
</div>
<div className="flex bg-background/80 backdrop-blur-sm rounded-md p-1 shadow-sm border border-border pointer-events-auto ml-auto">
<Button
variant={viewMode === 'solid' ? 'secondary' : 'ghost'}
size="sm"
className="h-7 px-3 text-xs"
onClick={() => setViewMode('solid')}
>
<Box className="w-3.5 h-3.5 mr-1.5" />
Sólido
</Button>
<Button
variant={viewMode === 'airflow' ? 'secondary' : 'ghost'}
size="sm"
className="h-7 px-3 text-xs"
onClick={() => setViewMode('airflow')}
>
<Wind className="w-3.5 h-3.5 mr-1.5" />
Fluxo
</Button>
</div>
<div className="flex flex-wrap gap-2 pointer-events-auto justify-start sm:justify-end">
<EducationalManual type="warehouse" params={{ windAngle, cpi }} />
+4
View File
@@ -10,6 +10,7 @@ interface GalpaoState {
template: GalpaoTemplate;
extraHeight: number; // Altura da platibanda
skirtHeight: number; // Altura da saia
viewMode: 'solid' | 'airflow'; // Modo de visualização 3D
// Dimensões do Galpão Retangular
width: number;
@@ -25,6 +26,7 @@ interface GalpaoState {
setTemplate: (val: GalpaoTemplate) => void;
setExtraHeight: (val: number) => void;
setSkirtHeight: (val: number) => void;
setViewMode: (val: 'solid' | 'airflow') => void;
setWidth: (val: number) => void;
setLength: (val: number) => void;
setHeight: (val: number) => void;
@@ -42,6 +44,7 @@ export const useGalpaoStore = create<GalpaoState>((set, get) => ({
template: 'standard',
extraHeight: 1.5,
skirtHeight: 4.5,
viewMode: 'solid',
width: initWidth,
length: initLength,
height: initHeight,
@@ -61,6 +64,7 @@ export const useGalpaoStore = create<GalpaoState>((set, get) => ({
setTemplate: (val) => set({ template: val }),
setExtraHeight: (val) => set({ extraHeight: val }),
setSkirtHeight: (val) => set({ skirtHeight: val }),
setViewMode: (val) => set({ viewMode: val }),
setWidth: (val) => {
set({ width: val });
+36
View File
@@ -0,0 +1,36 @@
const fs = require('fs');
let code = fs.readFileSync('app/src/components/Warehouse3D.tsx', 'utf8');
// Import AirflowSystem
if (!code.includes('AirflowSystem')) {
code = code.replace("import { useGalpaoStore } from '../store/galpaoStore';", "import { useGalpaoStore } from '../store/galpaoStore';\nimport { AirflowSystem } from './three/AirflowSystem';");
}
// Add viewMode to useGalpaoStore destructuring in WarehouseModel
if (!code.includes('viewMode = useGalpaoStore')) {
code = code.replace("const { width, length, height, roofPitch, wallCpe, roofCpe, template, extraHeight, skirtHeight } = useGalpaoStore();", "const { width, length, height, roofPitch, wallCpe, roofCpe, template, extraHeight, skirtHeight, viewMode } = useGalpaoStore();");
}
// Add viewMode to PressureArrow
if (!code.includes('const { viewMode } = useGalpaoStore();') && code.includes('function PressureArrow')) {
code = code.replace("const { q } = useWindStore();", "const { q } = useWindStore();\n const { viewMode } = useGalpaoStore();\n if (viewMode === 'airflow') return null;");
}
// Make meshStandardMaterial transparent
code = code.replace(/<meshStandardMaterial(?!\s*color=\{color\}\s*transparent\s*opacity=\{0\.6\})([^>]+)>/g, (match, p1) => {
// If it already has transparent, skip
if (match.includes("transparent={viewMode")) return match;
return `<meshStandardMaterial${p1} transparent={viewMode === 'airflow'} opacity={viewMode === 'airflow' ? 0.35 : 1} />`;
});
// Add AirflowSystem to the scene
if (!code.includes('<AirflowSystem')) {
code = code.replace("{/* === RÓTULOS 3D === */}", "{viewMode === 'airflow' && <AirflowSystem windAngle={windAngle} width={width} length={length} height={height} permeabilityCase={permeabilityCase} />}\n\n {/* === RÓTULOS 3D === */}");
}
// Rótulos 3D visibility
if (code.includes('{showMainWalls && (')) {
code = code.replace("{showMainWalls && (", "{showMainWalls && viewMode === 'solid' && (");
}
fs.writeFileSync('app/src/components/Warehouse3D.tsx', code);