import { Document, Page, Text, View, StyleSheet, Image as PdfImage, pdf } from '@react-pdf/renderer'; import { useGalpaoStore } from '../store/galpaoStore'; import { useWindStore } from '../store/appStore'; import { useCaptureStore } from '../store/captureStore'; import { getColumnLinearLoads, getRoofLinearLoads, getAllPillarBaseReactions, getDragForce, } from './line-loads'; import { getWallCpeOfficial, getRoofCpeOfficial } from './coefficients'; const styles = StyleSheet.create({ page: { flexDirection: 'column', padding: 40, fontSize: 10, fontFamily: 'Helvetica', color: '#333', }, header: { marginBottom: 20, borderBottom: '2pt solid #6b21a8', paddingBottom: 10, }, title: { fontSize: 20, fontWeight: 'bold', color: '#6b21a8' }, subtitle: { fontSize: 10, color: '#666', marginTop: 4 }, section: { marginTop: 15, marginBottom: 10 }, sectionTitle: { fontSize: 14, fontWeight: 'bold', marginBottom: 8, color: '#111' }, row: { flexDirection: 'row', marginBottom: 4 }, label: { width: 200, fontWeight: 'bold' }, value: { flex: 1 }, table: { display: 'flex', flexDirection: 'column', marginTop: 10, borderTop: '1pt solid #ccc', borderLeft: '1pt solid #ccc', }, tableRow: { flexDirection: 'row' }, tableHeader: { backgroundColor: '#f3f4f6', fontWeight: 'bold' }, tableCell: { flex: 1, padding: 5, borderRight: '1pt solid #ccc', borderBottom: '1pt solid #ccc', textAlign: 'center', }, tableCellFirst: { flex: 1, padding: 5, borderRight: '1pt solid #ccc', borderBottom: '1pt solid #ccc', textAlign: 'left', }, footer: { position: 'absolute', bottom: 30, left: 40, right: 40, textAlign: 'center', color: '#999', fontSize: 8, borderTop: '1pt solid #eaeaea', paddingTop: 10, }, sceneImage: { width: 480, height: 270, objectFit: 'contain', marginVertical: 8, border: '1pt solid #ddd', }, sceneCaption: { fontSize: 8, color: '#666', fontStyle: 'italic', textAlign: 'center', marginBottom: 8, }, }); interface ReportProps { galpao: ReturnType; wind: ReturnType; sceneImage?: string | null; } const ReportDocument = ({ galpao, wind, sceneImage }: ReportProps) => { const pressure = (cpe: number) => (wind.q * (cpe - wind.cpi)).toFixed(3); const cpi = wind.cpi.toFixed(2); const fmtSigned = (v: number, p = 3) => (v >= 0 ? `+${v.toFixed(p)}` : v.toFixed(p)); const FRAME_SPACING = 6.0; const PURLIN_SPACING = 1.5; return ( VentoApp — Memória de Cálculo Cargas de Vento em Galpão — NBR 6123:2023 1. Parâmetros Globais do Vento e Pressão Dinâmica Velocidade Básica (V₀): {wind.v0} m/s (conforme Figura 1 e Anexo C da NBR 6123) Fator Topográfico (S₁): {wind.s1} (conforme Seção 5.2) Fator de Rugosidade (S₂): {wind.s2.toFixed(3)} (Categoria {wind.terrainCategory}, Classe {wind.structureClass}, conforme Tabela 2) Fator Estatístico (S₃): {wind.s3.toFixed(2)} (Grupo {wind.s3Group}, conforme Tabela 4) Memória de Cálculo (Sec 4.2 e 4.3): Vₖ = V₀ × S₁ × S₂ × S₃ Vₖ = {wind.v0} × {wind.s1} × {wind.s2.toFixed(3)} × {wind.s3.toFixed(2)} = {wind.vk.toFixed(2)} m/s q = 0,613 × (Vₖ)² q = 0,613 × ({wind.vk.toFixed(2)})² = {(0.613 * Math.pow(wind.vk, 2) / 1000).toFixed(4)} kN/m² 2. Geometria do Galpão Largura (b): {galpao.width} m Comprimento (a): {galpao.length} m Altura do Pé-direito (h): {galpao.height} m Inclinação do Telhado (θ): {galpao.roofPitch}° Direção do Vento Analisada: {wind.windAngle}° 3. Pressão Interna (sec. 6.3) Caso de Permeabilidade: {wind.permeabilityCase} Coeficiente Cpi: {cpi} {[0, 90].map((angle, index) => { const wCpe = getWallCpeOfficial(galpao.length, galpao.width, galpao.height, angle as 0 | 90); const rCpe = getRoofCpeOfficial(galpao.length, galpao.width, galpao.height, galpao.roofPitch, angle as 0 | 90); const colLoads = getColumnLinearLoads(wind.cpi, wind.q, wCpe, FRAME_SPACING, angle as 0 | 90); const rLoads = getRoofLinearLoads(wind.cpi, wind.q, rCpe, PURLIN_SPACING, galpao.roofPitch); const rxns = getAllPillarBaseReactions(colLoads, galpao.height); const dForce = getDragForce(wCpe, rCpe, wind.q, galpao.length, galpao.width, galpao.height, galpao.roofPitch, angle as 0 | 90); const secBase = index === 0 ? 4 : 6; return ( Cenário: Vento a {angle}° {secBase}. Coeficientes e Pressões (q × (Cpe − Cpi)) Elemento / Região Cpe Cpi p [kN/m²] {Object.entries(wCpe).map(([face, cpeVal]) => ( Parede — {face} {(cpeVal as number).toFixed(2)} {cpi} {pressure(cpeVal as number)} ))} {Object.entries(rCpe).map(([face, cpeVal]) => ( Telhado — {face} {(cpeVal as number).toFixed(2)} {cpi} {pressure(cpeVal as number)} ))} {secBase + 1}. Cargas Lineares (kN/m) Pórticos: {FRAME_SPACING} m | Terças: {PURLIN_SPACING} m Pilar Cpe w [kN/m] {([ ['Barlavento', angle === 0 ? wCpe.C : wCpe.A, colLoads.windward], ['Sotavento', angle === 0 ? wCpe.D : wCpe.B, colLoads.leeward], ['Lateral 1', angle === 0 ? wCpe.A : wCpe.C, colLoads.sideA], ['Lateral 2', angle === 0 ? wCpe.B : wCpe.D, colLoads.sideB], ] as const).map(([label, cpeVal, w]) => ( {label} {cpeVal.toFixed(2)} {fmtSigned(w)} ))} Terça (Zona) Cpe w [kN/m] {(['E', 'F', 'G', 'H', 'I', 'J'] as const).map((z) => ( {z} {rCpe[z].toFixed(2)} {fmtSigned(rLoads[z])} ))} Reação global na base: {fmtSigned(rxns.total, 3)} kN Força de arrasto global (Cₐ): {dForce.forceKN.toFixed(3)} kN ); })} {sceneImage && ( 8. Modelo 3D (M9.3 — Captura de Cena) Vista isométrica capturada em tempo real pelo projetista na interface web. Cores indicam intensidade de pressão (azul: empuxo, vermelho: sucção). )} Gerado por VentoApp — Ferramenta de Auxílio ao Cálculo Estrutural (NBR 6123:2023) ); }; export async function exportGalpaoToPDF() { const galpao = useGalpaoStore.getState(); const wind = useWindStore.getState(); const sceneImage = useCaptureStore.getState().capturedImage; const blob = await pdf( , ).toBlob(); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.setAttribute('href', url); link.setAttribute('download', 'memoria_calculo_vento.pdf'); document.body.appendChild(link); link.click(); document.body.removeChild(link); }