diff --git a/src/core/rebar/coping.ts b/src/core/rebar/coping.ts index ad0b858..3a4777a 100644 --- a/src/core/rebar/coping.ts +++ b/src/core/rebar/coping.ts @@ -229,12 +229,13 @@ export interface StepBar { /** * C15 (거꾸로 된 U, on the highest segment) and C16 (L형, on segments 3 and 4), 11 bars each across the width (코핑철근.md §7). - * Across: 137 from the cover line, then @250 (피복선 기준). Along: from each segment start + c to the next end − c. + * Across: 137 from the cover line, then @pitch (피복선 기준). pitch = 2 × 주철근 기본간격 (승인 2026-10-26; 250 at P1). + * Along: from each segment start + c to the next end − c. */ -export function stairBars(L: number, B: number, topEl: number, m: Material = DEFAULT_MATERIAL): { c15: StepBar[]; c16: StepBar[] } { +export function stairBars(L: number, B: number, topEl: number, m: Material = DEFAULT_MATERIAL, pitch = 2 * DEFAULT_COPING_REBAR.mainSpacing): { c15: StepBar[]; c16: StepBar[] } { const ld = stepDevLength(m); const c = COPING_REBAR.cover; - const ns = Array.from({ length: 11 }, (_, k) => -B / 2 + c + 137 + 250 * k); + const ns = Array.from({ length: 11 }, (_, k) => -B / 2 + c + 137 + pitch * k); const seg = STAIR_SEGMENTS; const top = (i: number) => topEl + seg[i].rise / 1000; const c15Z = top(3) - c / 1000; diff --git a/src/core/rebar/summary.ts b/src/core/rebar/summary.ts index ac14b9f..a3ab2d5 100644 --- a/src/core/rebar/summary.ts +++ b/src/core/rebar/summary.ts @@ -134,6 +134,10 @@ export function linkedSpacing(name: string, model: PierModel): Pick c14.filter((b) => b.line === line).length; const omitted = c14.filter((b) => !b.legs).length; rows.push({ member: 'coping', name: 'C14', size: 'H19', count: c14.length, length: c14Length(model.copingB * 1000), note: `위 2 줄 ${lineCount(1)} · ${lineCount(2)}본씩, 아래 ${[3, 4, 5, 6].map(lineCount).join(' · ')} (합 ${c14.length}) · 연직 발 생략 ${omitted}개 (하면 밖)` }); - const stairs = stairBars(model.copingL * 1000, model.copingB * 1000, model.copingRefTop, mCoping); + const stairs = stairBars(model.copingL * 1000, model.copingB * 1000, model.copingRefTop, mCoping, 2 * model.copingRebar.mainSpacing); rows.push({ member: 'coping', name: 'C15', size: 'H19', count: stairs.c15.length, length: stairs.c15[0].length, note: '계단부, 가장 높은 구간 (거꾸로 U)' }); rows.push({ member: 'coping', name: 'C16', size: 'H19', count: stairs.c16.length, length: stairs.c16[0].length, note: `계단부, 3·4구간 (${Math.round(stairs.c16[0].length)} / ${Math.round(stairs.c16[stairs.c16.length - 1].length)})` }); diff --git a/src/view/rebar3d.ts b/src/view/rebar3d.ts index 25e5e3b..46ca8e8 100644 --- a/src/view/rebar3d.ts +++ b/src/view/rebar3d.ts @@ -249,7 +249,7 @@ export function buildRebar3D(m: PierModel, common: Material = DEFAULT_MATERIAL, } } // C15 (거꾸로 된 U) and C16 (L형) on the stair (코핑철근.md §7): 11 bars each across the width. - const stairs = stairBars(L * 1000, B * 1000, topEl, mCoping); + const stairs = stairBars(L * 1000, B * 1000, topEl, mCoping, 2 * m.copingRebar.mainSpacing); for (const s of stairs.c15) { const n = mm(s.n); const ld = mm(s.leg); diff --git a/tests/rebar-coping-main.test.ts b/tests/rebar-coping-main.test.ts index b620252..f0d743a 100644 --- a/tests/rebar-coping-main.test.ts +++ b/tests/rebar-coping-main.test.ts @@ -40,12 +40,16 @@ describe('C1–C8 상·하면 주철근 (코핑철근.md §5, plan C3)', () => { expect([c1.spacing, c1.actual, c1.basis]).toEqual(['250', '125', 'default']); const c12 = base.find((r) => r.name === 'C12')!; expect(c12.basis).toBe('default'); + const c15 = base.find((r) => r.name === 'C15')!; + expect([c15.spacing, c15.actual, c15.basis]).toEqual(['250', '250', 'default']); const m = buildPier({ ...loadP1(), copingRebar: { mainSpacing: 140 }, blockRebar: { spacingS1: 130 } }); const rows = rebarSummary(m).rows; const c8 = rows.find((r) => r.name === 'C8')!; expect([c8.spacing, c8.actual, c8.basis]).toEqual(['280', '140', 'input']); expect(rows.find((r) => r.name === 'C12')!.basis).toBe('default'); + const c16 = rows.find((r) => r.name === 'C16')!; + expect([c16.spacing, c16.actual, c16.basis]).toEqual(['280', '280', 'input']); const s1 = rows.find((r) => r.name === 'S1')!; expect([s1.spacing, s1.actual, s1.basis]).toEqual(['130', '130', 'input']); }); diff --git a/tests/rebar-stairs.test.ts b/tests/rebar-stairs.test.ts index 532e141..50f41d7 100644 --- a/tests/rebar-stairs.test.ts +++ b/tests/rebar-stairs.test.ts @@ -65,3 +65,12 @@ describe('C14 연직 발 (코핑철근.md §8)', () => { expect(omitted).toEqual([[4, 1], [4, 11]]); }); }); + +describe('C15 간격 = 2 × 주철근 기본간격 (승인 2026-10-26)', () => { + it('the default pitch is 250, and a larger pitch moves the 11 C15 bars apart', () => { + const { c15 } = stairBars(L, B, m.copingRefTop); + expect(c15[1].n - c15[0].n).toBe(250); + const wider = stairBars(L, B, m.copingRefTop, undefined, 280).c15; + expect(wider[1].n - wider[0].n).toBe(280); + }); +});