import React, { useMemo, useState } from 'react'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Slider } from '@/components/ui/slider'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Badge } from '@/components/ui/badge'; import { Separator } from '@/components/ui/separator'; import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'; import { Input } from '@/components/ui/input'; import { useWindStore } from '@/store/appStore'; import { getDynamicParams, estimateFundamentalFrequency, calculateVp, dynamicFactor, getStrouhalNumber, criticalVelocity, vortexDispenseCheck, scrutonNumber, isVortexSusceptible, evaluateComfort, maxAcceleration, type StructureDynamicType, type SectionShape, } from '@/lib/modules/dynamics'; import { calculateS2 } from '@/lib/wind-kernel'; import { TABLE_32 } from '@/lib/nbr-tables/table-32'; import Dynamics3DViewer from '@/components/three/Dynamics3D'; import SceneCapturePanel from '../components/SceneCapturePanel'; import ExportMenu from '../components/ExportMenu'; import { EducationalManual } from '@/components/EducationalManual'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; import { WindParametersSummary } from '@/components/WindParametersSummary'; import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { usePersistentState } from '@/hooks/usePersistentState'; const DynamicsModule: React.FC = () => { const { v0, s1, s3, terrainCategory, structureClass, q } = useWindStore(); const [activeTab, setActiveTab] = usePersistentState('DynamicsModule_activeTab', 'edificio'); const [structureType, setStructureType] = React.useState('concrete-shearwall'); const [height, setHeight] = React.useState(40); const [freq, setFreq] = React.useState(0.5); const [uMax, setUMax] = React.useState(0.01); const [useType, setUseType] = React.useState<'residential' | 'commercial'>('commercial'); const [sectionShape, setSectionShape] = React.useState('rectangle-b-a-1-2'); const [sectionRatio, setSectionRatio] = React.useState(1); const [sectionSize, setSectionSize] = React.useState(0.5); const [massEq, setMassEq] = React.useState(2000); const dynParams = useMemo(() => getDynamicParams(structureType), [structureType]); const estimatedFreq = useMemo(() => estimateFundamentalFrequency(structureType, height), [structureType, height]); const vp = useMemo(() => calculateVp(v0, s3), [v0, s3]); const s2 = useMemo(() => calculateS2(height, terrainCategory, structureClass), [height, terrainCategory, structureClass]); const dynFactor = useMemo( () => dynamicFactor({ category: terrainCategory, vp, freq, height, xi: dynParams.xiPercent, }), [terrainCategory, vp, freq, height, dynParams.xiPercent], ); const st = useMemo(() => getStrouhalNumber(sectionShape, sectionRatio), [sectionShape, sectionRatio]); const vcr = useMemo(() => criticalVelocity(freq, sectionSize, st), [freq, sectionSize, st]); const scrutin = useMemo(() => scrutonNumber(dynParams.xiPercent / 100, massEq, 1.226, sectionSize), [dynParams.xiPercent, massEq, sectionSize]); const okVortex = useMemo(() => vortexDispenseCheck(vcr, v0, s1, s2, s3), [vcr, v0, s1, s2, s3]); const aMax = useMemo(() => maxAcceleration(freq, uMax), [freq, uMax]); const comfort = useMemo(() => evaluateComfort({ freq, aMax, use: useType }), [freq, aMax, useType]); const handleExportPDF = () => { const sections: GenericPDFSection[] = [ { title: 'Geometria e Parâmetros Base (Dinâmica)', type: 'grid', gridItems: [ { label: 'Tipo', value: structureType }, { label: 'Altura (h)', value: `${height} m` }, { label: 'Frequência (f)', value: `${freq.toFixed(2)} Hz` }, { label: 'Freq. Estimada (Tab. 31)', value: `${estimatedFreq.toFixed(2)} Hz` }, { label: 'γ (Gama)', value: dynParams.gamma.toString() }, { label: 'ξ (Amortecimento)', value: `${dynParams.xiPercent}%` }, ], }, { title: 'Resposta Dinâmica (Sec. 9.3)', type: 'grid', gridItems: [ { label: 'Vₚ', value: `${vp.toFixed(2)} m/s` }, { label: 'Fator ζ', value: dynFactor.toFixed(3) }, { label: `S₂(z=${height}m)`, value: s2.toFixed(3) }, { label: 'q₀', value: `${q.toFixed(4)} kN/m²` }, ], }, { title: 'Desprendimento de Vórtices (Sec. 10)', type: 'grid', gridItems: [ { label: 'Forma da seção', value: sectionShape }, { label: 'Strouhal (St)', value: st.toFixed(3) }, { label: 'Vcr (Crítica)', value: `${vcr.toFixed(2)} m/s` }, { label: 'Scruton (Sc)', value: scrutin.toFixed(1) }, { label: 'Susceptível', value: isVortexSusceptible(scrutin) ? 'Sim' : 'Não' }, { label: 'Dispensa (Sec 10.2)', value: okVortex ? 'Sim (OK)' : 'Não (REVER)' }, ], }, { title: 'Conforto Humano (ISO 10137)', type: 'grid', gridItems: [ { label: 'Uso', value: useType === 'residential' ? 'Residencial' : 'Comercial' }, { label: 'Desloc. uₘₐₓ', value: `${(uMax * 1000).toFixed(2)} mm` }, { label: 'a_max (Pico)', value: `${aMax.toFixed(4)} m/s²` }, { label: 'a_lim (Limite)', value: `${comfort.aLim.toFixed(4)} m/s²` }, { label: 'Status', value: comfort.ok ? 'OK' : 'REVER' }, ], }, { title: 'Demonstração dos Passos Matemáticos — Análise Dinâmica e Vórtices', type: 'math-card', mathSteps: [ { title: '1. Fator de Amplificação Dinâmica (ξ) ou Coeficiente de Rajada', formula: 'ξ = f(f₁, T₀, β, amortecimento) (NBR 6123 Sec. 9 / Anexo A)', calculation: `Para frequência f₁ = ${freq} Hz e amortecimento ζ = ${dynParams.xiPercent}%, fator de resposta dinâmica calculado = ${dynFactor.toFixed(3)}`, result: `ξ = ${dynFactor.toFixed(3)}`, note: 'Amplifica as cargas estáticas para levar em conta a ressonância gerada pela turbulência atmosférica.', }, { title: '2. Velocidade Crítica de Desprendimento de Vórtices (Vcr)', formula: 'Vcr = (f₁ · L) / St [m/s] (Sec. 10 / Tabela 31 NBR 6123)', calculation: `(${freq} Hz · ${sectionSize} m) / ${st.toFixed(3)} (Strouhal) = ${vcr.toFixed(2)} m/s`, result: `Vcr = ${vcr.toFixed(2)} m/s`, note: 'Velocidade de vento na qual a frequência de desprendimento de vórtices coincide com a frequência própria da estrutura.', }, { title: '3. Número de Scruton (Sc) e Suscetibilidade Aerodinâmica', formula: 'Sc = (2 · me · δ) / (ρ · L²) (Análise de amortecimento aerodinâmico)', calculation: `Com massa equivalente ${massEq} kg/m e amortecimento, Scruton calculado = ${scrutin.toFixed(1)}`, result: `Sc = ${scrutin.toFixed(1)}`, note: 'Estruturas com baixo Scruton (geralmente < 15) são suscetíveis a fortes vibrações transversais por vórtices.', }, { title: '4. Aceleração de Pico no Topo (a_max) vs Conforto Humano', formula: 'a_max = (2π · f₁)² · u_max [m/s²] (ISO 10137 / NBR 6123 Anexo A)', calculation: `Aceleração de pico = ${aMax.toFixed(4)} m/s² vs Limite aceitável = ${comfort.aLim.toFixed(4)} m/s²`, result: `a_max = ${aMax.toFixed(4)} m/s² (${comfort.ok ? 'OK' : 'REVER'})`, note: 'Verificação do bem-estar dos ocupantes em edifícios altos submetidos a vibrações induzidas pelo vento.', }, ], }, ]; exportGenericToPDF('Análise Dinâmica', sections); }; return (
{/* Coluna Esquerda: Controles */}
Edifício Vórtices Conforto
Análise Dinâmica Contínua Sec. 9.3 / Tab. 31 e 32
{height} m
setHeight(v[0])} />
{freq.toFixed(2)} Hz
setFreq(v[0])} />
f₁ estimado (Tab. 31):{estimatedFreq.toFixed(2)} Hz
γ:{dynParams.gamma}
ξ:{dynParams.xiPercent}%
Desprendimento de Vórtices Sec. 10 — Vcr, Scruton, St
{sectionRatio.toFixed(2)}
setSectionRatio(v[0])} />
{sectionSize} m
setSectionSize(v[0])} />
setMassEq(Number(e.target.value))} />
Conforto Humano Sec. 9.6 — ISO 10137
{(uMax * 1000).toFixed(2)} mm
setUMax(v[0])} />
{/* Coluna Central: Gráficos e 3D */}
{activeTab === 'edificio' && (
Perfil de Velocidade Fator Dinâmico ζ
)} Visualização 3D
{/* Coluna Direita: Resultados */}
Parâmetros Dinâmicos
Vₚ:{vp.toFixed(2)} m/s
ζ (fator):{dynFactor.toFixed(3)}
S₂(z={height}m):{s2.toFixed(3)}
q₀:{q.toFixed(4)} kN/m²
Análise de Vórtices
Strouhal (St):{st.toFixed(3)}
Vcr (Crítica):{vcr.toFixed(2)} m/s
Scruton (Sc):{scrutin.toFixed(1)}
Susceptível:{isVortexSusceptible(scrutin) ? 'sim' : 'não'}
Dispensa (sec 10.2): {okVortex ? 'OK' : 'REVER'}
Verificação de Conforto
a_max (Pico):{aMax.toFixed(4)} m/s²
a_lim (Limite):{comfort.aLim.toFixed(4)} m/s²
a / a_lim:{comfort.ratio.toFixed(2)}
Status: {comfort.ok ? 'OK' : 'REVER'}

