import { useState, useMemo } from 'react'; import { usePileCapStore } from '../../store/pileCapStore'; import { calculatePileReactions } from '../../calculations/pileReactions'; import { calculateLoadCombinations, calculateServiceCombinations } from '../../calculations/loadCombinations'; import { DraggableLabel } from './DraggableLabel'; export const FlexuralDiagram = () => { const geometry = usePileCapStore(state => state.geometry); const loads = usePileCapStore(state => state.loads); const design = usePileCapStore(state => state.design); // Global Diagram State const isLocked = usePileCapStore(state => state.diagramState.isLocked); const labelPositions = usePileCapStore(state => state.diagramState.labelPositions); const setDiagramLocked = usePileCapStore(state => state.setDiagramLocked); const updateLabelPosition = usePileCapStore(state => state.updateLabelPosition); const getLabelPos = (id: string, defaultX: number, defaultY: number) => { return labelPositions[id] || { x: defaultX, y: defaultY }; }; const [combType, setCombType] = useState<'service' | 'factored'>('factored'); const [selectedCombName, setSelectedCombName] = useState(''); // Calculate combinations based on type const combinations = useMemo(() => { return combType === 'service' ? calculateServiceCombinations(loads.cases) : calculateLoadCombinations(loads.cases); }, [loads.cases, combType]); // Select default combination useMemo(() => { if (combinations.length > 0) { const exists = combinations.find(c => c.combinationName === selectedCombName); if (!exists) { setSelectedCombName(combinations[0].combinationName); } } }, [combinations, selectedCombName]); const activeCombination = combinations.find(c => c.combinationName === selectedCombName) || combinations[0]; // Calculate reactions for active combination const pileReactions = useMemo(() => { if (!activeCombination) return []; return calculatePileReactions(geometry, activeCombination, design); }, [geometry, activeCombination, design]); // --- Calculations for Flexure --- const columnCenterX = geometry.length / 2; const columnCenterY = geometry.width / 2; const colDimX = geometry.columnLength / 12; // ft const colDimY = geometry.columnWidth / 12; // ft // Critical locations (face of column) const critX = colDimX / 2; // ft from center const critY = colDimY / 2; // ft from center // Calculate Moments let momentX = 0; // Moment about X axis (due to Y distance) - Bending about X let momentZ = 0; // Moment about Z axis (due to X distance) - Bending about Z // Identify piles contributing to moment const pilesContributingX: number[] = []; // Pile IDs const pilesContributingZ: number[] = []; // Pile IDs pileReactions.forEach(pile => { // Bending about X (uses Y distances) const distY = pile.y - columnCenterY; if (Math.abs(distY) > critY) { const leverArm = Math.abs(distY) - critY; if (pile.reaction > 0) { momentX += pile.reaction * leverArm; pilesContributingX.push(pile.pileNumber); } } // Bending about Z (uses X distances) const distX = pile.x - columnCenterX; if (Math.abs(distX) > critX) { const leverArm = Math.abs(distX) - critX; if (pile.reaction > 0) { momentZ += pile.reaction * leverArm; pilesContributingZ.push(pile.pileNumber); } } }); // --- Drawing Logic --- const scale = 30; const padding = 40; const planWidth = geometry.length * scale; const planHeight = geometry.width * scale; const svgWidth = planWidth + 2 * padding; const svgHeight = (planHeight + padding + 40) * 2 + padding; // Two diagrams const originX = padding; const originY1 = padding + 20; // Top diagram (Bending about Z) const originY2 = originY1 + planHeight + padding + 60; // Bottom diagram (Bending about X) const renderDiagram = (yOffset: number, title: string, axis: 'X' | 'Z', moment: number, contributingPiles: number[]) => { // Axis 'Z' means bending about Z axis -> Critical section is vertical lines at +/- critX // Axis 'X' means bending about X axis -> Critical section is horizontal lines at +/- critY const isZ = axis === 'Z'; // const critDist = isZ ? critX : critY; // Shaded regions (contributing areas) // For Z axis: Left and Right of column // For X axis: Top and Bottom of column return ( {/* Title */} {/* Cap Outline */} {/* Shaded Areas */} {isZ ? ( <> {/* Left Area */} {/* Right Area */} ) : ( <> {/* Top Area */} {/* Bottom Area */} )} {/* Centerlines */} {/* Critical Section Lines */} {isZ ? ( <> ) : ( <> )} {/* Column */} {/* Piles */} {pileReactions.map((p, i) => { const px = p.x * scale; const py = (geometry.width - p.y) * scale; const pileRadius = (geometry.pileSize / 12 / 2) * scale; const isContributing = contributingPiles.includes(p.pileNumber); return ( ); })} {/* Dimensions */} {isZ ? ( <> {/* Width of critical section (Column Width) */} ) : ( <> {/* Height of critical section (Column Depth) */} )} {/* Moment Callout */} ); }; return (
{/* Controls */}
{/* Diagram */}
{renderDiagram(originY1, "Bending about Z", 'Z', momentZ, pilesContributingZ)} {renderDiagram(originY2, "Bending about X", 'X', momentX, pilesContributingX)}
); };