From 7463788dba06b16ef84eb323e8ba4f7feab8260d Mon Sep 17 00:00:00 2001 From: Marcos Date: Sun, 12 Jul 2026 16:42:11 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20Auto-deploy:=20BrainWind=20atual?= =?UTF-8?q?izado=20em=2012/07/2026=2016:42:11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/components/three/Vault3D.tsx | 142 +++++++++++++++++++-------- app/src/pages/VaultModule.tsx | 20 +++- 2 files changed, 121 insertions(+), 41 deletions(-) diff --git a/app/src/components/three/Vault3D.tsx b/app/src/components/three/Vault3D.tsx index 143ec51..0125ad1 100644 --- a/app/src/components/three/Vault3D.tsx +++ b/app/src/components/three/Vault3D.tsx @@ -12,6 +12,8 @@ export interface Vault3DInput { rise: number; cpi: number; cpeProfile: Record; + cpeParallel?: Record; + viewDirection?: 'perpendicular' | 'parallel'; } function vaultColor(cpe: number, cpi: number, isDark: boolean): THREE.Color { @@ -24,10 +26,11 @@ function vaultColor(cpe: number, cpi: number, isDark: boolean): THREE.Color { return new THREE.Color(`hsl(0, ${70 + intensity * 25}%, ${Math.max(40, l + 5)}%)`); } -function VaultModel({ span, length, rise, cpi, cpeProfile }: Vault3DInput) { +function VaultModel({ span, length, rise, cpi, cpeProfile, cpeParallel, viewDirection = 'perpendicular' }: Vault3DInput) { const theme = useCanvasTheme(); const isDark = theme === 'dark'; const segments = 64; + const points = useMemo(() => { const pts: THREE.Vector3[] = []; for (let i = 0; i <= segments; i++) { @@ -39,20 +42,6 @@ function VaultModel({ span, length, rise, cpi, cpeProfile }: Vault3DInput) { return pts; }, [span, rise, segments]); - // Divide em zonas (1, 2, 3, 4, 5, 6) - const zones = useMemo(() => { - const arr: { cpe: number; startIdx: number; endIdx: number }[] = []; - const zoneSize = segments / 6; - for (let z = 0; z < 6; z++) { - const startIdx = Math.floor(z * zoneSize); - const endIdx = Math.floor((z + 1) * zoneSize); - const key = `zone${z + 1}`; - arr.push({ cpe: cpeProfile[key] ?? -0.5, startIdx, endIdx }); - } - return arr; - }, [cpeProfile, segments]); - - // Shape para fechar os tímpanos (paredes frontais/traseiras em arco) const archShape = useMemo(() => { const s = new THREE.Shape(); s.moveTo(-span / 2, 0); @@ -67,34 +56,109 @@ function VaultModel({ span, length, rise, cpi, cpeProfile }: Vault3DInput) { return s; }, [span, rise, segments]); - return ( - - {/* Casca da Abóbada */} - {zones.map((zone, idx) => { - const color = vaultColor(zone.cpe, cpi, isDark); - const verts: number[] = []; - for (let i = zone.startIdx; i <= zone.endIdx; i++) { - verts.push(points[i].x, points[i].y, points[i].z); - verts.push(points[i].x, points[i].y, length); - } - const indices: number[] = []; - for (let i = 0; i < (zone.endIdx - zone.startIdx); i++) { - const a = i * 2; - const b = i * 2 + 1; - const c = i * 2 + 2; - const d = i * 2 + 3; - indices.push(a, b, c, b, d, c); - } - return ( - + const renderPerpendicular = () => { + const zones = []; + const zoneSize = segments / 6; + for (let z = 0; z < 6; z++) { + const startIdx = Math.floor(z * zoneSize); + const endIdx = Math.floor((z + 1) * zoneSize); + const key = `zone${z + 1}`; + zones.push({ cpe: cpeProfile[key] ?? -0.5, startIdx, endIdx, label: `${z + 1}` }); + } + + return zones.map((zone, idx) => { + const color = vaultColor(zone.cpe, cpi, isDark); + const verts: number[] = []; + for (let i = zone.startIdx; i <= zone.endIdx; i++) { + verts.push(points[i].x, points[i].y, points[i].z); + verts.push(points[i].x, points[i].y, length); + } + const indices: number[] = []; + for (let i = 0; i < (zone.endIdx - zone.startIdx); i++) { + const a = i * 2; + const b = i * 2 + 1; + const c = i * 2 + 2; + const d = i * 2 + 3; + indices.push(a, b, c, b, d, c); + } + + const midIdx = Math.floor((zone.startIdx + zone.endIdx) / 2); + const midPt = points[midIdx]; + + return ( + + - ); - })} + + {zone.label} + + + ); + }); + }; + + const renderParallel = () => { + const zones = ['A', 'B', 'C', 'D']; + const zStep = length / 4; + + return zones.map((label, idx) => { + const zStart = idx * zStep; + const zEnd = (idx + 1) * zStep; + const cpe = cpeParallel?.[label] ?? -0.5; + const color = vaultColor(cpe, cpi, isDark); + + const verts: number[] = []; + for (let i = 0; i <= segments; i++) { + verts.push(points[i].x, points[i].y, zStart); + verts.push(points[i].x, points[i].y, zEnd); + } + const indices: number[] = []; + for (let i = 0; i < segments; i++) { + const a = i * 2; + const b = i * 2 + 1; + const c = i * 2 + 2; + const d = i * 2 + 3; + indices.push(a, b, c, b, d, c); + } + + return ( + + + + + + + + + + {label} + + + ); + }); + }; + + return ( + + {viewDirection === 'perpendicular' ? renderPerpendicular() : renderParallel()} {/* Tímpano Traseiro (Z = 0) */} @@ -122,7 +186,7 @@ function VaultModel({ span, length, rise, cpi, cpeProfile }: Vault3DInput) { ); } -export default function Vault3DViewer({ span, length, rise, cpi, cpeProfile }: Vault3DInput) { +export default function Vault3DViewer({ span, length, rise, cpi, cpeProfile, cpeParallel, viewDirection = 'perpendicular' }: Vault3DInput) { const theme = useCanvasTheme(); const isDark = theme === 'dark'; @@ -150,7 +214,7 @@ export default function Vault3DViewer({ span, length, rise, cpi, cpeProfile }: V shadow-mapSize-width={1024} shadow-mapSize-height={1024} /> - + { const [rise, setRise] = React.useState(5); const [regime, setRegime] = React.useState('laminar-rough'); const [localCpi, setLocalCpi] = React.useState(0); + const [viewDirection, setViewDirection] = React.useState<'perpendicular' | 'parallel'>('perpendicular'); const result = useMemo(() => calculateVault({ f: rise, l: span, b: length, vk, regime, cpi: localCpi }), [span, length, rise, vk, regime, localCpi]); @@ -128,12 +130,26 @@ const VaultModule: React.FC = () => { Cpi = {localCpi > 0 ? `+${localCpi.toFixed(2)}` : localCpi.toFixed(2)} -
+
+ setViewDirection(v as 'perpendicular' | 'parallel')} className="bg-background/80 backdrop-blur-sm rounded-md shadow-sm border border-border"> + + Vento ⊥ + Vento ∥ + +
- +