From bad220d79170ece2e5c3fdb5e361f5ba216330cc Mon Sep 17 00:00:00 2001 From: ypshim Date: Mon, 26 Oct 2026 17:51:10 +0900 Subject: [PATCH] =?UTF-8?q?fix(rebar):=20=EC=A1=B0=EB=A6=BD=20=EC=B2=A0?= =?UTF-8?q?=EA=B7=BC=20B11=C2=B7B12=EB=A5=BC=20=EC=A7=9D=EC=88=98=20?= =?UTF-8?q?=EA=B2=A9=EC=9E=90=EC=97=90=20=EB=B0=B0=EC=B9=98=20(B9=20?= =?UTF-8?q?=ED=99=80=EC=88=98=20=EA=B2=A9=EC=9E=90=EC=99=80=20=EA=B2=B9?= =?UTF-8?q?=EC=B9=98=EC=A7=80=20=EC=95=8A=EC=9D=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - assemblyEvenStart: 짝수 시작칸 중 양끝 여유 차이가 가장 작은 값 선택 - B12(W 방향, n 61)는 시작칸이 1(홀수)이던 것을 2(짝수)로 바꿔 B9 행선과 겹치지 않게 함 - 개수(B11 30, B12 24)와 가운데 정렬(차이 ≤ 1칸)은 그대로, 엇갈림 2칸 유지 - 테스트: 조립 철근 인덱스가 모두 짝수 Co-Authored-By: Claude Haiku 5.5 --- src/core/rebar/footing.ts | 35 ++++++++++++++++++++++--------- tests/footing-section-svg.test.ts | 9 ++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/core/rebar/footing.ts b/src/core/rebar/footing.ts index 8528c9e..698ed08 100644 --- a/src/core/rebar/footing.ts +++ b/src/core/rebar/footing.ts @@ -139,18 +139,33 @@ 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). */ /** - * Grid indices of the assembly bars (B11 or B12) on one level, centred in the grid (승인 2026-10-26, 추천안). - * Bars every k steps; the level's 2-step stagger alternates (even levels start at e0, odd levels at e0 + 2), and e0 centres - * the even group so the left and right gaps differ by at most one step. n = positions − 1; idx = level index (0, 1, …). + * First grid index of the even group of the assembly bars (B11 or B12), chosen on EVEN indices (확대기초철근 (1).md §9.1: the + * B5·B1 and B9 positions take the odd grid, so B11·B12 sit on the even grid and never overlap them). Among the even choices, + * the one that centres the union of both 2-step-staggered groups best (left and right gaps as equal as possible) is used. + */ +export function assemblyEvenStart(n: number, k: number): number { + let best = 0; + let bestGap = Infinity; + for (let e0 = 0; e0 + 2 <= n; e0 += 2) { + const idx: number[] = []; + for (const s of [e0, e0 + 2]) for (let i = s; i <= n; i += k) idx.push(i); + const gap = Math.abs(Math.min(...idx) - (n - Math.max(...idx))); + if (gap < bestGap) { + best = e0; + bestGap = gap; + } + } + return best; +} + +/** + * Grid indices of the assembly bars (B11 or B12) on one level (승인 2026-10-26): every k grid steps, on even indices, with the + * 2-step stagger between levels (even levels at e0, odd levels at e0 + 2). n = positions − 1; idx = level index (0, 1, …). */ export function assemblyGridIndices(n: number, k: number, idx: number): number[] { - const cEven = Math.floor(n / k) + 1; - const cOdd = Math.floor((n - 2) / k) + 1; - const e0 = Math.max(0, Math.floor((n - (cEven - 1) * k - 2) / 2)); - const even = idx % 2 === 0; - const start = even ? e0 : e0 + 2; - const count = even ? cEven : cOdd; - return Array.from({ length: count }, (_, m) => start + m * k).filter((i) => i <= n); + const start = idx % 2 === 0 ? assemblyEvenStart(n, k) : assemblyEvenStart(n, k) + 2; + const count = start <= n ? Math.floor((n - start) / k) + 1 : 0; + return Array.from({ length: count }, (_, m) => start + m * k); } export function assemblyBars(L: number, W: number, H: number, f: FootingRebarSettings = DEFAULT_FOOTING_REBAR): FootingBar[] { diff --git a/tests/footing-section-svg.test.ts b/tests/footing-section-svg.test.ts index 05315e3..8e5dd11 100644 --- a/tests/footing-section-svg.test.ts +++ b/tests/footing-section-svg.test.ts @@ -49,4 +49,13 @@ describe('확대기초 교각축 직각 단면 (폭 W) (승인 2026-10-26, 추 } expect(assemblyGridIndices(61, 8, 1)[0] - assemblyGridIndices(61, 8, 0)[0]).toBe(2); }); + + it('조립 철근(B11·B12)은 짝수 격자에 놓여 B9(홀수 격자)와 겹치지 않음 (확대기초철근 (1).md §9.1)', () => { + const levels = assemblyJs(H, fs).length; + for (const [n, k] of [[79, 8], [61, 8]] as const) { + for (let idx = 0; idx < levels; idx++) { + for (const i of assemblyGridIndices(n, k, idx)) expect(i % 2).toBe(0); + } + } + }); });