feat(rebar): B1–B4와 B5–B8 방향 교환 (B1–B4는 W 방향, B5–B8은 L 방향)

- 재료표(B5–B8 9150 = 9350 − 2c, B1–B4 7600 = 7800 − 2c)와 같은 방향 배정으로 맞춤
- 격자 키를 물리 방향으로 정리: sL = L 방향 간격, sW = W 방향 간격 (기존 sAxis → sL, sPerp → sW)
- B1–B4: L 격자로 개수, 길이 W − 2c. B5–B8: W 격자로 개수, 길이 L − 2c
- B9: 열(L)은 sL, 행(W)은 sW 격자 (물리 격자 일치), 개수 불변
- B11은 L 방향 간격(sL), B12는 W 방향 간격(sW)
- 3D 확대기초 철근과 단면 그림의 역할을 교환 (B1–B4 선, B5–B8 점)
- 철근표 간격과 규칙 값, 설정 라벨을 방향 기준으로 수정
- 테스트 기대값 갱신 (B1–B4 40, B5–B8 31, B6 13646 분할 경고)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-10-27 09:36:20 +09:00
1 parent f2fa48913b
commit 752de2fd9d
12 files changed
+160 -167

No files matched your search

+22 -21
View File
@@ -23,7 +23,7 @@ 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, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR, key: FootingGridKey = 'sAxis'): GridResult {
export function grid(side: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR, key: FootingGridKey = 'sL'): GridResult {
const s = f[key];
const A = side - 2 * f.cSide;
const n = Math.floor((A - 2 * f.eMin) / s);
@@ -70,13 +70,13 @@ export interface FootingBar {
length: number;
}
/** B1–B4 (§5): the grid along W, with lengths from L. */
/** B1–B4 (승인 2026-10-26, 방향 교환): run along W, spaced along L (grid L, sL), with lengths from W. */
export function bottomBars(L: number, W: number, H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): FootingBar[] {
const g = grid(W, f);
const g = grid(L, f, 'sL');
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 * f.cSide;
const l0 = W - 2 * f.cSide;
const leg2 = legB2(H, f);
return [
{ name: 'B1', size: 'S25', count: N2, length: l0 + 2 * legLength('S25') },
@@ -86,13 +86,13 @@ export function bottomBars(L: number, W: number, H: number, f: FootingRebarSetti
];
}
/** B5–B8 (§6): the grid along L, with lengths from W. */
/** B5–B8 (승인 2026-10-26, 방향 교환): run along L, spaced along W (grid W, sW), with lengths from L. */
export function crossBars(L: number, W: number, H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): FootingBar[] {
const g = grid(L, f, 'sPerp');
const g = grid(W, f, 'sW');
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 * f.cSide;
const l0 = L - 2 * f.cSide;
const z = layers(H, f);
const leg6 = z.B5 - z.B68;
return [
@@ -172,9 +172,9 @@ export function assemblyGridIndices(n: number, k: number, idx: number): number[]
export function assemblyBars(L: number, W: number, H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): FootingBar[] {
const levelsJ = assemblyJs(H, f);
// B11 is spread along L (교각축 직각 grid) and B12 along W (교각축 grid), per 확대기초철근 (1).md §8.
const nW = grid(W, f, 'sAxis').positions - 1;
const nL = grid(L, f, 'sPerp').positions - 1;
// B11 is spread along L (grid L, sL) and B12 along W (grid W, sW), per 확대기초철근 (1).md §8 (after the 방향 교환).
const nW = grid(W, f, 'sW').positions - 1;
const nL = grid(L, f, 'sL').positions - 1;
const b11 = levelsJ.reduce((a, _, idx) => a + assemblyGridIndices(nL, f.kB11, idx).length, 0);
const b12 = levelsJ.reduce((a, _, idx) => a + assemblyGridIndices(nW, f.kB12, idx).length, 0);
const hook = Math.round(hookLength('main90', d('S16'))); // 246 mm, rounded as in the document (§8.3)
@@ -187,7 +187,7 @@ export function assemblyBars(L: number, W: number, H: number, f: FootingRebarSet
}
/** 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, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR, key: FootingGridKey = 'sAxis'): number[] {
export function gridPositions(side: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR, key: FootingGridKey = 'sL'): number[] {
const g = grid(side, f, key);
return Array.from({ length: g.positions }, (_, i) => -side / 2 + f.cSide + g.e + i * f[key]);
}
@@ -204,8 +204,9 @@ export interface B9Bar {
* 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, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): { rows: number; perRow: number[]; bars: B9Bar[] } {
const xs = gridPositions(L, f);
const ns = gridPositions(W, f);
// B9: columns along L use the L grid (sL); rows along W use the W grid (sW) (방향 교환 후 물리적 격자, 승인 2026-10-26).
const xs = gridPositions(L, f, 'sL');
const ns = gridPositions(W, f, 'sW');
const rowIndices: number[] = [];
for (let i = 1; i < ns.length; i += 4) rowIndices.push(i);
const halfBar = NOMINAL_DIAMETER.H16 / 2;
@@ -265,8 +266,8 @@ export function assemblyHeights(H: number, f: FootingRebarSettings = DEFAULT_FOO
* rebar table and the 3D view use, so the panel cannot drift from the calculation. H is the footing height (mm).
*/
export function footingRuleValues(H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): {
upper2sAxis: number;
upper2sPerp: number;
upper2L: number;
upper2W: number;
delta2: number;
b2Leg: number;
b6Leg: number;
@@ -279,15 +280,15 @@ export function footingRuleValues(H: number, f: FootingRebarSettings = DEFAULT_F
} {
const z = layers(H, f);
return {
upper2sAxis: 2 * f.sAxis,
upper2sPerp: 2 * f.sPerp,
upper2L: 2 * f.sL,
upper2W: 2 * f.sW,
delta2: DELTA2,
b2Leg: legB2(H, f),
b6Leg: z.B5 - z.B68,
b9Pitch: 4 * f.sAxis,
b9Stagger: 2 * f.sAxis,
b11Pitch: f.kB11 * f.sPerp,
b12Pitch: f.kB12 * f.sAxis,
b9Pitch: 4 * f.sL,
b9Stagger: 2 * f.sL,
b11Pitch: f.kB11 * f.sL,
b12Pitch: f.kB12 * f.sW,
assemblyVertical: f.n * f.sV,
assemblyLevels: assemblyJs(H, f).length,
};