function pressureColor(cpe: number, cpi: number): string { const p = cpe - cpi; const intensity = Math.min(1, Math.abs(p) / 1.2); if (p > 0) return `hsl(${215 - intensity * 10}, ${70 + intensity * 25}%, ${Math.max(35, 65 - intensity * 25)}%)`; return `hsl(0, ${70 + intensity * 25}%, ${Math.max(40, 65 - intensity * 20)}%)`; } function cpeColor(cpe: number): string { const clamped = Math.max(-2.5, Math.min(1.5, cpe)); const t = (clamped + 2.5) / 4.0; const h = 240 - t * 240; return `hsl(${h}, 70%, 50%)`; } function forceLen(kN: number): number { return Math.min(Math.max(Math.abs(kN) * 8, 15), 80); } interface WarehouseProps { width: number; length: number; height: number; roofPitch: number; wallCpe: { A: number; B: number; C: number; D: number }; roofCpe: { E: number; F: number; G: number; H: number }; windAngle: 0 | 90; cpi: number; } function WarehouseDiagram({ width, length, height, roofPitch, wallCpe, roofCpe, cpi }: WarehouseProps) { const vw = 400, vh = 300; const s = Math.min((vw - 80) / length, (vh - 100) / (height + (width / 2) * Math.tan((roofPitch * Math.PI) / 180))); const ox = vw / 2, oy = vh - 40; const wS = width * s, lS = length * s, hS = height * s; const roofH = (width / 2) * Math.tan((roofPitch * Math.PI) / 180) * s; const hx = lS / 2, hy = hS; return ( {/* Chão */} {/* Parede frontal */} C {/* Parede lateral esquerda (projeção) */} D {/* Telhado */} G/H {/* Rótulos de dimensão */} L = {length}m h = {height}m {/* Seta de vento */} Vento ); } interface CylinderProps { diameter: number; height: number; cpi: number; cpeProfile: { angle: number; cpe: number }[]; } function CylinderDiagram({ diameter, height, cpeProfile }: CylinderProps) { const vw = 400, vh = 300; const s = Math.min((vw - 100) / diameter, (vh - 80) / height); const cx = vw / 2, cy = vh - 40; const r = (diameter / 2) * s; const h = height * s; return ( {/* Cilindro — perfil lateral com cores */} {cpeProfile.slice(0, -1).map((p, i) => { const next = cpeProfile[i + 1]; const x0 = cx - r + (i / (cpeProfile.length - 1)) * r * 2; const x1 = cx - r + ((i + 1) / (cpeProfile.length - 1)) * r * 2; const mid = (p.cpe + next.cpe) / 2; return ; })} {/* Outline */} {/* Tampa superior */} d = {diameter}m h = {height}m Cpe: {cpeProfile[0]?.cpe.toFixed(1)} (0°) → {cpeProfile[Math.floor(cpeProfile.length / 2)]?.cpe.toFixed(1)} (90°) ); } interface VaultProps { span: number; length: number; rise: number; cpi: number; cpeProfile: Record; } function VaultDiagram({ span, rise, cpeProfile }: VaultProps) { const vw = 400, vh = 300; const s = Math.min((vw - 80) / span, (vh - 80) / rise); const ox = vw / 2, oy = vh - 40; const spanS = span * s, riseS = rise * s; const archPoints: string[] = []; const segments = 32; for (let i = 0; i <= segments; i++) { const t = i / segments; const x = ox - spanS / 2 + t * spanS; const y = oy - riseS * Math.sin(t * Math.PI); archPoints.push(`${x},${y}`); } const zones = [ { idx: 0, label: '1', key: 'zone1' }, { idx: 5, label: '2', key: 'zone2' }, { idx: 11, label: '3', key: 'zone3' }, { idx: 16, label: '4', key: 'zone4' }, { idx: 21, label: '5', key: 'zone5' }, { idx: 27, label: '6', key: 'zone6' }, ]; return ( {/* Arco */} {/* Zonas coloridas */} {zones.map((z, i) => { const nextIdx = i < zones.length - 1 ? zones[i + 1].idx : segments; const pts: string[] = []; for (let j = z.idx; j <= nextIdx; j++) { const t = j / segments; pts.push(`${ox - spanS / 2 + t * spanS},${oy - riseS * Math.sin(t * Math.PI)}`); } const lastT = nextIdx / segments; pts.push(`${ox - spanS / 2 + lastT * spanS},${oy}`); const firstT = z.idx / segments; pts.push(`${ox - spanS / 2 + firstT * spanS},${oy}`); const cpeVal = cpeProfile[z.key] ?? -0.5; const midX = ox - spanS / 2 + ((z.idx + nextIdx) / 2 / segments) * spanS; const midY = oy - riseS * 0.6; return ( {z.label} ); })} vão = {span}m flecha = {rise}m ); } interface DomeProps { diameter: number; rise: number; wallHeight: number; cpi: number; cpeBarlavento: number; cpeTopo: number; cpeLateral: number; } function DomeDiagram({ diameter, rise, wallHeight, cpeBarlavento, cpeTopo }: DomeProps) { const vw = 400, vh = 300; const s = Math.min((vw - 80) / diameter, (vh - 80) / (wallHeight + rise)); const cx = vw / 2, cy = vh - 40; const r = (diameter / 2) * s; const wh = wallHeight * s; const rh = rise * s; return ( {/* Parede cilíndrica */} {/* Cúpula — 3 zonas */} {/* Labels */} Barlavento Topo Lateral d = {diameter}m | h = {wallHeight}m | flecha = {rise}m ); } interface SignProps { length: number; height: number; groundClearance: number; alpha: number; cf: number; forceKN: number; applicationPoint: number; } function SignDiagram({ length, height, groundClearance, cf, forceKN }: SignProps) { const vw = 400, vh = 300; const s = Math.min((vw - 100) / length, (vh - 80) / (height + groundClearance)); const cx = vw / 2, ground = vh - 40; const gc = groundClearance * s; const h = height * s; const w = length * s; const plateY = ground - gc - h; return ( {/* Placa */} {/* Suporte */} Cf = {cf.toFixed(2)} {/* Seta de força */} {forceKN > 0 && ( {forceKN.toFixed(1)} kN )} {/* Dimensões */} ℓ = {length}m | h = {height}m | e = {groundClearance}m ); } interface BarProps { barType: 'flat' | 'circular'; section?: string; diameter?: number; width?: number; length: number; alpha: number; fxKN: number; fyKN: number; cx: number; } function BarDiagram({ barType, length, alpha, fxKN, fyKN, cx: cxVal }: BarProps) { const vw = 400, vh = 300; const cx = vw / 2, cy = vh / 2; const barLen = Math.min(length * 8, vw - 100); const forceMag = Math.sqrt(fxKN * fxKN + fyKN * fyKN); const forceAngle = Math.atan2(fyKN, fxKN); void barType; return ( {/* Barra */} {barType === 'circular' ? `d=${length}m` : `ℓ=${length}m`} {/* Eixo */} {/* Seta de força */} {forceMag > 0.01 && ( {forceMag.toFixed(1)} kN )} α = {alpha}° | Cx = {cxVal.toFixed(2)} ); } interface BridgeProps { lp: number; width: number; deckHeight: number; heg: number; cx: number; cz: number; fxPerLength: number; fzPerLength: number; } function BridgeDiagram({ lp, width, deckHeight, heg, cx: cxVal, fxPerLength, fzPerLength }: BridgeProps) { const vw = 400, vh = 300; const s = Math.min((vw - 80) / lp, (vh - 80) / (deckHeight + heg)); const ox = vw / 2, ground = vh - 40; const lpS = lp * s; const dh = deckHeight * s; const deckT = Math.max(heg, 0.8) * s; return ( {/* Água/solo */} {/* Pilares */} {[-0.35, 0, 0.35].map((frac, i) => ( ))} {/* Tabuleiro */} Cx = {cxVal.toFixed(2)} {/* Guarda-rodas */} {/* Setas de força */} {fxPerLength !== 0 && ( )} {fzPerLength !== 0 && ( )} Lp = {lp}m | B = {width}m | z = {deckHeight}m ); } interface TowerProps { section: 'square' | 'triangular'; baseWidth: number; height: number; panels: number; phi: number; alphaWind: number; forceKN: number; } function TowerDiagram({ baseWidth, height, panels, phi, forceKN }: TowerProps) { const vw = 400, vh = 300; const s = Math.min((vw - 80) / baseWidth, (vh - 80) / height); const cx = vw / 2, ground = vh - 40; const bw = baseWidth * s; const h = height * s; const lines: React.ReactElement[] = []; for (let p = 0; p < panels; p++) { const y0 = ground - (p / panels) * h; const y1 = ground - ((p + 1) / panels) * h; const shrink = p / panels; const nextShrink = (p + 1) / panels; const w0 = bw * (1 - shrink * 0.6); const w1 = bw * (1 - nextShrink * 0.6); // Montantes lines.push(); lines.push(); // Diagonais lines.push(); lines.push(); // Travessa lines.push(); } return ( {lines} {/* Seta de força */} {forceKN > 0 && ( {forceKN.toFixed(1)} kN )} h = {height}m | base = {baseWidth}m | φ = {phi.toFixed(2)} ); } interface IsolatedRoofProps { type: 'shed' | 'gable'; theta: number; height: number; width: number; depth: number; cpeWindward: number; cpeLeeward: number; cpeTop: number; forceKN: number; } function IsolatedRoofDiagram({ type, theta, height, width, depth, cpeWindward, cpeLeeward, cpeTop, forceKN }: IsolatedRoofProps) { const vw = 400, vh = 300; const s = Math.min((vw - 100) / depth, (vh - 80) / (height + depth * Math.tan((theta * Math.PI) / 180))); const cx = vw / 2, ground = vh - 40; const h = height * s; const d = depth * s; const rise = d * Math.tan((theta * Math.PI) / 180); return ( {/* Pilares */} {type === 'gable' && } {/* Cobertura */} {type === 'shed' ? ( ) : ( <> )} {/* Labels de zona */} Barl. {cpeWindward.toFixed(1)} Sot. {cpeLeeward.toFixed(1)} Topo {cpeTop.toFixed(1)} {/* Seta de força */} {forceKN > 0 && ( {forceKN.toFixed(1)} kN )} θ = {theta}° | h = {height}m | b = {width}m | l = {depth}m ); } interface DynamicsProps { height: number; freq: number; windSpeed: number; scruton: number; sectionShape: string; sectionSize: number; showVortexStreet: boolean; showModeShape: boolean; } function DynamicsDiagram({ height, freq, scruton, sectionShape, sectionSize, showVortexStreet, showModeShape }: DynamicsProps) { const vw = 400, vh = 300; const s = Math.min((vw - 100) / sectionSize, (vh - 80) / height); const cx = vw / 2, ground = vh - 40; const h = height * s; const w = sectionSize * s; void sectionShape; return ( {/* Estrutura */} {sectionShape === 'circle' ? ( ) : ( )} {/* Modo de oscilação */} {showModeShape && ( )} {/* Rua de vórtices */} {showVortexStreet && ( {[0.2, 0.4, 0.6, 0.8, 1.0].map((_, i) => ( ))} )} {/* Seta de vento */} Vento h = {height}m | f₁ = {freq}Hz | Sc = {scruton.toFixed(1)} ); } export interface PiperackProps { width: number; height: number; elevation: number; spacing: number; numFrames: number; phiTotal: number; eta: number; } function PiperackDiagram({ width, height, elevation, spacing, numFrames, phiTotal, eta }: PiperackProps) { const vw = 400, vh = 300; const s = Math.min((vw - 80) / width, (vh - 80) / (elevation + height)); const cx = vw / 2, ground = vh - 40; const w = width * s; const h = height * s; const elev = elevation * s; void spacing; void eta; return ( {/* Colunas */} {/* Viga */} {/* Seta de vento */} Vento b = {width}m | h = {height}m | z = {elevation}m | φ = {phiTotal.toFixed(2)} | n = {numFrames} ); } export type FallbackDiagramProps = | { type: 'warehouse'; props: WarehouseProps } | { type: 'cylinder'; props: CylinderProps } | { type: 'vault'; props: VaultProps } | { type: 'dome'; props: DomeProps } | { type: 'sign'; props: SignProps } | { type: 'bar'; props: BarProps } | { type: 'bridge'; props: BridgeProps } | { type: 'tower'; props: TowerProps } | { type: 'isolatedRoof'; props: IsolatedRoofProps } | { type: 'dynamics'; props: DynamicsProps } | { type: 'piperack'; props: PiperackProps }; export default function FallbackDiagram(input: FallbackDiagramProps) { return (
{input.type === 'warehouse' && } {input.type === 'cylinder' && } {input.type === 'vault' && } {input.type === 'dome' && } {input.type === 'sign' && } {input.type === 'bar' && } {input.type === 'bridge' && } {input.type === 'tower' && } {input.type === 'isolatedRoof' && } {input.type === 'dynamics' && } {input.type === 'piperack' && }
); }