🚀 Auto-deploy: BrainWind atualizado em 26/08/2026 11:43:19
This commit is contained in:
@@ -15,7 +15,6 @@ const TowerModule = lazy(() => import('./pages/TowerModule'));
|
|||||||
const PiperackModule = lazy(() => import('./pages/PiperackModule'));
|
const PiperackModule = lazy(() => import('./pages/PiperackModule'));
|
||||||
const ShelterModule = lazy(() => import('./pages/ShelterModule'));
|
const ShelterModule = lazy(() => import('./pages/ShelterModule'));
|
||||||
const CertificatePage = lazy(() => import('./pages/CertificatePage'));
|
const CertificatePage = lazy(() => import('./pages/CertificatePage'));
|
||||||
const CompareModule = lazy(() => import('./pages/Compare'));
|
|
||||||
import {
|
import {
|
||||||
Home, Settings, Menu, Cylinder, Church, CircleDot,
|
Home, Settings, Menu, Cylinder, Church, CircleDot,
|
||||||
Square, Layers, BarChart3, Activity, Warehouse,
|
Square, Layers, BarChart3, Activity, Warehouse,
|
||||||
@@ -377,7 +376,6 @@ function App() {
|
|||||||
<Route path="/calc-ca" element={<CalcCaModule />} />
|
<Route path="/calc-ca" element={<CalcCaModule />} />
|
||||||
<Route path="/settings" element={<SettingsModule />} />
|
<Route path="/settings" element={<SettingsModule />} />
|
||||||
<Route path="/certificado" element={<CertificatePage />} />
|
<Route path="/certificado" element={<CertificatePage />} />
|
||||||
<Route path="/compare" element={<CompareModule />} />
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export function DragCoefficientChart({ l1, l2, h, caLow, caHigh }: DragCoefficie
|
|||||||
|
|
||||||
{/* Fake Curves */}
|
{/* Fake Curves */}
|
||||||
{curves.map((d, idx) => (
|
{curves.map((d, idx) => (
|
||||||
<path key={idx} d={d} fill="none" stroke="currentColor" strokeOpacity={0.2} strokeWidth={1.5} className="text-red-500" />
|
<path key={idx} d={d} fill="none" strokeWidth={1.5} className="stroke-amber-500/40 dark:stroke-amber-400/60" />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Axes */}
|
{/* Axes */}
|
||||||
|
|||||||
@@ -67,11 +67,12 @@ const Heatmap2D: React.FC<Heatmap2DProps> = ({ overrideAngle, overrideWallCpe, o
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full flex flex-col items-center justify-center p-4">
|
<div className="w-full h-full flex flex-col items-center justify-center p-4">
|
||||||
<div className="bg-background rounded-xl p-4 shadow-sm border border-border w-full max-w-2xl">
|
<div className="bg-background rounded-xl p-4 shadow-sm border border-border w-full max-w-2xl flex flex-col max-h-full">
|
||||||
<h3 className="text-center font-semibold mb-2">Planta Baixa - Telhado e Paredes</h3>
|
<h3 className="text-center font-semibold mb-2 shrink-0">Planta Baixa - Telhado e Paredes</h3>
|
||||||
<p className="text-xs text-center text-muted-foreground mb-4">Vento incidindo a {windAngle}°</p>
|
<p className="text-xs text-center text-muted-foreground mb-4 shrink-0">Vento incidindo a {windAngle}°</p>
|
||||||
|
|
||||||
<svg width="100%" height="100%" viewBox={`0 0 ${svgW} ${svgH}`} className="max-w-[500px] mx-auto overflow-visible">
|
<div className="flex-1 min-h-0 relative flex items-center justify-center w-full">
|
||||||
|
<svg width="100%" height="100%" viewBox={`0 0 ${svgW} ${svgH}`} className="max-w-[400px] max-h-[40vh] mx-auto overflow-visible" style={{ maxHeight: 'min(400px, 40vh)' }}>
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="arrow" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
<marker id="arrow" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
||||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor" className="text-foreground" />
|
<path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor" className="text-foreground" />
|
||||||
@@ -81,19 +82,19 @@ const Heatmap2D: React.FC<Heatmap2DProps> = ({ overrideAngle, overrideWallCpe, o
|
|||||||
{/* Vento (Setas Dinâmicas) */}
|
{/* Vento (Setas Dinâmicas) */}
|
||||||
{windAngle === 90 && (
|
{windAngle === 90 && (
|
||||||
<g className="text-blue-500" stroke="currentColor" strokeWidth="3" markerEnd="url(#arrow)">
|
<g className="text-blue-500" stroke="currentColor" strokeWidth="3" markerEnd="url(#arrow)">
|
||||||
<line x1={x0 - 60} y1={cy} x2={x0 - 20} y2={cy} />
|
<line x1={x0 - 100} y1={cy} x2={x0 - 60} y2={cy} />
|
||||||
<line x1={x0 - 60} y1={cy - 40} x2={x0 - 20} y2={cy - 40} />
|
<line x1={x0 - 100} y1={cy - 40} x2={x0 - 60} y2={cy - 40} />
|
||||||
<line x1={x0 - 60} y1={cy + 40} x2={x0 - 20} y2={cy + 40} />
|
<line x1={x0 - 100} y1={cy + 40} x2={x0 - 60} y2={cy + 40} />
|
||||||
<text x={x0 - 70} y={cy} textAnchor="end" className="text-sm font-bold fill-blue-500" stroke="none">Vento (90°)</text>
|
<text x={x0 - 110} y={cy} textAnchor="end" dominantBaseline="middle" className="text-2xl font-bold fill-blue-500" stroke="none">Vento (90°)</text>
|
||||||
</g>
|
</g>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{windAngle === 0 && (
|
{windAngle === 0 && (
|
||||||
<g className="text-blue-500" stroke="currentColor" strokeWidth="3" markerEnd="url(#arrow)">
|
<g className="text-blue-500" stroke="currentColor" strokeWidth="3" markerEnd="url(#arrow)">
|
||||||
<line x1={cx} y1={y0 - 60} x2={cx} y2={y0 - 20} />
|
<line x1={cx} y1={y0 - 100} x2={cx} y2={y0 - 60} />
|
||||||
<line x1={cx - 40} y1={y0 - 60} x2={cx - 40} y2={y0 - 20} />
|
<line x1={cx - 40} y1={y0 - 100} x2={cx - 40} y2={y0 - 60} />
|
||||||
<line x1={cx + 40} y1={y0 - 60} x2={cx + 40} y2={y0 - 20} />
|
<line x1={cx + 40} y1={y0 - 100} x2={cx + 40} y2={y0 - 60} />
|
||||||
<text x={cx} y={y0 - 70} textAnchor="middle" className="text-sm font-bold fill-blue-500" stroke="none">Vento (0°)</text>
|
<text x={cx} y={y0 - 115} textAnchor="middle" className="text-2xl font-bold fill-blue-500" stroke="none">Vento (0°)</text>
|
||||||
</g>
|
</g>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -162,23 +163,24 @@ const Heatmap2D: React.FC<Heatmap2DProps> = ({ overrideAngle, overrideWallCpe, o
|
|||||||
{/* Indicadores de Paredes */}
|
{/* Indicadores de Paredes */}
|
||||||
{windAngle === 0 ? (
|
{windAngle === 0 ? (
|
||||||
<>
|
<>
|
||||||
<text x={cx} y={y0 - 25} textAnchor="middle" className="text-xs font-bold fill-foreground">A</text>
|
<text x={cx} y={y0 - 25} textAnchor="middle" className="text-2xl font-bold fill-foreground">A</text>
|
||||||
<text x={cx} y={y0 + drawL + 25} textAnchor="middle" dominantBaseline="hanging" className="text-xs font-bold fill-foreground">B</text>
|
<text x={cx} y={y0 + drawL + 25} textAnchor="middle" dominantBaseline="hanging" className="text-2xl font-bold fill-foreground">B</text>
|
||||||
<text x={x0 - 25} y={cy} textAnchor="end" dominantBaseline="middle" className="text-xs font-bold fill-foreground">D</text>
|
<text x={x0 - 25} y={cy} textAnchor="end" dominantBaseline="middle" className="text-2xl font-bold fill-foreground">D</text>
|
||||||
<text x={x0 + drawW + 25} y={cy} textAnchor="start" dominantBaseline="middle" className="text-xs font-bold fill-foreground">C</text>
|
<text x={x0 + drawW + 25} y={cy} textAnchor="start" dominantBaseline="middle" className="text-2xl font-bold fill-foreground">C</text>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<text x={x0 - 25} y={cy} textAnchor="end" dominantBaseline="middle" className="text-xs font-bold fill-foreground">A</text>
|
<text x={x0 - 25} y={cy} textAnchor="end" dominantBaseline="middle" className="text-2xl font-bold fill-foreground">A</text>
|
||||||
<text x={x0 + drawW + 25} y={cy} textAnchor="start" dominantBaseline="middle" className="text-xs font-bold fill-foreground">B</text>
|
<text x={x0 + drawW + 25} y={cy} textAnchor="start" dominantBaseline="middle" className="text-2xl font-bold fill-foreground">B</text>
|
||||||
<text x={cx} y={y0 + drawL + 25} textAnchor="middle" dominantBaseline="hanging" className="text-xs font-bold fill-foreground">C</text>
|
<text x={cx} y={y0 + drawL + 25} textAnchor="middle" dominantBaseline="hanging" className="text-2xl font-bold fill-foreground">C</text>
|
||||||
<text x={cx} y={y0 - 25} textAnchor="middle" className="text-xs font-bold fill-foreground">D</text>
|
<text x={cx} y={y0 - 25} textAnchor="middle" className="text-2xl font-bold fill-foreground">D</text>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</svg>
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mt-6 flex flex-wrap justify-center gap-4 text-sm">
|
<div className="mt-4 shrink-0 flex flex-wrap justify-center gap-4 text-xs sm:text-sm">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-4 h-4 rounded" style={{ backgroundColor: getPressureColor(-1.0, cpi) }}></div>
|
<div className="w-4 h-4 rounded" style={{ backgroundColor: getPressureColor(-1.0, cpi) }}></div>
|
||||||
<span>Sucção Máxima</span>
|
<span>Sucção Máxima</span>
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ import { evaluateComfort } from '../comfort';
|
|||||||
import { classifyBridge, type BridgeClassificationInput } from '../modules/bridge';
|
import { classifyBridge, type BridgeClassificationInput } from '../modules/bridge';
|
||||||
import { calculateTower } from '../modules/tower';
|
import { calculateTower } from '../modules/tower';
|
||||||
import { calculatePiperack, type PiperackInput } from '../modules/piperack';
|
import { calculatePiperack, type PiperackInput } from '../modules/piperack';
|
||||||
|
import { calculateVault, type VaultInput } from '../modules/vault';
|
||||||
|
import { calculateDome, type DomeInput } from '../modules/dome';
|
||||||
|
import { calculateIsolatedGableRoof, calculateIsolatedShedRoof } from '../nbr-tables/table-24-25';
|
||||||
|
import { calculateFlatBarForce, type FlatBarForceInput } from '../nbr-tables/table-26';
|
||||||
|
import { applyRoughnessChange } from '../wind-direction';
|
||||||
|
import { TABLE_1 } from '../nbr-tables/table-1';
|
||||||
|
import { getStrouhalNumber, criticalVelocity, scrutonNumber } from '../nbr-tables/table-33';
|
||||||
|
|
||||||
function calcWind(
|
function calcWind(
|
||||||
v0: number, s1: number, s3: number,
|
v0: number, s1: number, s3: number,
|
||||||
@@ -267,6 +274,209 @@ export function generateAllScenarios(): AuditScenario[] {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- NOVOS CENÁRIOS BIBLIOGRÁFICOS EXPANDIDOS (LITERATURA & NBR 6123) ---
|
||||||
|
|
||||||
|
// 1.1 Abóbada Cilíndrica (Blessmann / Tabela 15 NBR 6123)
|
||||||
|
{
|
||||||
|
const vaultIn: VaultInput = { f: 4, l: 20, b: 40, vk: 35, regime: 'laminar-rough', cpi: 0 };
|
||||||
|
const vaultRes = calculateVault(vaultIn);
|
||||||
|
scenarios.push(makeScenario(
|
||||||
|
'blessmann-abobada-f4-l20', 'vault', 'Blessmann: Abóbada Cilíndrica f=4m l=20m b=40m',
|
||||||
|
{ ...vaultIn },
|
||||||
|
{ q: vaultRes.q, fOverL: 4 / 20 },
|
||||||
|
{ ...vaultRes.windPerpendicular, q: vaultRes.q },
|
||||||
|
{ q: { min: 0.5, max: 1.5 } },
|
||||||
|
'vault',
|
||||||
|
{ f: 4, l: 20, b: 40, cpeValues: vaultRes.windPerpendicular },
|
||||||
|
'Sec. 6.2.3 / Tabela 15',
|
||||||
|
true,
|
||||||
|
'Blessmann, J. Ação do Vento em Coberturas Curvas e Abóbadas, Exemplo 3.2 / NBR 6123:2023 Tabela 15',
|
||||||
|
'Abóbada cilíndrica com vão de 20m, flecha de 4m (f/l = 0,20) e comprimento de 40m sob vento perpendicular de 35 m/s. Avaliação da sucção no topo e pressões no bordo de ataque.'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.2 Cúpula Semiesférica apoiada no solo (Rebello / Tabela 21 NBR 6123)
|
||||||
|
{
|
||||||
|
const domeIn: DomeInput = { d: 30, f: 6, vk: 40, type: 'on-ground', cpi: 0 };
|
||||||
|
const domeRes = calculateDome(domeIn);
|
||||||
|
scenarios.push(makeScenario(
|
||||||
|
'rebello-cupula-d30-f6', 'dome', 'Rebello: Cúpula no Solo d=30m f=6m',
|
||||||
|
{ ...domeIn },
|
||||||
|
{ q: domeRes.q, fOverD: domeRes.fOverD },
|
||||||
|
{ cpeBarlavento: domeRes.cpeBarlavento, cpeTopo: domeRes.cpeTopo, liftCoefficient: domeRes.liftCoefficient, liftForceKN: domeRes.liftForceKN },
|
||||||
|
{ q: { min: 0.5, max: 1.8 }, liftForceKN: { min: 50, max: 1000 } },
|
||||||
|
'dome',
|
||||||
|
{ d: 30, f: 6, cpeTopo: domeRes.cpeTopo, cpeBarlavento: domeRes.cpeBarlavento },
|
||||||
|
'Sec. 6.2.4 / Tabela 21',
|
||||||
|
true,
|
||||||
|
'Rebello, Y. A Concepção Estrutural e a Arquitetura / NBR 6123 Tabela 21 (Cúpulas sobre o solo)',
|
||||||
|
'Cúpula esférica implantada diretamente no terreno com diâmetro d=30m e flecha f=6m (f/d = 0,20). Determinação da força global de sustentação aerodinâmica (empuxo ascendente) para velocidade característica de 40 m/s.'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.3 Cobertura Isolada a Duas Águas - Hangar Aberto (Pfeil / Tabela 25 NBR 6123)
|
||||||
|
{
|
||||||
|
const isoIn = { theta: 10, height: 5, depth: 15 };
|
||||||
|
const isoRes = calculateIsolatedGableRoof(isoIn);
|
||||||
|
scenarios.push(makeScenario(
|
||||||
|
'pfeil-cobertura-isolada-2aguas', 'isolated-roof', 'Pfeil: Cobertura Isolada 2 Águas θ=10°',
|
||||||
|
{ ...isoIn, q: 0.85 },
|
||||||
|
{ cpb1: isoRes.cpb.cpb1, cpa1: isoRes.cpa.cpa1 },
|
||||||
|
{ cpb1: isoRes.cpb.cpb1, cpb2: isoRes.cpb.cpb2, cpa1: isoRes.cpa.cpa1, cpa2: isoRes.cpa.cpa2, applies: isoRes.applies ? 1 : 0 },
|
||||||
|
{ cpb1: { min: 0.5, max: 2.0 }, cpa1: { min: -2.0, max: -0.5 } },
|
||||||
|
'isolatedRoof',
|
||||||
|
{ theta: 10, height: 5, depth: 15, cpa: isoRes.cpa, cpb: isoRes.cpb },
|
||||||
|
'Sec. 7.2 / Tabela 25',
|
||||||
|
true,
|
||||||
|
'Pfeil & Pfeil, Estruturas de Aço, 8ª ed., Cap. 5 (Galpões Abertos e Hangares Isolados) / NBR 6123 Tabela 25',
|
||||||
|
'Cobertura isolada simétrica de duas águas com inclinação de 10° e pé-direito livre h=5m (b=15m). Análise dos coeficientes de pressão líquida nos carregamentos extremos de sobrepressão e sucção.'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.4 Cobertura Isolada a Uma Água - Marquise / Abrigo (NBR 6123 Tabela 24)
|
||||||
|
{
|
||||||
|
const shedIn = { theta: 5, height: 3.5, depth: 10 };
|
||||||
|
const shedRes = calculateIsolatedShedRoof(shedIn);
|
||||||
|
scenarios.push(makeScenario(
|
||||||
|
'nbr-marquise-isolada-1agua', 'isolated-roof', 'NBR 6123: Marquise Isolada a Uma Água θ=5°',
|
||||||
|
{ ...shedIn, q: 0.95 },
|
||||||
|
{ cph1High: shedRes.cph1.high, cph1Low: shedRes.cph1.low },
|
||||||
|
{ cph1High: shedRes.cph1.high, cph1Low: shedRes.cph1.low, applies: shedRes.applies ? 1 : 0 },
|
||||||
|
{ cph1High: { min: -1.5, max: 0.0 }, cph1Low: { min: -2.5, max: -0.5 } },
|
||||||
|
'isolatedRoof',
|
||||||
|
{ theta: 5, height: 3.5, depth: 10 },
|
||||||
|
'Sec. 7.2.1 / Tabela 24',
|
||||||
|
true,
|
||||||
|
'NBR 6123:2023, Tabela 24 — Coberturas isoladas a uma água plana / Exemplo Didático CBCA',
|
||||||
|
'Marquise/alpendre isolado a 1 água com inclinação suave de 5° e altura de 3.5m. Verificação dos coeficientes médios e limites de sucção no bordo de ataque.'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.5 Torre Treliçada Triangular de Telecomunicações (NBR 6123 Tabela 29/30 & Blessmann)
|
||||||
|
{
|
||||||
|
const towerIn = { section: 'triangular' as const, barType: 'flat' as const, phi: 0.25, aFace: 90, alphaWind: 0 as const, q: 1.15 };
|
||||||
|
const towerRes = calculateTower(towerIn);
|
||||||
|
scenarios.push(makeScenario(
|
||||||
|
'blessmann-torre-triangular-phi025', 'tower', 'Blessmann: Torre Triangular φ=0.25',
|
||||||
|
{ ...towerIn },
|
||||||
|
{ caEff: towerRes.caEff },
|
||||||
|
{ caEff: towerRes.caEff, forceKN: towerRes.forceKN },
|
||||||
|
{ caEff: { min: 1.5, max: 3.5 }, forceKN: { min: 100, max: 500 } },
|
||||||
|
'tower',
|
||||||
|
{ section: 'triangular', baseWidth: 3, height: 30, panels: 6, phi: 0.25, alphaWind: 0, forceKN: towerRes.forceKN },
|
||||||
|
'Sec. 8.5 / Tabelas 29-30',
|
||||||
|
true,
|
||||||
|
'Blessmann, J. Ação do Vento em Torres e Estruturas Treliçadas, Cap. 7 / NBR 6123:2023 Sec. 8.5',
|
||||||
|
'Torre treliçada de seção triangular equilátera com índice de plenitude φ=0,25 e área projetada da face de 90 m² submetida a vento frontal (0°). Cálculo do coeficiente de arrasto efetivo e da força global na torre.'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.6 Barra Isolada Perfil L / Cantoneira de Torre (Pfeil & NBR 6123 Tabela 26)
|
||||||
|
{
|
||||||
|
const barIn: FlatBarForceInput = { section: 'l', alpha: 45, width: 0.15, length: 3.0, q: 1.05 };
|
||||||
|
const barRes = calculateFlatBarForce(barIn);
|
||||||
|
scenarios.push(makeScenario(
|
||||||
|
'pfeil-cantoneira-l-alpha45', 'bar', 'Pfeil: Cantoneira L150x150 sob Vento a 45°',
|
||||||
|
{ ...barIn },
|
||||||
|
{ cx: barRes.cx, cy: barRes.cy, kFactor: barRes.kFactor },
|
||||||
|
{ fxKN: barRes.fxKN, fyKN: barRes.fyKN, cx: barRes.cx, cy: barRes.cy },
|
||||||
|
{ cx: { min: 1.0, max: 2.2 }, cy: { min: 1.0, max: 2.2 }, fxKN: { min: 0.5, max: 2.0 } },
|
||||||
|
'bar',
|
||||||
|
{ section: 'l', width: 0.15, length: 3.0, alpha: 45, fxKN: barRes.fxKN, fyKN: barRes.fyKN },
|
||||||
|
'Sec. 8.1 / Tabelas 26 e 28',
|
||||||
|
true,
|
||||||
|
'Pfeil, M. & Pfeil, W. Estruturas de Aço, Cap. Ações em Barras Isoladas / NBR 6123 Tabela 26',
|
||||||
|
'Barra prismática isolada de seção cantoneira de abas iguais (L 150x150 mm) com 3 metros de comprimento livre, sob incidência oblíqua do vento a 45°. Determinação das componentes de força Cx, Cy e redução por comprimento finito (Fator K).'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.7 Galpão Extenso — Força de Atrito Superficial (Blessmann Cap. 5 / NBR 6123 Sec. 6.1.5)
|
||||||
|
{
|
||||||
|
const fricIn = { roughness: 'rounded-ribs' as const, length: 80, width: 15, height: 6, roofPitch: 8, q: 1.0, windAngle: 0 as const };
|
||||||
|
const fricRes = calculateFriction(fricIn);
|
||||||
|
scenarios.push(makeScenario(
|
||||||
|
'blessmann-galpao-extenso-atrito', 'galpao', 'Blessmann: Galpão Extenso 80x15m — Atrito Superficial',
|
||||||
|
{ ...fricIn },
|
||||||
|
{ applies: fricRes.applies ? 1 : 0, ratioLh: 80 / 6 },
|
||||||
|
{ applies: fricRes.applies ? 1 : 0, forceKN: fricRes.forceKN, roofArea: fricRes.roofArea, cf: fricRes.cf },
|
||||||
|
{ forceKN: { min: 10, max: 50 }, roofArea: { min: 1000, max: 1500 } },
|
||||||
|
'warehouse',
|
||||||
|
{ width: 15, length: 80, height: 6, roofPitch: 8, windAngle: 0, cpi: 0 },
|
||||||
|
'Sec. 6.1.5',
|
||||||
|
true,
|
||||||
|
'Blessmann, J. O Vento na Engenharia Estrutural, Cap. 5 (Forças de Atrito em Edificações Longas) / NBR 6123 Sec. 6.1.5',
|
||||||
|
'Galpão industrial longo de 80 metros de comprimento por 15m de largura e 6m de altura (l/h = 13,3 > 4). Telhas com nervuras onduladas/arredondadas (Cf = 0,02). Cálculo da força de arrasto por atrito superficial na cobertura.'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.8 Transição de Rugosidade de Terreno — Litoral para Centro Urbano (NBR 6123 Sec. 5.5)
|
||||||
|
{
|
||||||
|
const transIn = { near: 'IV' as TerrainCategory, far: 'I' as TerrainCategory, distance: 500, height: 30 };
|
||||||
|
const params = new Map<TerrainCategory, { b: number; p: number; fr: number }>();
|
||||||
|
(['I', 'II', 'III', 'IV', 'V'] as TerrainCategory[]).forEach(cat => {
|
||||||
|
params.set(cat, TABLE_1[cat].B);
|
||||||
|
});
|
||||||
|
const transRes = applyRoughnessChange(transIn, params);
|
||||||
|
scenarios.push(makeScenario(
|
||||||
|
'nbr-transicao-rugosidade-catI-IV', 'galpao', 'NBR 6123: Transição de Rugosidade Cat I -> Cat IV (x=500m, z=30m)',
|
||||||
|
{ ...transIn },
|
||||||
|
{ z1: transRes.z1, z2: transRes.z2, s2: transRes.s2 },
|
||||||
|
{ s2: transRes.s2, z1: transRes.z1 },
|
||||||
|
{ s2: { min: 0.8, max: 1.3 } },
|
||||||
|
'warehouse',
|
||||||
|
{ width: 20, length: 40, height: 30, roofPitch: 0, windAngle: 0, cpi: 0 },
|
||||||
|
'Sec. 5.5',
|
||||||
|
true,
|
||||||
|
'NBR 6123:2023, Seção 5.5 (Mudança de Rugosidade do Terreno) / Notas de Aula Prof. Blessmann',
|
||||||
|
'Edificação de 30m de altura situada a 500 metros da linha de transição costeira (saída de Terreno Aberto Cat. I para Zona Urbana Densa Cat. IV). Determinação da altura da camada limite interna (z1) e do fator S2 interpolado.'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.9 Desprendimento de Vórtices em Chaminé Esbelta (Strouhal & Scruton — NBR 6123 Sec. 10)
|
||||||
|
{
|
||||||
|
const st = getStrouhalNumber('circle', 0); // 0.20
|
||||||
|
const fn = 1.2; // Hz
|
||||||
|
const d = 1.5; // m
|
||||||
|
const vcr = criticalVelocity(fn, d, st); // 1.2 * 1.5 / 0.20 = 9.0 m/s
|
||||||
|
const sc = scrutonNumber(0.02, 600, 1.225, 1.5); // 0.02 * 600 / (1.225 * 1.5) = 6.53
|
||||||
|
scenarios.push(makeScenario(
|
||||||
|
'nbr-vortices-chamine-strouhal', 'dynamics', 'NBR 6123: Vórtices de Von Kármán em Chaminé Cilíndrica',
|
||||||
|
{ shape: 'circle', diameter: d, naturalFreq: fn, massPerLength: 600, dampingRatio: 0.02 },
|
||||||
|
{ st, vcr, sc },
|
||||||
|
{ st, vcr, sc, isSusceptible: sc < 20 ? 1 : 0 },
|
||||||
|
{ vcr: { min: 5, max: 20 }, sc: { min: 2, max: 20 } },
|
||||||
|
'dynamics',
|
||||||
|
{ height: 45, freq: fn, windSpeed: vcr, scruton: sc, sectionShape: 'circle', sectionSize: d },
|
||||||
|
'Sec. 10 / Tabela 33',
|
||||||
|
true,
|
||||||
|
'NBR 6123:2023, Seção 10 (Efeitos Dinâmicos de Desprendimento de Vórtices) & Simiu & Scanlan',
|
||||||
|
'Chaminé metálica esbelta circular com d=1.5m, frequência natural fundamental fn=1.2 Hz, massa linear de 600 kg/m e amortecimento estrutural ξ=0,02. Cálculo do Número de Strouhal (St=0,20), velocidade crítica de ressonância (Vcr) e Número de Scruton (Sc).'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.10 Pipe-Rack Industrial com Múltiplos Planos (NBR 6123 Sec. 8.4 / Blessmann)
|
||||||
|
{
|
||||||
|
const pipeIn: PiperackInput = {
|
||||||
|
width: 12, height: 5, elevation: 8, spacing: 6, numFrames: 4, phiStruct: 0.20,
|
||||||
|
pipes: [{ id: 'p1', diameter: 0.6, elevationOffset: 5 }, { id: 'p2', diameter: 0.6, elevationOffset: 5 }],
|
||||||
|
q: 1.10
|
||||||
|
};
|
||||||
|
const pipeRes = calculatePiperack(pipeIn);
|
||||||
|
scenarios.push(makeScenario(
|
||||||
|
'blessmann-piperack-4quadros', 'piperack', 'Blessmann: Pipe-Rack Industrial 4 Quadros com Tubulações',
|
||||||
|
{ ...pipeIn },
|
||||||
|
{ phiTotal: pipeRes.phiTotal, effectiveElevation: pipeRes.effectiveElevation },
|
||||||
|
{ caFrontal: pipeRes.caFrontal, eta: pipeRes.eta, globalForce: pipeRes.globalForce },
|
||||||
|
{ caFrontal: { min: 1.5, max: 3.0 }, eta: { min: 0.4, max: 1.0 }, globalForce: { min: 10, max: 300 } },
|
||||||
|
'warehouse',
|
||||||
|
{ width: 12, height: 5, length: 24 },
|
||||||
|
'Sec. 8.4',
|
||||||
|
true,
|
||||||
|
'Blessmann, J. Ação do Vento em Estruturas Industriais e Reticulados / NBR 6123:2023 Sec. 8.4',
|
||||||
|
'Pipe-rack industrial contendo 4 pórticos transversais espaçados de 6m com bandejas de cabos e 2 linhas de tubulação de 600mm de diâmetro. Cálculo do índice de plenitude total (φ), fator de sombreamento (η) e carga de arrasto global.'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// 2. GALPÃO VARREDURA
|
// 2. GALPÃO VARREDURA
|
||||||
const galpaoGeos = [
|
const galpaoGeos = [
|
||||||
{ w: 10, l: 20, h: 4 }, { w: 15, l: 30, h: 6 }, { w: 20, l: 50, h: 8 },
|
{ w: 10, l: 20, h: 4 }, { w: 15, l: 30, h: 6 }, { w: 20, l: 50, h: 8 },
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useGalpaoStore } from '../store/galpaoStore';
|
|
||||||
import { getWallCpeOfficial as getWallCpe, getRoofCpeOfficial as getRoofCpe } from '../lib/coefficients';
|
|
||||||
import Heatmap2D from '../components/Heatmap2D';
|
|
||||||
|
|
||||||
const Compare: React.FC = () => {
|
|
||||||
const { length, width, height, roofPitch } = useGalpaoStore();
|
|
||||||
|
|
||||||
const wallCpe0 = getWallCpe(length, width, height, 0);
|
|
||||||
const roofCpe0 = getRoofCpe(length, width, height, roofPitch, 0);
|
|
||||||
|
|
||||||
const wallCpe90 = getWallCpe(length, width, height, 90);
|
|
||||||
const roofCpe90 = getRoofCpe(length, width, height, roofPitch, 90);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col h-full w-full p-4 lg:p-6 bg-muted/30 overflow-auto">
|
|
||||||
<h1 className="text-2xl font-bold mb-2">Comparação A/B (0° vs 90°)</h1>
|
|
||||||
<p className="text-muted-foreground mb-6">Compare visualmente a distribuição de pressões (Cpe - Cpi) nas duas direções normativas simultaneamente.</p>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 flex-1 min-h-[600px]">
|
|
||||||
<Heatmap2D overrideAngle={0} overrideWallCpe={wallCpe0} overrideRoofCpe={roofCpe0} />
|
|
||||||
<Heatmap2D overrideAngle={90} overrideWallCpe={wallCpe90} overrideRoofCpe={roofCpe90} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Compare;
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import Warehouse3DViewer from '../components/Warehouse3D';
|
import Warehouse3DViewer from '../components/Warehouse3D';
|
||||||
import Heatmap2D from '../components/Heatmap2D';
|
import Heatmap2D from '../components/Heatmap2D';
|
||||||
import LinearLoadsTable from '../components/LinearLoadsTable';
|
import LinearLoadsTable from '../components/LinearLoadsTable';
|
||||||
@@ -8,6 +7,7 @@ import FtoolExportCard from '../components/FtoolExportCard';
|
|||||||
import { CpiSettingsCard } from '../components/CpiSettingsCard';
|
import { CpiSettingsCard } from '../components/CpiSettingsCard';
|
||||||
import { useGalpaoStore } from '../store/galpaoStore';
|
import { useGalpaoStore } from '../store/galpaoStore';
|
||||||
import { useWindStore } from '../store/appStore';
|
import { useWindStore } from '../store/appStore';
|
||||||
|
import { getWallCpeOfficial as getWallCpe, getRoofCpeOfficial as getRoofCpe } from '../lib/coefficients';
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||||
import { Slider } from '@/components/ui/slider';
|
import { Slider } from '@/components/ui/slider';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
@@ -16,7 +16,7 @@ import { Separator } from '@/components/ui/separator';
|
|||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { AlertCircle, Lightbulb, Box, Wind, Columns } from 'lucide-react';
|
import { AlertCircle, Lightbulb, Box, Wind } from 'lucide-react';
|
||||||
import ExportMenu from '../components/ExportMenu';
|
import ExportMenu from '../components/ExportMenu';
|
||||||
import { EducationalManual } from '@/components/EducationalManual';
|
import { EducationalManual } from '@/components/EducationalManual';
|
||||||
import { PressureLegend } from '@/components/PressureLegend';
|
import { PressureLegend } from '@/components/PressureLegend';
|
||||||
@@ -280,25 +280,17 @@ const GalpaoModule: React.FC = () => {
|
|||||||
<TabsList className="h-7 px-1">
|
<TabsList className="h-7 px-1">
|
||||||
<TabsTrigger value="3d" className="text-xs h-6 px-3">Modelo 3D</TabsTrigger>
|
<TabsTrigger value="3d" className="text-xs h-6 px-3">Modelo 3D</TabsTrigger>
|
||||||
<TabsTrigger value="heatmap" className="text-xs h-6 px-3">Heatmap 2D</TabsTrigger>
|
<TabsTrigger value="heatmap" className="text-xs h-6 px-3">Heatmap 2D</TabsTrigger>
|
||||||
|
<TabsTrigger value="compare" className="text-xs h-6 px-3">Modo A/B</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex pointer-events-auto ml-1">
|
|
||||||
<Button asChild size="sm" variant="outline" className="h-7 px-2 text-xs bg-background/80 backdrop-blur-sm border-border shadow-sm">
|
|
||||||
<Link to="/compare">
|
|
||||||
<Columns className="w-3.5 h-3.5 mr-1.5" />
|
|
||||||
Modo A/B
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-2 pointer-events-auto justify-start sm:justify-end ml-auto">
|
<div className="flex flex-wrap gap-2 pointer-events-auto justify-start sm:justify-end ml-auto">
|
||||||
<EducationalManual type="warehouse" params={{ windAngle, cpi }} />
|
<EducationalManual type="warehouse" params={{ windAngle, cpi }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TabsContent value="3d" className="flex-1 m-0 h-full w-full relative">
|
<TabsContent value="3d" className="flex-1 m-0 h-full w-full relative">
|
||||||
<div className="absolute top-4 left-4 z-20 flex bg-background/80 backdrop-blur-sm rounded-md p-1 shadow-sm border border-border pointer-events-auto">
|
<div className="absolute bottom-20 left-4 z-20 flex bg-background/80 backdrop-blur-sm rounded-md p-1 shadow-sm border border-border pointer-events-auto">
|
||||||
<Button
|
<Button
|
||||||
variant={viewMode === 'solid' ? 'secondary' : 'ghost'}
|
variant={viewMode === 'solid' ? 'secondary' : 'ghost'}
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -328,6 +320,21 @@ const GalpaoModule: React.FC = () => {
|
|||||||
<TabsContent value="heatmap" className="flex-1 m-0 h-full w-full bg-muted/10 overflow-auto flex items-center justify-center">
|
<TabsContent value="heatmap" className="flex-1 m-0 h-full w-full bg-muted/10 overflow-auto flex items-center justify-center">
|
||||||
<Heatmap2D />
|
<Heatmap2D />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="compare" className="flex-1 m-0 h-full w-full bg-muted/10 overflow-auto p-4 flex flex-col">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 flex-1">
|
||||||
|
<Heatmap2D
|
||||||
|
overrideAngle={0}
|
||||||
|
overrideWallCpe={getWallCpe(length, width, height, 0)}
|
||||||
|
overrideRoofCpe={getRoofCpe(length, width, height, roofPitch, 0)}
|
||||||
|
/>
|
||||||
|
<Heatmap2D
|
||||||
|
overrideAngle={90}
|
||||||
|
overrideWallCpe={getWallCpe(length, width, height, 90)}
|
||||||
|
overrideRoofCpe={getRoofCpe(length, width, height, roofPitch, 90)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user