feat(core): RB-C07 — delete P4 ties that interfere with the footing top bars

- A P4 is deleted when |z_P4 − z_bar| < (d_P4 + d_bar)/2 against B5 and B1
  (the 확대기초 top layer, from the 상면). B11/B12 are not checked (Q-C9 무시).
- The P6 crossties start from the first kept P4.
- Tests: the document's example keeps all 8; B5 moved to −70 deletes the P4 at
  −60; a P4 just outside the limit is kept.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-10-22 11:49:16 +09:00
1 parent 02d740d8ca
commit 5cc166c2e1
2 files changed
+61 -2

No files matched your search

+38 -2
View File
@@ -1,4 +1,5 @@
import { developmentLength, hookLength, NOMINAL_DIAMETER, spliceLength } from './common';
import { layers } from './footing';
/**
* 기둥 철근 C4 (plan C4). Source: RAG/기둥철근 (1).md, 2026-10-22 최종 정리. Type-1 원형 중실 기둥.
@@ -52,6 +53,38 @@ export const COLUMN_P1: ColumnInput = {
ctcP6: 1800,
};
/** A bar in the 확대기초 top layer, height from the 확대기초 상면 (mm, negative = below). */
export interface TopBar {
name: string;
z: number;
d: number;
}
/**
* RB-C07 (기둥철근.md §6.1–6.2, 결정): a P4 that interferes with a 확대기초 top bar is deleted.
* Interference: |z_P4 − z_bar| < (d_P4 + d_bar)/2. Only the top layer (B5, B1) is checked; the assembly bars
* B11/B12 are not, by the 2026-10-22 decision (Q-C9 무시).
*/
export function deleteInterferingP4(p4: number[], topBars: TopBar[], dP4: number = NOMINAL_DIAMETER.H19): { kept: number[]; deleted: { z: number; bar: string }[] } {
const kept: number[] = [];
const deleted: { z: number; bar: string }[] = [];
for (const z of p4) {
const hit = topBars.find((b) => Math.abs(z - b.z) < (dP4 + b.d) / 2);
if (hit) deleted.push({ z, bar: hit.name });
else kept.push(z);
}
return { kept, deleted };
}
/** Top-layer bars of the footing as heights from its 상면 (B5 and B1, §4.2). */
export function footingTopBars(Hf: number): TopBar[] {
const z = layers(Hf);
return [
{ name: 'B5', z: z.B5 - Hf, d: NOMINAL_DIAMETER.S29 },
{ name: 'B1', z: z.B1 - Hf, d: NOMINAL_DIAMETER.S25 },
];
}
const dS32 = NOMINAL_DIAMETER.S32;
const dH19 = NOMINAL_DIAMETER.H19;
const dH16 = NOMINAL_DIAMETER.H16;
@@ -83,9 +116,11 @@ export function columnRebar(x: ColumnInput = COLUMN_P1) {
const p5 = Array.from({ length: n5all }, (_, j) => lE - (j + 1) * x.ctcP5);
const z5last = lE - n5all * x.ctcP5;
const m4 = Math.floor((z5last + x.rP4 * x.Hf) / x.ctcP4);
const p4 = Array.from({ length: m4 }, (_, j) => z5last - (j + 1) * x.ctcP4);
const p4All = Array.from({ length: m4 }, (_, j) => z5last - (j + 1) * x.ctcP4);
const p4Check = deleteInterferingP4(p4All, footingTopBars(x.Hf));
const p4 = p4Check.kept;
// P6 crossties (기둥철근.md §7): 4 bars per 단, from the first P4
// P6 crossties (기둥철근.md §7): 4 bars per 단, from the first kept P4
const z4first = p4.length ? p4[0] : z5last;
const p6Levels: number[] = [];
for (let z = z4first; z <= x.Hc; z += x.ctcP6) p6Levels.push(z);
@@ -127,5 +162,6 @@ export function columnRebar(x: ColumnInput = COLUMN_P1) {
p3Count: p3.length,
p5Count: p5.length,
p4Count: p4.length,
p4Deleted: p4Check.deleted,
};
}
+23
View File
@@ -61,3 +61,26 @@ describe('기둥 철근 C4 (기둥철근.md, plan C4)', () => {
expect(c.p6Length).toBeCloseTo(2071, 0);
});
});
import { deleteInterferingP4, footingTopBars } from '../src/core/rebar/column';
describe('RB-C07 P4 삭제 규칙 (기둥철근.md §6.2)', () => {
it('the example: no P4 interferes with the top bars (B5 −100, B1 −127): 8 kept, 0 deleted', () => {
expect(c.p4Count).toBe(8);
expect(c.p4Deleted).toEqual([]);
const r = deleteInterferingP4([-60, -180], footingTopBars(2000));
expect(r.deleted).toEqual([]);
});
it('hypothetical: B5 moved to −70 deletes the P4 at −60 (|−60 + 70| = 10 < 23.85)', () => {
const bars = [{ name: 'B5', z: -70, d: 28.6 }];
const r = deleteInterferingP4([-60, -180, -300], bars);
expect(r.deleted).toEqual([{ z: -60, bar: 'B5' }]);
expect(r.kept).toEqual([-180, -300]);
});
it('a P4 just outside the limit is kept: |−60 + 84| = 24 ≥ 23.85', () => {
const r = deleteInterferingP4([-60], [{ name: 'B5', z: -84, d: 28.6 }]);
expect(r.kept).toEqual([-60]);
});
});