From f37a0bf9ce702cb18d3048caec689aa19b76fe61 Mon Sep 17 00:00:00 2001 From: ypshim Date: Fri, 23 Oct 2026 13:10:51 +0900 Subject: [PATCH] fix(rebar): column heights Hc and h_cop from the model (Level underside, step top) instead of P1 constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hc = Level underside − footing top, and h_cop = step top at the column centre − Level underside. P1 gives 12703 and 2644, unchanged. With taper h2 = 3000 the Level underside moves down, so Hc = 11203 and the P2 top (Level underside + l_d) moves to 13039, while the P1 top stays at the step top (15147). The 3D and the 철근표 use the same heights. Co-Authored-By: Claude Haiku 5.5 --- src/core/rebar/column.ts | 14 ++++++++++++++ src/core/rebar/summary.ts | 4 ++-- src/view/rebar3d.ts | 5 +++-- tests/column-model.test.ts | 27 +++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 tests/column-model.test.ts diff --git a/src/core/rebar/column.ts b/src/core/rebar/column.ts index eee2363..23bac4c 100644 --- a/src/core/rebar/column.ts +++ b/src/core/rebar/column.ts @@ -1,3 +1,4 @@ +import type { PierModel } from '../geometry'; import { DEFAULT_MATERIAL, developmentLength, fyOf, hookLength, Material, NOMINAL_DIAMETER, spliceLength } from './common'; import { layers } from './footing'; @@ -35,6 +36,19 @@ export interface ColumnInput { } /** 예제 운곡교 P1 (기둥철근.md §1.2 기본값). */ +/** + * Column inputs from the model (1단계 연계, 승인 2026-10-23). Hc = Level 하면 − 확대기초 상면 (기둥철근.md §2: 12.703 on P1); + * h_cop = 계단 상면(기둥 중심 구간의 상면) − Level 하면 (2.644 on P1, 43.555 − 40.911). Only these two follow the model; + * the other values stay as in COLUMN_P1. + */ +export function columnInputFor(model: PierModel, base: ColumnInput = COLUMN_P1): ColumnInput { + const col = model.columns[0]; + 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 }; +} + export const COLUMN_P1: ColumnInput = { D: 2000, Hc: 12703, diff --git a/src/core/rebar/summary.ts b/src/core/rebar/summary.ts index 65dabe6..3796924 100644 --- a/src/core/rebar/summary.ts +++ b/src/core/rebar/summary.ts @@ -3,7 +3,7 @@ import { barLength, bearingBlockRebar, totals } from './bearing-block'; import { bottomPiece, topBarLength, widthLayout } from './coping-main'; import { shearBars, shearBarsMulti, copingSideBars, SIDE_HOOK_LEG, c14Bars, c14Length, stairBars, stepDevLength, undersideAt, shearULength, capLength, PIECES_PER_POSITION } from './coping'; import { sideRowLevels } from '../coping-geom'; -import { columnRebar, COLUMN_P1 } from './column'; +import { columnRebar, columnInputFor } from './column'; import { bottomBars, crossBars, sideBars, assemblyBars, shearB9 } from './footing'; import { DEFAULT_MATERIAL, FckByMember, fyOf, isAssumedMaterial, Material, materialFor, splitPieces } from './common'; @@ -209,7 +209,7 @@ export function rebarSummary( 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)})` }); // Column (기둥철근.md · plan C4), P1 defaults - const col = columnRebar(COLUMN_P1, mColumn); + const col = columnRebar(columnInputFor(model), mColumn); rows.push({ member: 'column', name: 'P1', size: 'S32', count: col.mainP1, length: null, pieces: col.p1Pieces.map((length) => ({ length, count: col.mainP1 })), note: `외측 r ${col.r1}, 마디 ${col.p1Pieces.join(' / ')} (첫 마디에 갈고리 ${col.p1Hook}, 기둥철근.md §3.2)` }); rows.push({ member: 'column', name: 'P2', size: 'S32', count: col.mainP2, length: null, pieces: col.p2Pieces.map((length) => ({ length, count: col.mainP2 })), note: `내측 r ${col.r2}, 마디 ${col.p2Pieces.join(' / ')}` }); rows.push({ member: 'column', name: '주철근 커플러', size: 'D32용 (L150 Ø60)', count: col.couplers, length: 150, note: '@3000, P2는 +150 mm 상향 엇갈림' }); diff --git a/src/view/rebar3d.ts b/src/view/rebar3d.ts index 27a2962..55e6663 100644 --- a/src/view/rebar3d.ts +++ b/src/view/rebar3d.ts @@ -4,7 +4,7 @@ import { assemblyEndOffset, assemblyJs, b10Levels, b9Bars, FOOTING_DEFAULTS, gri import { bottomPiece, MAIN, widthLayout } from '../core/rebar/coping-main'; import { C14_INSET, c14Bars, CAP, COPING_REBAR, SHEAR_LOOP_STEP, shearBars, shearBarsMulti, shearUHeight, copingSideBars, SIDE_HOOK_LEG, stairBars, undersideAt } from '../core/rebar/coping'; import { sideRowLevels } from '../core/coping-geom'; -import { columnRebar, COLUMN_P1 } from '../core/rebar/column'; +import { columnRebar, columnInputFor, COLUMN_P1 } from '../core/rebar/column'; import { barsAcross, BLOCK_REBAR } from '../core/rebar/bearing-block'; import { blockFramePoint } from '../core/geometry'; import { bendRadius, DEFAULT_MATERIAL, FckByMember, hookLength, Material, materialFor, NOMINAL_DIAMETER } from '../core/rebar/common'; @@ -256,7 +256,8 @@ export function buildRebar3D(m: PierModel, common: Material = DEFAULT_MATERIAL, } // Columns: P1 / P2 main bars on their radii, ties P3 / P5 / P4 as rings, P6 crossties across the column. - const col = columnRebar(COLUMN_P1, mColumn); + const colInput = columnInputFor(m); + const col = columnRebar(colInput, mColumn); const zBot = col.zBot; // mm const ring = (name: string, k: number, sc: number, y: number, r: number, d: number) => { const N = 36; diff --git a/tests/column-model.test.ts b/tests/column-model.test.ts new file mode 100644 index 0000000..54668ee --- /dev/null +++ b/tests/column-model.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from 'vitest'; +import { buildPier } from '../src/core/geometry'; +import { columnInputFor, columnRebar } from '../src/core/rebar/column'; +import { loadP1 } from './helpers'; + +describe('기둥 철근 높이는 모델에서 (Hc · h_cop, 승인 2026-10-23)', () => { + it('P1: Hc 12703 and h_cop 2644 come out of the model, as in 기둥철근.md', () => { + const input = columnInputFor(buildPier(loadP1())); + expect(input.Hc).toBeCloseTo(12703, 0); + expect(input.hcop).toBeCloseTo(2644, 0); + const col = columnRebar(input); + expect(col.zTop2).toBeCloseTo(14539, 0); + expect(col.zTop1).toBeCloseTo(15147, 0); + }); + + it('taper h2 = 3000: Level underside 39.411, Hc 11203, P2 top 13039 (= Level underside + l_d)', () => { + const m = buildPier({ ...loadP1(), copingH2Mm: 3000 }); + const input = columnInputFor(m); + expect(input.Hc).toBeCloseTo(11203, 0); + // h_cop = step top (43.555) − Level underside (39.411): the coping at the column gets taller with h2. + 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); + }); +});