Files
Pier_v1/src/core/rebar/footing.ts
T
ypshimandClaude Haiku 5.5 4d8bbf7f6e test(core): footing drawing-reproduction checks from the document's test table
- Leg offsets (B1 +25.4, B3 +22.25, B5 +28.6, B7 +23.85) and B11 / B12 heights
  (550 / 950 / 1350; 569.1 / 969.1 / 1369.1).
- Reproduction counts: W 7800 → B4 30 / B2 29; L 9350 → B8 36 / B6 35.
- Variables: W 8000 with s 150 → 50 steps, e 150; W 12000 → 92 steps.
- Splice cases: L 8700 → B2 11946 (no splice), L 12400 → B4 12200,
  W 8800 → B6 12046.
- Not covered: the 배근도 H 2200 case for B10 (the document's count is unclear).

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-22 11:39:42 +09:00

195 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 };
}