🚀 Auto-deploy: BrainWind atualizado em 13/07/2026 18:36:44

This commit is contained in:
2026-07-13 18:36:44 +00:00
parent 3f4352e0b7
commit 5572742d7c
+181 -181
View File
@@ -115,9 +115,108 @@ const PiperackModule: React.FC = () => {
</div> </div>
</header> </header>
<div className="flex-1 overflow-hidden grid lg:grid-cols-[1fr_400px]"> <div className="flex-1 flex flex-col lg:flex-row p-4 gap-4 overflow-hidden bg-muted/30">
{/* Painel Esquerdo: 3D */}
<div className="relative min-h-[400px] lg:min-h-0 bg-muted/30"> {/* Coluna Esquerda: Entradas */}
<div className="w-full lg:w-80 shrink-0 overflow-y-auto bg-card rounded-xl border border-border shadow-sm p-5 space-y-6">
<div>
<h3 className="font-medium mb-4">Geometria da Estrutura</h3>
<div className="space-y-5">
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm font-medium">Largura (b)</span>
<span className="text-sm text-muted-foreground">{width} m</span>
</div>
<Slider value={[width]} min={2} max={25} step={0.5} onValueChange={v => setWidth(v[0])} />
</div>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm font-medium">Altura do pórtico (h)</span>
<span className="text-sm text-muted-foreground">{height} m</span>
</div>
<Slider value={[height]} min={0.5} max={5} step={0.1} onValueChange={v => setHeight(v[0])} />
</div>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm font-medium">Elevação (z)</span>
<span className="text-sm text-muted-foreground">{elevation} m</span>
</div>
<Slider value={[elevation]} min={2} max={25} step={1} onValueChange={v => setElevation(v[0])} />
</div>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm font-medium">Espaçamento (e)</span>
<span className="text-sm text-muted-foreground">{spacing} m</span>
</div>
<Slider value={[spacing]} min={0.5} max={5} step={0.5} onValueChange={v => setSpacing(v[0])} />
</div>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm font-medium">Número de Pórticos (n)</span>
<span className="text-sm text-muted-foreground">{numFrames}</span>
</div>
<Slider value={[numFrames]} min={1} max={5} step={1} onValueChange={v => setNumFrames(v[0])} />
</div>
<div className="space-y-2">
<div className="flex justify-between items-center">
<div className="flex items-center gap-2">
<span className="text-sm font-medium">Índice de Solidez (φ str)</span>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Info className="w-4 h-4 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent side="top" className="max-w-xs">
<p>O índice de solidez (φ) representa a relação entre a área efetivamente bloqueada pelos perfis do reticulado e a área total contornada pelo pórtico. Quanto mais "fechado" for o pórtico estrutural, maior o arrasto aerodinâmico (ref. Tabela 28 da NBR 6123).</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<span className="text-sm text-muted-foreground">{phiStruct.toFixed(2)}</span>
</div>
<Slider value={[phiStruct]} min={0.1} max={0.6} step={0.05} onValueChange={v => setPhiStruct(v[0])} />
</div>
</div>
</div>
<div>
<div className="flex items-center justify-between mb-4">
<h3 className="font-medium">Tubulações (Bloqueio)</h3>
<Button variant="outline" size="sm" onClick={addPipe} className="h-7 text-xs">
<Plus className="w-3 h-3 mr-1" /> Adicionar
</Button>
</div>
{pipes.length === 0 ? (
<div className="text-center p-4 border border-dashed rounded-lg text-sm text-muted-foreground">
Nenhuma tubulação adicionada.
</div>
) : (
<div className="space-y-2">
{pipes.map((pipe, index) => (
<div key={pipe.id} className="flex items-center gap-2 p-2 rounded-md border bg-muted/20">
<label className="w-20 text-xs text-muted-foreground shrink-0">Tubo {index + 1}</label>
<Input
type="number"
value={pipe.diameter}
onChange={(e) => updatePipe(pipe.id, Number(e.target.value))}
step={0.1}
min={0.1}
className="h-7 text-xs font-mono"
/>
<span className="text-xs text-muted-foreground">m</span>
<Button variant="ghost" size="icon" className="h-7 w-7 text-destructive shrink-0" onClick={() => removePipe(pipe.id)}>
<Trash2 className="w-4 h-4" />
</Button>
</div>
))}
</div>
)}
</div>
</div>
{/* Coluna Central: 3D */}
<div className="flex-1 flex flex-col min-h-[500px] lg:min-h-0 bg-background rounded-xl border border-border shadow-sm overflow-hidden relative">
<Piperack3D <Piperack3D
width={width} width={width}
height={height} height={height}
@@ -130,193 +229,94 @@ const PiperackModule: React.FC = () => {
/> />
</div> </div>
{/* Painel Direito: Controles */} {/* Coluna Direita: Resultados */}
<div className="flex flex-col border-l bg-card overflow-y-auto"> <div className="w-full lg:w-96 shrink-0 flex flex-col gap-4 overflow-y-auto pb-8 lg:pb-0">
<div className="p-6 space-y-8"> <Card className="shadow-sm border-border">
<Card className="shadow-none border-primary/20"> <CardHeader className="pb-4">
<CardHeader className="pb-4"> <CardTitle className="text-base flex items-center justify-between">
<CardTitle className="text-base flex items-center justify-between"> Forças Calculadas
Forças Calculadas <Badge variant="secondary" className="font-mono">{result.globalForce.toFixed(1)} kN (Global)</Badge>
<Badge variant="secondary" className="font-mono">{result.globalForce.toFixed(1)} kN (Global)</Badge> </CardTitle>
</CardTitle> </CardHeader>
</CardHeader> <CardContent className="space-y-4 text-sm">
<CardContent className="space-y-4 text-sm"> <div className="flex justify-between items-center pb-2 border-b">
<div className="flex justify-between items-center pb-2 border-b"> <span className="text-muted-foreground">φ efetivo</span>
<span className="text-muted-foreground">φ efetivo</span> <span className="font-mono font-medium">{result.phiTotal.toFixed(2)}</span>
<span className="font-mono font-medium">{result.phiTotal.toFixed(2)}</span> </div>
</div> <div className="flex justify-between items-center pb-2 border-b">
<div className="flex justify-between items-center pb-2 border-b"> <span className="text-muted-foreground">Ca frontal (Tab. 26 adaptada)</span>
<span className="text-muted-foreground">Ca frontal (Tab. 26 adaptada)</span> <span className="font-mono font-medium">{result.caFrontal.toFixed(2)}</span>
<span className="font-mono font-medium">{result.caFrontal.toFixed(2)}</span> </div>
</div> <div className="flex justify-between items-center pb-2 border-b">
<div className="flex justify-between items-center pb-2 border-b"> <span className="text-muted-foreground">Fator η (Tab. 28)</span>
<span className="text-muted-foreground">Fator η (Tab. 28)</span> <span className="font-mono font-medium">{result.eta.toFixed(2)}</span>
<span className="font-mono font-medium">{result.eta.toFixed(2)}</span> </div>
</div> <div className="flex justify-between items-center pb-2 border-b">
<div className="flex justify-between items-center pb-2 border-b"> <span className="text-muted-foreground text-primary font-medium">Carga Pórtico 1</span>
<span className="text-muted-foreground text-primary font-medium">Carga Pórtico 1</span> <span className="font-mono font-bold text-primary">{result.linearLoadFront.toFixed(2)} kN/m</span>
<span className="font-mono font-bold text-primary">{result.linearLoadFront.toFixed(2)} kN/m</span> </div>
</div> <div className="flex justify-between items-center pb-4 border-b">
<div className="flex justify-between items-center pb-4 border-b"> <span className="text-muted-foreground text-primary font-medium">Carga Pórticos {numFrames > 1 ? `2 a ${numFrames}` : '-'}</span>
<span className="text-muted-foreground text-primary font-medium">Carga Pórticos {numFrames > 1 ? `2 a ${numFrames}` : '-'}</span> <span className="font-mono font-bold text-primary">{result.linearLoadBack.toFixed(2)} kN/m</span>
<span className="font-mono font-bold text-primary">{result.linearLoadBack.toFixed(2)} kN/m</span> </div>
</div>
<div className="pt-2">
<div className="pt-2"> <h4 className="font-semibold text-sm mb-3">Reações Simplificadas nas Bases</h4>
<h4 className="font-semibold text-sm mb-3">Reações Simplificadas nas Bases</h4> <div className="bg-muted/30 p-3 rounded-md border text-xs space-y-4">
<div className="bg-muted/30 p-3 rounded-md border text-xs space-y-4">
<svg width="100%" height="120" viewBox="0 0 300 120" className="bg-white rounded border">
<line x1="20" y1="100" x2="280" y2="100" stroke="#cbd5e1" strokeWidth="2" />
<svg width="100%" height="120" viewBox="0 0 300 120" className="bg-white rounded border"> <rect x="80" y="30" width="8" height="70" fill="#64748b" />
<line x1="20" y1="100" x2="280" y2="100" stroke="#cbd5e1" strokeWidth="2" /> <text x="84" y="115" textAnchor="middle" fontSize="10" fill="#475569">C1 (Barlavento)</text>
<line x1="70" y1="100" x2="70" y2="70" stroke="#ef4444" strokeWidth="2" markerEnd="url(#arrowUp)" />
<rect x="80" y="30" width="8" height="70" fill="#64748b" /> <text x="65" y="85" textAnchor="end" fill="#ef4444" fontSize="10">Tração</text>
<text x="84" y="115" textAnchor="middle" fontSize="10" fill="#475569">C1 (Barlavento)</text>
<line x1="70" y1="100" x2="70" y2="70" stroke="#ef4444" strokeWidth="2" markerEnd="url(#arrowUp)" />
<text x="65" y="85" textAnchor="end" fill="#ef4444" fontSize="10">Tração</text>
<rect x="212" y="30" width="8" height="70" fill="#64748b" /> <rect x="212" y="30" width="8" height="70" fill="#64748b" />
<text x="216" y="115" textAnchor="middle" fontSize="10" fill="#475569">C2 (Sotavento)</text> <text x="216" y="115" textAnchor="middle" fontSize="10" fill="#475569">C2 (Sotavento)</text>
<line x1="225" y1="70" x2="225" y2="100" stroke="#3b82f6" strokeWidth="2" markerEnd="url(#arrowDown)" /> <line x1="225" y1="70" x2="225" y2="100" stroke="#3b82f6" strokeWidth="2" markerEnd="url(#arrowDown)" />
<text x="230" y="85" textAnchor="start" fill="#3b82f6" fontSize="10">Comp.</text> <text x="230" y="85" textAnchor="start" fill="#3b82f6" fontSize="10">Comp.</text>
<rect x="76" y="30" width="148" height="10" fill="#64748b" /> <rect x="76" y="30" width="148" height="10" fill="#64748b" />
<line x1="20" y1="35" x2="75" y2="35" stroke="#10b981" strokeWidth="2" markerEnd="url(#arrowRight)" /> <line x1="20" y1="35" x2="75" y2="35" stroke="#10b981" strokeWidth="2" markerEnd="url(#arrowRight)" />
<text x="45" y="30" textAnchor="middle" fill="#10b981" fontSize="10">Vento (F)</text> <text x="45" y="30" textAnchor="middle" fill="#10b981" fontSize="10">Vento (F)</text>
<defs> <defs>
<marker id="arrowUp" markerWidth="6" markerHeight="6" refX="3" refY="0" orient="auto"> <marker id="arrowUp" markerWidth="6" markerHeight="6" refX="3" refY="0" orient="auto">
<path d="M0,6 L3,0 L6,6" fill="none" stroke="#ef4444" strokeWidth="1.5" /> <path d="M0,6 L3,0 L6,6" fill="none" stroke="#ef4444" strokeWidth="1.5" />
</marker> </marker>
<marker id="arrowDown" markerWidth="6" markerHeight="6" refX="3" refY="6" orient="auto"> <marker id="arrowDown" markerWidth="6" markerHeight="6" refX="3" refY="6" orient="auto">
<path d="M0,0 L3,6 L6,0" fill="none" stroke="#3b82f6" strokeWidth="1.5" /> <path d="M0,0 L3,6 L6,0" fill="none" stroke="#3b82f6" strokeWidth="1.5" />
</marker> </marker>
<marker id="arrowRight" markerWidth="6" markerHeight="6" refX="6" refY="3" orient="auto"> <marker id="arrowRight" markerWidth="6" markerHeight="6" refX="6" refY="3" orient="auto">
<path d="M0,0 L6,3 L0,6" fill="none" stroke="#10b981" strokeWidth="1.5" /> <path d="M0,0 L6,3 L0,6" fill="none" stroke="#10b981" strokeWidth="1.5" />
</marker> </marker>
</defs> </defs>
</svg> </svg>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-1">
<div className="font-bold text-primary">Pórtico Frontal</div>
<div className="flex justify-between text-muted-foreground"><span>F (Transv.):</span> <span className="font-mono">{(result.linearLoadFront * width).toFixed(2)} kN</span></div>
<div className="flex justify-between text-muted-foreground"><span>Momento:</span> <span className="font-mono">{(result.linearLoadFront * width * elevation).toFixed(2)} kN·m</span></div>
<div className="flex justify-between text-muted-foreground"><span>Reação (C1/C2):</span> <span className="font-mono">±{((result.linearLoadFront * width * elevation) / spacing).toFixed(2)} kN</span></div>
</div>
{numFrames > 1 && (
<div className="space-y-1"> <div className="space-y-1">
<div className="font-bold text-primary">Pórtico Frontal</div> <div className="font-bold text-primary">Pórticos Traseiros</div>
<div className="flex justify-between text-muted-foreground"><span>F (Transv.):</span> <span className="font-mono">{(result.linearLoadFront * width).toFixed(2)} kN</span></div> <div className="flex justify-between text-muted-foreground"><span>F (Transv.):</span> <span className="font-mono">{(result.linearLoadBack * width).toFixed(2)} kN</span></div>
<div className="flex justify-between text-muted-foreground"><span>Momento:</span> <span className="font-mono">{(result.linearLoadFront * width * elevation).toFixed(2)} kN·m</span></div> <div className="flex justify-between text-muted-foreground"><span>Momento:</span> <span className="font-mono">{(result.linearLoadBack * width * elevation).toFixed(2)} kN·m</span></div>
<div className="flex justify-between text-muted-foreground"><span>Reação (C1/C2):</span> <span className="font-mono">±{((result.linearLoadFront * width * elevation) / spacing).toFixed(2)} kN</span></div> <div className="flex justify-between text-muted-foreground"><span>Reação (C1/C2):</span> <span className="font-mono">±{((result.linearLoadBack * width * elevation) / spacing).toFixed(2)} kN</span></div>
</div> </div>
{numFrames > 1 && ( )}
<div className="space-y-1">
<div className="font-bold text-primary">Pórticos Traseiros</div>
<div className="flex justify-between text-muted-foreground"><span>F (Transv.):</span> <span className="font-mono">{(result.linearLoadBack * width).toFixed(2)} kN</span></div>
<div className="flex justify-between text-muted-foreground"><span>Momento:</span> <span className="font-mono">{(result.linearLoadBack * width * elevation).toFixed(2)} kN·m</span></div>
<div className="flex justify-between text-muted-foreground"><span>Reação (C1/C2):</span> <span className="font-mono">±{((result.linearLoadBack * width * elevation) / spacing).toFixed(2)} kN</span></div>
</div>
)}
</div>
</div>
</div>
</CardContent>
</Card>
<div className="space-y-6">
<div>
<h3 className="font-medium mb-4">Geometria da Estrutura</h3>
<div className="space-y-5">
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm font-medium">Largura (b)</span>
<span className="text-sm text-muted-foreground">{width} m</span>
</div>
<Slider value={[width]} min={2} max={25} step={0.5} onValueChange={v => setWidth(v[0])} />
</div>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm font-medium">Altura do pórtico (h)</span>
<span className="text-sm text-muted-foreground">{height} m</span>
</div>
<Slider value={[height]} min={0.5} max={5} step={0.1} onValueChange={v => setHeight(v[0])} />
</div>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm font-medium">Elevação (z)</span>
<span className="text-sm text-muted-foreground">{elevation} m</span>
</div>
<Slider value={[elevation]} min={2} max={25} step={1} onValueChange={v => setElevation(v[0])} />
</div>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm font-medium">Espaçamento (e)</span>
<span className="text-sm text-muted-foreground">{spacing} m</span>
</div>
<Slider value={[spacing]} min={0.5} max={5} step={0.5} onValueChange={v => setSpacing(v[0])} />
</div>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm font-medium">Número de Pórticos (n)</span>
<span className="text-sm text-muted-foreground">{numFrames}</span>
</div>
<Slider value={[numFrames]} min={1} max={5} step={1} onValueChange={v => setNumFrames(v[0])} />
</div>
<div className="space-y-2">
<div className="flex justify-between items-center">
<div className="flex items-center gap-2">
<span className="text-sm font-medium">Índice de Solidez (φ str)</span>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Info className="w-4 h-4 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent side="top" className="max-w-xs">
<p>O índice de solidez (φ) representa a relação entre a área efetivamente bloqueada pelos perfis do reticulado e a área total contornada pelo pórtico. Quanto mais "fechado" for o pórtico estrutural, maior o arrasto aerodinâmico (ref. Tabela 28 da NBR 6123).</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<span className="text-sm text-muted-foreground">{phiStruct.toFixed(2)}</span>
</div>
<Slider value={[phiStruct]} min={0.1} max={0.6} step={0.05} onValueChange={v => setPhiStruct(v[0])} />
</div> </div>
</div> </div>
</div> </div>
</CardContent>
<div> </Card>
<div className="flex items-center justify-between mb-4">
<h3 className="font-medium">Tubulações (Bloqueio)</h3> <SceneCapturePanel />
<Button variant="outline" size="sm" onClick={addPipe} className="h-7 text-xs">
<Plus className="w-3 h-3 mr-1" /> Adicionar
</Button>
</div>
{pipes.length === 0 ? (
<div className="text-center p-4 border border-dashed rounded-lg text-sm text-muted-foreground">
Nenhuma tubulação adicionada.
</div>
) : (
<div className="space-y-2">
{pipes.map((pipe, index) => (
<div key={pipe.id} className="flex items-center gap-2 p-2 rounded-md border bg-muted/20">
<label className="w-20 text-xs text-muted-foreground shrink-0">Tubo {index + 1}</label>
<Input
type="number"
value={pipe.diameter}
onChange={(e) => updatePipe(pipe.id, Number(e.target.value))}
step={0.1}
min={0.1}
className="h-7 text-xs font-mono"
/>
<span className="text-xs text-muted-foreground">m</span>
<Button variant="ghost" size="icon" className="h-7 w-7 text-destructive shrink-0" onClick={() => removePipe(pipe.id)}>
<Trash2 className="w-4 h-4" />
</Button>
</div>
))}
</div>
)}
</div>
</div>
<SceneCapturePanel />
</div>
</div> </div>
</div> </div>
</div> </div>