feat(rebar): 확대기초 배근 입력 8개 (피복 3·여유·격자 s·s_v·e_top·n), FT01·FT02 검사
- core/rebar/footing-settings.ts: 입력 정의, 기본값(확대기초철근 (1).md), 범위(제안), 검사 (FT01 범위, FT02 n 정수) - 확대기초 계산 함수가 설정을 인자로 받음 (기본값 = 기존 값, 기존 호출은 그대로) - 3D 확대기초 그림의 125·100 하드코딩 제거, 철근표·3D·기둥 층 높이가 같은 설정을 씀 - 패널: 상면·측면·하면 피복을 칸 분리, s_v와 e_top 분리. 고정 규칙(k=2, Δ₂=100, 전단 4s·엇갈림 2s, cycle 교대)과 B2·B6 디리는 읽기 전용으로 표시 - 기본값에서 P1 결과 불변 (기존 테스트 통과) Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
550b508e7a
commit
5a20a18185
11 files changed
+281
-96
No files matched your search
+53
-67
@@ -1,26 +1,15 @@
|
||||
import { hookLength, NOMINAL_DIAMETER, type BarSize } from './common';
|
||||
import { DEFAULT_FOOTING_REBAR, type FootingRebarSettings } from './footing-settings';
|
||||
|
||||
/**
|
||||
* 확대기초 철근 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,
|
||||
/** 상단 최소 여유 e_top (B10 top level, §7.1). */
|
||||
eTop: 150,
|
||||
} as const;
|
||||
/** 확대기초 고정 규칙 (사양): Δ₂ 하면 1·2단 간격 = 100 (고정). The other values are the editable defaults. */
|
||||
export const DELTA2 = 100;
|
||||
|
||||
/** Kept for reference; the calculations read the settings passed in (승인 2026-10-26). */
|
||||
export const FOOTING_DEFAULTS = { ...DEFAULT_FOOTING_REBAR, delta2: DELTA2 } as const;
|
||||
|
||||
export interface GridResult {
|
||||
/** 격자 칸 수 n = floor((A − 2·e_min)/s); 위치 수 = n + 1. */
|
||||
@@ -34,10 +23,10 @@ export interface GridResult {
|
||||
}
|
||||
|
||||
/** 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;
|
||||
export function grid(side: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): GridResult {
|
||||
const A = side - 2 * f.cSide;
|
||||
const n = Math.floor((A - 2 * f.eMin) / f.s);
|
||||
const G = n * f.s;
|
||||
const e = (A - G) / 2;
|
||||
return { n, G, e, positions: n + 1 };
|
||||
}
|
||||
@@ -57,18 +46,17 @@ export interface Layers {
|
||||
B7: number;
|
||||
}
|
||||
|
||||
export function layers(H: number): Layers {
|
||||
const { cTop, cBottom, delta2 } = FOOTING_DEFAULTS;
|
||||
const zB5 = H - cTop;
|
||||
export function layers(H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): Layers {
|
||||
const zB5 = H - f.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 };
|
||||
const zB24 = f.cBottom;
|
||||
const zB68 = f.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);
|
||||
export function legB2(H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): number {
|
||||
const z = layers(H, f);
|
||||
return z.B1 - z.B24; // 1723 at H 2000
|
||||
}
|
||||
|
||||
@@ -82,13 +70,13 @@ export interface FootingBar {
|
||||
}
|
||||
|
||||
/** 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);
|
||||
export function bottomBars(L: number, W: number, H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): FootingBar[] {
|
||||
const g = grid(W, f);
|
||||
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);
|
||||
const l0 = L - 2 * f.cSide;
|
||||
const leg2 = legB2(H, f);
|
||||
return [
|
||||
{ name: 'B1', size: 'S25', count: N2, length: l0 + 2 * legLength('S25') },
|
||||
{ name: 'B2', size: 'S25', count: N2, length: l0 + 2 * leg2 },
|
||||
@@ -98,13 +86,13 @@ export function bottomBars(L: number, W: number, H: number): FootingBar[] {
|
||||
}
|
||||
|
||||
/** 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);
|
||||
export function crossBars(L: number, W: number, H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): FootingBar[] {
|
||||
const g = grid(L, f);
|
||||
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 l0 = W - 2 * f.cSide;
|
||||
const z = layers(H, f);
|
||||
const leg6 = z.B5 - z.B68;
|
||||
return [
|
||||
{ name: 'B5', size: 'S29', count: N6, length: l0 + 2 * legLength('S29') },
|
||||
@@ -115,19 +103,19 @@ export function crossBars(L: number, W: number, H: number): FootingBar[] {
|
||||
}
|
||||
|
||||
/** B10 levels: m = floor((z_B5 − e_top − c_b)/s_v) (§7.1), so the count is m + 1 (9 for P1, H 2000). */
|
||||
export function b10Levels(H: number): number {
|
||||
const m = Math.floor((layers(H).B5 - FOOTING_DEFAULTS.eTop - FOOTING_DEFAULTS.cBottom) / FOOTING_DEFAULTS.sV);
|
||||
export function b10Levels(H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): number {
|
||||
const m = Math.floor((layers(H, f).B5 - f.eTop - f.cBottom) / f.sV);
|
||||
return m + 1;
|
||||
}
|
||||
|
||||
/** Depth of a side bar from the side face: c_s + (d_a + d_b)/2 (§7, §8.3). B10-1 hangs on B6, B10-2 on B2. */
|
||||
export const sideDepth = (dOuter: number, dB10: number) => FOOTING_DEFAULTS.cSide + (dOuter + dB10) / 2;
|
||||
export const sideDepth = (dOuter: number, dB10: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR) => f.cSide + (dOuter + dB10) / 2;
|
||||
|
||||
/** 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, H: number): FootingBar[] {
|
||||
const depth1 = sideDepth(NOMINAL_DIAMETER.S29, NOMINAL_DIAMETER.S19); // B10-1: 123.85
|
||||
const depth2 = sideDepth(NOMINAL_DIAMETER.S25, NOMINAL_DIAMETER.S19); // B10-2: 122.25
|
||||
const levels = b10Levels(H);
|
||||
export function sideBars(L: number, W: number, H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): FootingBar[] {
|
||||
const depth1 = sideDepth(NOMINAL_DIAMETER.S29, NOMINAL_DIAMETER.S19, f); // B10-1: 123.85
|
||||
const depth2 = sideDepth(NOMINAL_DIAMETER.S25, NOMINAL_DIAMETER.S19, f); // B10-2: 122.25
|
||||
const levels = b10Levels(H, f);
|
||||
const hook = hookLength('main90', d('S19'));
|
||||
return [
|
||||
{ name: 'B10-1', size: 'S19', count: levels * 2, length: L - 2 * depth2 + 2 * hook },
|
||||
@@ -136,10 +124,10 @@ export function sideBars(L: number, W: number, H: number): FootingBar[] {
|
||||
}
|
||||
|
||||
/** Assembly levels j = n, 2n, … strictly inside the B10 levels, 0 < j < m (§8.1). */
|
||||
export function assemblyJs(H: number): number[] {
|
||||
const m = b10Levels(H) - 1;
|
||||
export function assemblyJs(H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): number[] {
|
||||
const m = b10Levels(H, f) - 1;
|
||||
const js: number[] = [];
|
||||
for (let j = FOOTING_DEFAULTS.n; j < m; j += FOOTING_DEFAULTS.n) js.push(j);
|
||||
for (let j = f.n; j < m; j += f.n) js.push(j);
|
||||
return js;
|
||||
}
|
||||
|
||||
@@ -149,21 +137,21 @@ export function assemblyEndOffset(): number {
|
||||
}
|
||||
|
||||
/** 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, H: number): FootingBar[] {
|
||||
const levelsJ = assemblyJs(H);
|
||||
export function assemblyBars(L: number, W: number, H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): FootingBar[] {
|
||||
const levelsJ = assemblyJs(H, f);
|
||||
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
|
||||
const nW = grid(W, f).positions - 1; // 61
|
||||
const nL = grid(L, f).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 = Math.round(hookLength('main90', d('S16'))); // 246 mm, rounded as in the document (§8.3)
|
||||
const depth1 = sideDepth(NOMINAL_DIAMETER.S29, NOMINAL_DIAMETER.S19);
|
||||
const depth2 = sideDepth(NOMINAL_DIAMETER.S25, NOMINAL_DIAMETER.S19);
|
||||
const depth1 = sideDepth(NOMINAL_DIAMETER.S29, NOMINAL_DIAMETER.S19, f);
|
||||
const depth2 = sideDepth(NOMINAL_DIAMETER.S25, NOMINAL_DIAMETER.S19, f);
|
||||
return [
|
||||
{ name: 'B11', size: 'S16', count: b11, length: W - 2 * (depth1 + assemblyEndOffset()) + 2 * hook },
|
||||
{ name: 'B12', size: 'S16', count: b12, length: L - 2 * (depth2 + assemblyEndOffset()) + 2 * hook },
|
||||
@@ -171,9 +159,9 @@ export function assemblyBars(L: number, W: number, H: number): FootingBar[] {
|
||||
}
|
||||
|
||||
/** Plane positions of the grid along one side, mm from the footing centre: x_i = −side/2 + c_s + e + i·s (§3.1). */
|
||||
export function gridPositions(side: number): number[] {
|
||||
const g = grid(side);
|
||||
return Array.from({ length: g.positions }, (_, i) => -side / 2 + FOOTING_DEFAULTS.cSide + g.e + i * FOOTING_DEFAULTS.s);
|
||||
export function gridPositions(side: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): number[] {
|
||||
const g = grid(side, f);
|
||||
return Array.from({ length: g.positions }, (_, i) => -side / 2 + f.cSide + g.e + i * f.s);
|
||||
}
|
||||
|
||||
/** B9 bar of the rule (§9.1, Q-B18): a vertical bar at (x, n), mm from the footing centre. */
|
||||
@@ -187,9 +175,9 @@ export interface B9Bar {
|
||||
* starts at 1 (even r) or 3 (odd r) and steps by 4. A bar is left out when it falls in a column zone: its centre is closer
|
||||
* than the column radius plus half a bar to a column axis (Q-B20, applied: the 기둥 구역 is removed).
|
||||
*/
|
||||
export function b9Bars(L: number, W: number, columnX: number[], columnD: number = 2000): { rows: number; perRow: number[]; bars: B9Bar[] } {
|
||||
const xs = gridPositions(L);
|
||||
const ns = gridPositions(W);
|
||||
export function b9Bars(L: number, W: number, columnX: number[], columnD: number = 2000, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): { rows: number; perRow: number[]; bars: B9Bar[] } {
|
||||
const xs = gridPositions(L, f);
|
||||
const ns = gridPositions(W, f);
|
||||
const rowIndices: number[] = [];
|
||||
for (let i = 1; i < ns.length; i += 4) rowIndices.push(i);
|
||||
const halfBar = NOMINAL_DIAMETER.H16 / 2;
|
||||
@@ -213,9 +201,9 @@ export function b9Bars(L: number, W: number, columnX: number[], columnD: number
|
||||
* B9 전단철근 (확대기초철근 (1).md §9). Vertical bars from the B2·B4 level to the B5 level, H16, with 135° hooks at both ends.
|
||||
* Length = l_v + 2·l_h (l_v = z_B5 − z_B24). The hook tail (6·d) turns toward the footing centre (Q-B19, applied).
|
||||
*/
|
||||
export function shearB9(L: number, W: number, H: number, columnX: number[] = [], columnD: number = 2000): { rows: number; perRow: number[]; count: number; length: number; bars: B9Bar[] } {
|
||||
const { rows, perRow, bars } = b9Bars(L, W, columnX, columnD);
|
||||
const lv = layers(H).B5 - layers(H).B24; // 1750
|
||||
export function shearB9(L: number, W: number, H: number, columnX: number[] = [], columnD: number = 2000, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): { rows: number; perRow: number[]; count: number; length: number; bars: B9Bar[] } {
|
||||
const { rows, perRow, bars } = b9Bars(L, W, columnX, columnD, f);
|
||||
const lv = layers(H, f).B5 - layers(H, f).B24; // 1750
|
||||
const lh = hookLength('stirrup135', d('H16'));
|
||||
return { rows, perRow, count: bars.length, length: lv + 2 * lh, bars };
|
||||
}
|
||||
@@ -237,11 +225,9 @@ export function legOffsets(): { B1: number; B3: number; B5: number; B7: number }
|
||||
}
|
||||
|
||||
/** 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(H: number): { B11: number[]; B12: number[] } {
|
||||
const { cBottom } = FOOTING_DEFAULTS;
|
||||
const sV = FOOTING_DEFAULTS.sV;
|
||||
const js = assemblyJs(H);
|
||||
const B11 = js.map((j) => cBottom + j * sV);
|
||||
export function assemblyHeights(H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): { B11: number[]; B12: number[] } {
|
||||
const js = assemblyJs(H, f);
|
||||
const B11 = js.map((j) => f.cBottom + j * f.sV);
|
||||
const B12 = B11.map((z) => z + NOMINAL_DIAMETER.S19);
|
||||
return { B11, B12 };
|
||||
}
|
||||
Reference in new issue
Block a user