import { hookLength, NOMINAL_DIAMETER, type BarSize } from './common'; /** * 확대기초 철근 B1–B8, B10–B12 (plan C5). Source: RAG/확대기초철근 (1).md, 2026-10-22 최종 정리. * B9 (전단철근) is not computed yet. Lengths in mm, heights in mm from the 확대기초 하면. */ export const FOOTING_DEFAULTS = { /** 피복 상면 / 측면 / 하면 (최외측 철근 중심까지). */ cTop: 100, cSide: 100, cBottom: 150, /** 첫 철근 최소 여유. */ eMin: 150, /** 격자 실간격 s. */ s: 125, /** 하면 1단과 2단의 간격 Δ_2. */ delta2: 100, /** B10 높이 간격 s_v and 조립 단 배수 n. */ sV: 200, n: 2, /** B1·B3 leg and B5·B7 leg (proposed values; Q-B5 open). */ legB1: 300, legB3: 230, legB5: 343, legB7: 230, } as const; export interface GridResult { /** 격자 칸 수 n = floor((A − 2·e_min)/s); 위치 수 = n + 1. */ n: number; /** 격자 길이 G = n·s. */ G: number; /** 양끝 여유 e = (A − G)/2. */ e: number; /** 위치 수 n + 1. */ positions: number; } /** Grid along one side of the footing (§3.1). side = W for B1–B4, L for B5–B8. */ export function grid(side: number, s: number = FOOTING_DEFAULTS.s, cSide: number = FOOTING_DEFAULTS.cSide, eMin: number = FOOTING_DEFAULTS.eMin): GridResult { const A = side - 2 * cSide; const n = Math.floor((A - 2 * eMin) / s); const G = n * s; const e = (A - G) / 2; return { n, G, e, positions: n + 1 }; } const d = (size: BarSize) => NOMINAL_DIAMETER[size]; /** Layer heights (§4.2), mm from the 하면. H = footing height. */ export interface Layers { B5: number; B1: number; B24: number; B68: number; B3: number; B7: number; } export function layers(H: number): Layers { const { cTop, cBottom, delta2 } = FOOTING_DEFAULTS; const zB5 = H - cTop; const zB1 = zB5 - (d('S29') + d('S25')) / 2; const zB24 = cBottom; const zB68 = cBottom + (d('S25') + d('S29')) / 2; return { B5: zB5, B1: zB1, B24: zB24, B68: zB68, B3: zB24 + delta2, B7: zB68 + delta2 }; } /** Lengths of the B1–B4 family along the length L (§5.2). */ export function legB2(H: number): number { const z = layers(H); return z.B1 - z.B24; // 1723 at H 2000 } export interface FootingBar { name: 'B1' | 'B2' | 'B3' | 'B4' | 'B5' | 'B6' | 'B7' | 'B8' | 'B10-1' | 'B10-2' | 'B11' | 'B12'; size: BarSize; /** Bars in all (over the footing). */ count: number; /** Length of one bar, mm (straight part + legs). */ length: number; } /** B1–B4 (§5): the grid along W, with lengths from L. */ export function bottomBars(L: number, W: number, H: number): FootingBar[] { const g = grid(W); const n = g.positions; const N4 = Math.ceil(n / 2); // B4 at even i const N2 = Math.floor(n / 2); // B2 at odd i const l0 = L - 2 * FOOTING_DEFAULTS.cSide; const leg2 = legB2(H); return [ { name: 'B1', size: 'S25', count: N2, length: l0 + 2 * FOOTING_DEFAULTS.legB1 }, { name: 'B2', size: 'S25', count: N2, length: l0 + 2 * leg2 }, { name: 'B3', size: 'S19', count: N2, length: l0 + 2 * FOOTING_DEFAULTS.legB3 }, { name: 'B4', size: 'S25', count: N4, length: l0 }, ]; } /** B5–B8 (§6): the grid along L, with lengths from W. */ export function crossBars(L: number, W: number, H: number): FootingBar[] { const g = grid(L); const n = g.positions; const N8 = Math.ceil(n / 2); // B8 at even i const N6 = Math.floor(n / 2); // B6, B5, B7 at odd i const l0 = W - 2 * FOOTING_DEFAULTS.cSide; const z = layers(H); const leg6 = z.B5 - z.B68; return [ { name: 'B5', size: 'S29', count: N6, length: l0 + 2 * FOOTING_DEFAULTS.legB5 }, { name: 'B6', size: 'S29', count: N6, length: l0 + 2 * leg6 }, { name: 'B7', size: 'S19', count: N6, length: l0 + 2 * FOOTING_DEFAULTS.legB7 }, { name: 'B8', size: 'S29', count: N8, length: l0 }, ]; } /** B10-1 (on W sides) and B10-2 (on L sides): m + 1 levels at s_v, two faces each (§7). */ export function sideBars(L: number, W: number): FootingBar[] { const depth1 = 123.85; // B10-1 depth from the side const depth2 = 122.25; // B10-2 depth from the side const levels = 9; // 8 @ 200 (기본) const hook = hookLength('main90', d('S19')); return [ { name: 'B10-1', size: 'S19', count: levels * 2, length: L - 2 * depth2 + 2 * hook }, { name: 'B10-2', size: 'S19', count: levels * 2, length: W - 2 * depth1 + 2 * hookLength('main90', d('S19')) }, ]; } /** B11 (along W, between the B10-2 levels) and B12 (along L), 4s apart with a 2s stagger per level (§8). */ export function assemblyBars(L: number, W: number): FootingBar[] { const levelsJ = [2, 4, 6]; // j = n, 2n, 3n … inside the B10 levels (n = 2) const perLevel = (n: number, startOdd: number, startEven: number) => levelsJ.map((_, idx) => { const i0 = idx % 2 === 0 ? startOdd : startEven; return Math.floor((n - i0) / 4) + 1; }); const nW = grid(W).positions - 1; // 61 const nL = grid(L).positions - 1; // 79 // B11 is spread along L (n = 79) and B12 along W (n = 61); each level has its own count. const b11 = perLevel(nL, 0, 2).reduce((a, b) => a + b, 0); const b12 = perLevel(nW, 0, 2).reduce((a, b) => a + b, 0); const hook = hookLength('main90', d('S16')); return [ { name: 'B11', size: 'S16', count: b11, length: W - 2 * 141.35 + 2 * hook }, { name: 'B12', size: 'S16', count: b12, length: L - 2 * 139.75 + 2 * hook }, ]; } /** * B9 전단철근 (확대기초철근 (1).md §9). Wraps B5 (top outer) and B2·B4 (bottom outer), H16 @500 staggered. * Plane positions and hooks are proposed rules (Q-B18, Q-B19). */ export function shearB9(L: number, W: number, H: number): { rows: number; perRow: number[]; count: number; length: number } { const nW = grid(W).n; // 61 const nL = grid(L).n; // 79 const rowIndices: number[] = []; for (let i = 1; i <= nW; i += 4) rowIndices.push(i); // Even-numbered rows start at i_L = 1, odd-numbered rows at i_L = 3 (2s stagger). const perRow = rowIndices.map((_, r) => { const start = r % 2 === 0 ? 1 : 3; return Math.floor((nL - start) / 4) + 1; }); const count = perRow.reduce((a, b) => a + b, 0); const lv = layers(H).B5 - layers(H).B24; // 1750 const lh = hookLength('stirrup135', d('H16')); return { rows: rowIndices.length, perRow, count, length: lv + 2 * lh }; } /** Leg offsets from the grid line (§5.3, §6.3): the B1/B3 and B5/B7 legs sit beside the outer leg. */ export function legOffsets(): { B1: number; B3: number; B5: number; B7: number } { const dB2 = NOMINAL_DIAMETER.S25; const dB1 = NOMINAL_DIAMETER.S25; const dB3 = NOMINAL_DIAMETER.S19; const dB6 = NOMINAL_DIAMETER.S29; const dB5 = NOMINAL_DIAMETER.S29; const dB7 = NOMINAL_DIAMETER.S19; return { B1: (dB2 + dB1) / 2, B3: (dB2 + dB3) / 2, B5: (dB6 + dB5) / 2, B7: (dB6 + dB7) / 2, }; } /** B11/B12 heights (§8.1): B10 levels at 150 + k·s_v; the assembly levels are j = n, 2n, 3n (n = 2), and B12 sits d_B10 above B11. */ export function assemblyHeights(): { B11: number[]; B12: number[] } { const { cBottom } = FOOTING_DEFAULTS; const sV = FOOTING_DEFAULTS.sV; const n = FOOTING_DEFAULTS.n; const js = [n, 2 * n, 3 * n]; const B11 = js.map((j) => cBottom + j * sV); const B12 = B11.map((z) => z + NOMINAL_DIAMETER.S19); return { B11, B12 }; }