{comfort.description}

); }; const DynamicProfileChart: React.FC<{ q: number; height: number; category: 'I' | 'II' | 'III' | 'IV' | 'V' }> = ({ q, height, category }) => { const points = []; const { bm, p } = TABLE_32[category]; for (let z = 0; z <= height; z += height / 30) { const factor = bm * Math.pow(z / height, p); points.push({ z, value: q * factor }); } const maxValue = Math.max(...points.map((p) => p.value)); const pathData = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${(p.z / height) * 300},${160 - (p.value / maxValue) * 140}`).join(' '); return ( z (m) → {height.toFixed(0)} m q(z) (kN/m²) ); }; const DynamicFactorChart: React.FC<{ category: 'I' | 'II' | 'III' | 'IV' | 'V'; height: number; vp: number; freq: number }> = ({ category, height, vp, freq }) => { const points = []; for (let xi = 0.5; xi <= 5; xi += 0.5) { const factor = dynamicFactor({ category, vp, freq, height, xi }); points.push({ xi, factor }); } const maxFactor = Math.max(...points.map((p) => p.factor), 5); const pathData = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${((p.xi - 0.5) / 4.5) * 300},${160 - (p.factor / maxFactor) * 140}`).join(' '); return ( ξ (%) ζ ); }; export default DynamicsModule;