From f989315e02b485ed091004ad59e1671b52d26f54 Mon Sep 17 00:00:00 2001 From: ypshim Date: Fri, 23 Oct 2026 13:19:09 +0900 Subject: [PATCH] =?UTF-8?q?feat(rebar):=20P1=20top=20=3D=20C3=20=E2=88=92?= =?UTF-8?q?=20100=20with=20e=5FP1=20computed;=20coupler=20rule=20keeps=202?= =?UTF-8?q?.5=20m=20below=20the=20P1=20top?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit e_P1 is no longer an input: P1 top = C3 (lowest coping top − 127 − 2·57.2 = 43.170) − 100 = 43.070, so e_P1 = step top − 43.070 = 485 mm. The P1 top moves from 15147 to 14862 mm; the top piece shortens by 285 mm. Couplers are placed at k·3000 with at least 2500 mm below the P1 top (a value to confirm). On P1 the coupler count stays at four (3, 6, 9, 12 m). P2 and the tie top do not change. Co-Authored-By: Claude Haiku 5.5 --- src/core/rebar/column.ts | 15 ++++++++++++--- tests/column-model.test.ts | 6 +++--- tests/rebar-column.test.ts | 8 ++++---- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/core/rebar/column.ts b/src/core/rebar/column.ts index 23bac4c..b210319 100644 --- a/src/core/rebar/column.ts +++ b/src/core/rebar/column.ts @@ -27,6 +27,8 @@ export interface ColumnInput { ctcCoupler: number; /** P2 커플러를 P1보다 올리는 높이. */ dzCp2: number; + /** 커플러는 P1 위 끝에서 이 거리(mm) 이상 아래에만 둔다 (커플러 여유, 가정 2500). */ + couplerTopMargin?: number; ctcP3: number; ctcP5: number; ctcP4: number; @@ -46,7 +48,11 @@ export function columnInputFor(model: PierModel, base: ColumnInput = COLUMN_P1): const seg = model.segments.find((g) => col.s >= g.s0 - 1e-9 && col.s <= g.s1 + 1e-9) ?? model.segments[0]; const Hc = Math.round((model.levelBottom - model.footings[0].top) * 1000 * 10) / 10; const hcop = Math.round((seg.top - model.levelBottom) * 1000 * 10) / 10; - return { ...base, Hc, hcop }; + // C3 (상면 3단) = the lowest coping top −127 (C1 cover) −2·57.2 (layer spacing 상면 주철근 단 간격); P1: 43.170. + // P1 top = C3 − 100 (계산 2026-10-23), so e_P1 = step top − (C3 − 0.1) = 485 on P1. + const c3 = model.copingRefTop - 0.127 - 2 * 0.0572; + const eP1 = Math.round((seg.top - (c3 - 0.1)) * 1000); + return { ...base, Hc, hcop, eP1 }; } export const COLUMN_P1: ColumnInput = { @@ -54,7 +60,7 @@ export const COLUMN_P1: ColumnInput = { Hc: 12703, hcop: 2644, c: 125, - eP1: 200, + eP1: 485, eTop: 1623, Hf: 2000, nDiv: 46, @@ -113,7 +119,10 @@ export function columnRebar(x: ColumnInput = COLUMN_P1, m: Material = DEFAULT_MA const zTop2 = x.Hc + lD2; // Bottom of P1 and P2: 확대기초 하면 2단 (B7) 위 + (d_B7 + d_P1)/2 (기둥철근.md §3.2, the same for both). const zBot = -x.Hf + layers(x.Hf).B7 + (NOMINAL_DIAMETER.S19 + dS32) / 2; - const couplerLevels = Array.from({ length: Math.max(0, Math.floor(zTop1 / x.ctcCoupler) - 1) }, (_, i) => (i + 1) * x.ctcCoupler); + // Couplers at k·ctc from the footing, kept at least the margin below the P1 top (커플러 여유). + const couplerMargin = x.couplerTopMargin ?? 2500; + const couplerCount = Math.max(0, Math.floor((zTop1 - couplerMargin) / x.ctcCoupler)); + const couplerLevels = Array.from({ length: couplerCount }, (_, i) => (i + 1) * x.ctcCoupler); // Pieces of each main bar between its couplers (기둥철근.md §3.2): the bottom piece carries the 90° hook (16.5·d = 525). const p1Hook = Math.round(hookLength('main90', dS32)); const p1Points = [zBot, ...couplerLevels, zTop1]; diff --git a/tests/column-model.test.ts b/tests/column-model.test.ts index 54668ee..38b0248 100644 --- a/tests/column-model.test.ts +++ b/tests/column-model.test.ts @@ -10,7 +10,7 @@ describe('기둥 철근 높이는 모델에서 (Hc · h_cop, 승인 2026-10-23)' expect(input.hcop).toBeCloseTo(2644, 0); const col = columnRebar(input); expect(col.zTop2).toBeCloseTo(14539, 0); - expect(col.zTop1).toBeCloseTo(15147, 0); + expect(col.zTop1).toBeCloseTo(14862, 0); }); it('taper h2 = 3000: Level underside 39.411, Hc 11203, P2 top 13039 (= Level underside + l_d)', () => { @@ -21,7 +21,7 @@ describe('기둥 철근 높이는 모델에서 (Hc · h_cop, 승인 2026-10-23)' expect(input.hcop).toBeCloseTo(4144, 0); const col = columnRebar(input); expect(col.zTop2).toBeCloseTo(13039, 0); - // The P1 top is anchored to the step top, so it does not move with h2. - expect(col.zTop1).toBeCloseTo(15147, 0); + // The P1 top is anchored to the step top (and C3), so it does not move with h2. + expect(col.zTop1).toBeCloseTo(14862, 0); }); }); diff --git a/tests/rebar-column.test.ts b/tests/rebar-column.test.ts index fdff22a..4b797d6 100644 --- a/tests/rebar-column.test.ts +++ b/tests/rebar-column.test.ts @@ -10,8 +10,8 @@ describe('기둥 철근 C4 (기둥철근.md, plan C4)', () => { expect(c.angleStepDeg).toBeCloseTo(360 / 46, 9); }); - it('heights: P1 top 15147, P2 top ≈ 14539 (l_d of S32 at fck 27, 1836.9 ≈ the document 1836)', () => { - expect(c.zTop1).toBe(15147); + it('heights: P1 top 14862 (= C3 43.170 − 0.1, e_P1 485), P2 top ≈ 14539 (l_d of S32 at fck 27, 1836.9 ≈ the document 1836)', () => { + expect(c.zTop1).toBe(14862); expect(Math.abs(c.zTop2 - 14539)).toBeLessThan(1); }); @@ -86,10 +86,10 @@ describe('RB-C07 P4 삭제 규칙 (기둥철근.md §6.2)', () => { }); describe('기둥철근 P1·P2 마디와 바닥 (기둥철근.md §3.2)', () => { - it('bottom at −1697.5 (−H_f + z_B7 + (d_B7 + d_P1)/2), P1 pieces 4697.5 + 525 / 3000 ×3 / 3147', () => { + it('bottom at −1697.5 (−H_f + z_B7 + (d_B7 + d_P1)/2), P1 pieces 4697.5 + 525 / 3000 ×3 / 2862', () => { expect(c.zBot).toBeCloseTo(-1697.55, 1); expect(c.p1Hook).toBe(525); - expect(c.p1Pieces.map((v) => Math.round(v))).toEqual([5223, 3000, 3000, 3000, 3147]); + expect(c.p1Pieces.map((v) => Math.round(v))).toEqual([5223, 3000, 3000, 3000, 2862]); }); it('P2 pieces 4847.5 / 3000 ×3 / 2389', () => {