🚀 Auto-deploy: BrainWind atualizado em 13/07/2026 18:25:51
This commit is contained in:
@@ -520,33 +520,44 @@ export interface PiperackProps {
|
||||
eta: number;
|
||||
}
|
||||
|
||||
function PiperackDiagram({ width, height, elevation, spacing, numFrames, phiTotal, eta }: PiperackProps) {
|
||||
function PiperackDiagram({ width, height, elevation, spacing, numFrames, phiTotal }: PiperackProps) {
|
||||
const vw = 400, vh = 300;
|
||||
const s = Math.min((vw - 80) / width, (vh - 80) / (elevation + height));
|
||||
// Vista lateral (corte transversal do pipe-rack)
|
||||
// A largura visual nesta vista é a distância entre a primeira e a última fileira de colunas
|
||||
const crossSectionWidth = Math.max(spacing * (numFrames - 1), 1);
|
||||
const s = Math.min((vw - 120) / crossSectionWidth, (vh - 80) / (elevation + height));
|
||||
const cx = vw / 2, ground = vh - 40;
|
||||
const w = width * s;
|
||||
const h = height * s;
|
||||
const elev = elevation * s;
|
||||
const wS = crossSectionWidth * s;
|
||||
const hS = height * s;
|
||||
const elevS = elevation * s;
|
||||
|
||||
void spacing;
|
||||
void eta;
|
||||
const columns = [];
|
||||
for (let i = 0; i < numFrames; i++) {
|
||||
const x = cx - wS / 2 + (numFrames > 1 ? (i / (numFrames - 1)) * wS : 0);
|
||||
columns.push(
|
||||
<line key={i} x1={x} y1={ground} x2={x} y2={ground - elevS - hS} stroke="#475569" strokeWidth={4} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<svg viewBox={`0 0 ${vw} ${vh}`} className="w-full h-full">
|
||||
<rect width={vw} height={vh} fill="none" />
|
||||
<line x1={cx - w - 20} y1={ground} x2={cx + w + 20} y2={ground} stroke="#94a3b8" strokeWidth={1} />
|
||||
{/* Colunas */}
|
||||
<line x1={cx - w / 2} y1={ground} x2={cx - w / 2} y2={ground - elev - h} stroke="#475569" strokeWidth={3} />
|
||||
<line x1={cx + w / 2} y1={ground} x2={cx + w / 2} y2={ground - elev - h} stroke="#475569" strokeWidth={3} />
|
||||
{/* Viga */}
|
||||
<rect x={cx - w / 2} y={ground - elev - h} width={w} height={h} fill="#3b82f6" opacity={Math.max(0.2, phiTotal)} stroke="#334155" strokeWidth={1.5} />
|
||||
<line x1={cx - wS / 2 - 50} y1={ground} x2={cx + wS / 2 + 50} y2={ground} stroke="#94a3b8" strokeWidth={1} />
|
||||
|
||||
{/* Seta de vento */}
|
||||
{/* Colunas no corte transversal */}
|
||||
{columns}
|
||||
|
||||
{/* Tubulações e vigas transversais */}
|
||||
<rect x={cx - wS / 2} y={ground - elevS - hS} width={wS} height={hS} fill="#3b82f6" opacity={Math.max(0.2, phiTotal)} stroke="#334155" strokeWidth={1.5} />
|
||||
|
||||
{/* Seta de vento atingindo o primeiro pórtico */}
|
||||
<defs><marker id="parrow" markerWidth={8} markerHeight={6} refX={8} refY={3} orient="auto"><polygon points="0,0 8,3 0,6" fill="#22c55e" /></marker></defs>
|
||||
<line x1={30} y1={ground - elev - h / 2} x2={Math.max(35, cx - w / 2 - 20)} y2={ground - elev - h / 2} stroke="#22c55e" strokeWidth={2} markerEnd="url(#parrow)" />
|
||||
<text x={(30 + Math.max(35, cx - w / 2 - 20)) / 2} y={ground - elev - h / 2 - 8} textAnchor="middle" fontSize={8} fill="#22c55e">Vento</text>
|
||||
<line x1={cx - wS / 2 - 40} y1={ground - elevS - hS / 2} x2={cx - wS / 2 - 5} y2={ground - elevS - hS / 2} stroke="#22c55e" strokeWidth={2} markerEnd="url(#parrow)" />
|
||||
<text x={cx - wS / 2 - 25} y={ground - elevS - hS / 2 - 8} textAnchor="middle" fontSize={10} fill="#22c55e" fontWeight="bold">Vento</text>
|
||||
|
||||
<text x={cx} y={ground + 18} textAnchor="middle" fontSize={9} fill="#475569">b = {width}m | h = {height}m | z = {elevation}m | φ = {phiTotal.toFixed(2)} | n = {numFrames}</text>
|
||||
{/* Labels informativas sobre a vista */}
|
||||
<text x={cx} y={ground + 16} textAnchor="middle" fontSize={9} fill="#475569">Vista Lateral (Corte) — O vento atinge a face de comprimento b={width}m</text>
|
||||
<text x={cx} y={ground + 28} textAnchor="middle" fontSize={9} fill="#475569">Espaçamento (e) = {spacing}m | Pórticos paralelos ao vento (n) = {numFrames}</text>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,44 +35,63 @@ function PiperackModel({
|
||||
const steelColor = isDark ? '#94a3b8' : '#64748b';
|
||||
const pipeColors = ['#ef4444', '#3b82f6', '#f59e0b', '#10b981', '#8b5cf6'];
|
||||
|
||||
// Gera os pórticos
|
||||
// Gera os pórticos longitudinais (as fileiras de colunas)
|
||||
const frames = [];
|
||||
const totalSpacing = spacing * (numFrames > 1 ? numFrames - 1 : 0);
|
||||
const zOffset = totalSpacing / 2;
|
||||
|
||||
for (let i = 0; i < numFrames; i++) {
|
||||
const z = -i * spacing;
|
||||
const z = -zOffset + i * spacing;
|
||||
|
||||
// Simplificando o visual: desenha 5 colunas distribuídas ao longo da largura (comprimento do pipe-rack)
|
||||
const numCols = Math.max(2, Math.floor(width / 4) + 1);
|
||||
for (let c = 0; c < numCols; c++) {
|
||||
const colX = -halfW + (c / (numCols - 1)) * width;
|
||||
frames.push(
|
||||
<mesh key={`col-${i}-${c}`} position={[colX, rackTop / 2, z]} receiveShadow castShadow>
|
||||
<boxGeometry args={[0.3, rackTop, 0.3]} />
|
||||
<meshStandardMaterial color={steelColor} metalness={0.6} roughness={0.4} />
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
// Viga Longitudinal da fileira
|
||||
frames.push(
|
||||
<group key={`frame-${i}`} position={[0, 0, z]}>
|
||||
{/* Coluna Esquerda */}
|
||||
<mesh position={[-halfW, rackTop / 2, 0]} receiveShadow castShadow>
|
||||
<boxGeometry args={[0.3, rackTop, 0.3]} />
|
||||
<meshStandardMaterial color={steelColor} metalness={0.6} roughness={0.4} />
|
||||
</mesh>
|
||||
{/* Coluna Direita */}
|
||||
<mesh position={[halfW, rackTop / 2, 0]} receiveShadow castShadow>
|
||||
<boxGeometry args={[0.3, rackTop, 0.3]} />
|
||||
<meshStandardMaterial color={steelColor} metalness={0.6} roughness={0.4} />
|
||||
</mesh>
|
||||
{/* Viga Transversal (Reticulado simplificado) */}
|
||||
<mesh position={[0, elevation, 0]} receiveShadow castShadow>
|
||||
<boxGeometry args={[width, height, 0.3]} />
|
||||
<meshStandardMaterial color={steelColor} metalness={0.6} roughness={0.4} transparent opacity={phiTotal} />
|
||||
</mesh>
|
||||
</group>
|
||||
<mesh key={`beam-${i}`} position={[0, elevation, z]} receiveShadow castShadow>
|
||||
<boxGeometry args={[width, height, 0.3]} />
|
||||
<meshStandardMaterial color={steelColor} metalness={0.6} roughness={0.4} transparent opacity={phiTotal} />
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
// Gera as tubulações correndo longitudinalmente
|
||||
const rackLength = spacing * Math.max(numFrames - 1, 0.5); // Garante comprimento visual mesmo com 1 pórtico
|
||||
const pipeZCenter = -(spacing * (numFrames - 1)) / 2;
|
||||
|
||||
// Vigas transversais conectando as fileiras nas pontas
|
||||
if (numFrames > 1) {
|
||||
frames.push(
|
||||
<mesh key={`cross-1`} position={[-halfW, elevation, 0]} receiveShadow castShadow>
|
||||
<boxGeometry args={[0.3, height, totalSpacing]} />
|
||||
<meshStandardMaterial color={steelColor} metalness={0.6} roughness={0.4} transparent opacity={phiTotal} />
|
||||
</mesh>
|
||||
);
|
||||
frames.push(
|
||||
<mesh key={`cross-2`} position={[halfW, elevation, 0]} receiveShadow castShadow>
|
||||
<boxGeometry args={[0.3, height, totalSpacing]} />
|
||||
<meshStandardMaterial color={steelColor} metalness={0.6} roughness={0.4} transparent opacity={phiTotal} />
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
// Gera as tubulações correndo longitudinalmente (ao longo de X)
|
||||
const pipeMeshes = pipes.map((pipe, idx) => {
|
||||
// Distribui os tubos ao longo da largura do pórtico
|
||||
const xPos = -halfW + 0.5 + (idx % Math.max(pipes.length, 1)) * (width - 1) / Math.max(pipes.length - 1, 1) || 0;
|
||||
const yPos = elevation + (idx % 2 === 0 ? 0.2 : -0.2); // Leve variação de altura
|
||||
// Distribui os tubos ao longo do espaçamento transversal (Z)
|
||||
const zPos = numFrames > 1
|
||||
? -zOffset + 0.5 + (idx % Math.max(pipes.length, 1)) * (totalSpacing - 1) / Math.max(pipes.length - 1, 1) || 0
|
||||
: 0;
|
||||
const yPos = elevation + height/2 + pipe.diameter/2 + (idx % 2 === 0 ? 0 : 0.2);
|
||||
const color = pipeColors[idx % pipeColors.length];
|
||||
|
||||
return (
|
||||
<mesh key={`pipe-${pipe.id}`} position={[xPos, yPos, pipeZCenter]} rotation={[Math.PI / 2, 0, 0]} receiveShadow castShadow>
|
||||
<cylinderGeometry args={[pipe.diameter / 2, pipe.diameter / 2, rackLength + 1, 16]} />
|
||||
<mesh key={`pipe-${pipe.id}`} position={[0, yPos, zPos]} rotation={[0, 0, Math.PI / 2]} receiveShadow castShadow>
|
||||
<cylinderGeometry args={[pipe.diameter / 2, pipe.diameter / 2, width + 1, 16]} />
|
||||
<meshStandardMaterial color={color} metalness={0.3} roughness={0.2} />
|
||||
</mesh>
|
||||
);
|
||||
@@ -86,7 +105,7 @@ function PiperackModel({
|
||||
{/* Vento batendo lateralmente (transversal ao Pipe-rack) */}
|
||||
<WindArrow
|
||||
direction={[0, 0, 1]}
|
||||
target={[0, elevation, spacing * 0.5]}
|
||||
target={[0, elevation, zOffset + 2]}
|
||||
/>
|
||||
</group>
|
||||
);
|
||||
@@ -121,8 +140,8 @@ export function Piperack3D(props: Piperack3DInput) {
|
||||
minPolarAngle={0}
|
||||
maxPolarAngle={Math.PI / 2 - 0.05}
|
||||
minDistance={5}
|
||||
maxDistance={50}
|
||||
target={[0, props.elevation / 2, -(props.spacing * (props.numFrames - 1)) / 2]}
|
||||
maxDistance={80}
|
||||
target={[0, props.elevation / 2, 0]}
|
||||
/>
|
||||
</SceneCanvas>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user