From 48e6811600a80dfb1ba4b553ac9667f94c3211ee Mon Sep 17 00:00:00 2001 From: ypshim Date: Mon, 26 Oct 2026 17:23:57 +0900 Subject: [PATCH] =?UTF-8?q?feat(rebar):=20=ED=99=95=EB=8C=80=EA=B8=B0?= =?UTF-8?q?=EC=B4=88=20=ED=8C=A8=EB=84=90=20=EA=B7=9C=EC=B9=99=20=EA=B0=92?= =?UTF-8?q?=20=ED=91=9C=EC=8B=9C=20(=EC=83=81=EB=A9=B4=C2=B72=EB=8B=A8,=20?= =?UTF-8?q?B2=C2=B7B6=20=EB=94=94=EB=A6=AC,=20B9,=20=EC=A1=B0=EB=A6=BD=20?= =?UTF-8?q?=EB=8B=A8=20=EC=88=98)=20=EB=B0=8F=20"=EA=B7=9C=EC=B9=99"=20?= =?UTF-8?q?=ED=91=9C=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - footingRuleValues: 패널 표시값을 계산 함수에서 직접 읽음 (새 공식 없음) - B2·B6 디리 두 값, 조립 단 수(3단) 표시 - "(고정)"은 Δ₂ 100에만 사용, 4s·2s·상면 2s는 "(규칙)"으로 표기 - 테스트: 기본값 P1에서 250 · 100 · 1723 · 500·250 · 500·3단 @400 Co-Authored-By: Claude Haiku 5.5 --- src/core/rebar/footing.ts | 31 ++++++++++++++++++++++++++++ src/main.ts | 26 ++++++++++++++--------- tests/footing-rebar-settings.test.ts | 11 +++++++++- 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/core/rebar/footing.ts b/src/core/rebar/footing.ts index 403ae81..8a5fc09 100644 --- a/src/core/rebar/footing.ts +++ b/src/core/rebar/footing.ts @@ -232,3 +232,34 @@ export function assemblyHeights(H: number, f: FootingRebarSettings = DEFAULT_FOO const B12 = B11.map((z) => z + NOMINAL_DIAMETER.S19); return { B11, B12 }; } + +/** + * Values shown on the 확대기초 panel as rules and derived values (승인 2026-10-26). Each is read from the same functions the + * 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; + delta2: number; + b2Leg: number; + b6Leg: number; + b9Pitch: number; + b9Stagger: number; + assemblyPlanPitch: number; + assemblyVertical: number; + assemblyLevels: number; +} { + const z = layers(H, f); + return { + upper2sAxis: 2 * f.sAxis, + upper2sPerp: 2 * f.sPerp, + delta2: DELTA2, + b2Leg: legB2(H, f), + b6Leg: z.B5 - z.B68, + b9Pitch: 4 * f.sAxis, + b9Stagger: 2 * f.sAxis, + assemblyPlanPitch: 4 * f.sPerp, + assemblyVertical: f.n * f.sV, + assemblyLevels: assemblyJs(H, f).length, + }; +} diff --git a/src/main.ts b/src/main.ts index c80952f..2fce307 100644 --- a/src/main.ts +++ b/src/main.ts @@ -31,7 +31,7 @@ import { COPING_REBAR_RANGES, copingSettingsIssues, type CopingRebarSettings, DE import { COLUMN_REBAR_RANGES, type ColumnRebarSettings, columnSettingsIssues, DEFAULT_COLUMN_REBAR } from './core/rebar/column-settings'; import { columnInputFor, columnRebar } from './core/rebar/column'; import { DEFAULT_FOOTING_REBAR, FOOTING_REBAR_RANGES, type FootingRebarSettings, footingSettingsIssues } from './core/rebar/footing-settings'; -import { legB2 } from './core/rebar/footing'; +import { footingRuleValues } from './core/rebar/footing'; import { columnElevationSvg } from './view/column-elevation-svg'; import { type ColumnSection, columnSectionSvg } from './view/column-section-svg'; import { groundSectionSvg } from './view/ground-svg'; @@ -1339,15 +1339,21 @@ function renderFootingSettings(box: HTMLDivElement, pier: Pier): void { // Fixed rules and derived values (읽기 전용). Derived from the current settings of the first footing. const fs = current; const fixed = el('div', undefined, 'muted small'); - const H = pier.model.footings[0] ? (pier.model.footings[0].top - pier.model.footings[0].bottom) * 1000 : undefined; - fixed.textContent = [ - `B1–B4 간격 2·s (고정) ${2 * fs.sAxis} mm · B5–B8 간격 2·s (고정) ${2 * fs.sPerp} mm`, - `하면 1·2단 간격 Δ₂ (고정) 100 mm`, - `전단 B9 간격 4s · 행 엇갈림 2s (고정) ${4 * fs.sAxis} · ${2 * fs.sAxis} mm`, - `조립 평면 간격 4s · 연직 n·s_v (자동) ${4 * fs.sPerp} · ${fs.n * fs.sV} mm`, - `cycle 배치: 교대 배치 (고정)`, - H === undefined ? 'B2·B6 디리 (자동): 확대기초 없음' : `B2·B6 디리 (자동) z_B1 − z_B24 = ${legB2(H, fs)} mm`, - ].join(' · '); + const foot = pier.model.footings[0]; + const H = foot ? (foot.top - foot.bottom) * 1000 : undefined; + if (H === undefined) { + fixed.textContent = '확대기초 없음 · 확대기초 규칙을 표시하지 않습니다'; + } else { + const r = footingRuleValues(H, fs); + fixed.textContent = [ + `상면·2단 간격 B1·B3 = 2·s교각축 ${r.upper2sAxis} mm · B5·B7 = 2·s교각축직각 ${r.upper2sPerp} mm (규칙)`, + `하면 1·2단 간격 Δ₂ = ${r.delta2} mm (고정)`, + `B2 디리 ${r.b2Leg} mm · B6 디리 ${r.b6Leg} mm (자동, B1·B5 높이까지)`, + `B9 간격 4·s교각축 ${r.b9Pitch} · 행 엇갈림 2·s교각축 ${r.b9Stagger} mm (규칙)`, + `B11·B12 평면 간격 4·s교각축직각 ${r.assemblyPlanPitch} · 연직 n·s_v ${r.assemblyVertical} mm · 조립 단 ${r.assemblyLevels}단 (자동)`, + 'cycle 배치: 교대 배치 (고정)', + ].join(' · '); + } wrap.appendChild(fixed); const msg = el('p', undefined, 'muted small'); const apply = el('button', '적용', 'btn small'); diff --git a/tests/footing-rebar-settings.test.ts b/tests/footing-rebar-settings.test.ts index 9335b30..7a7345f 100644 --- a/tests/footing-rebar-settings.test.ts +++ b/tests/footing-rebar-settings.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { buildPier } from '../src/core/geometry'; -import { assemblyJs, gridPositions } from '../src/core/rebar/footing'; +import { assemblyJs, footingRuleValues, gridPositions } from '../src/core/rebar/footing'; import { DEFAULT_FOOTING_REBAR, FOOTING_REBAR_RANGES, footingSettingsIssues } from '../src/core/rebar/footing-settings'; import { rebarSummary } from '../src/core/rebar/summary'; import { loadP1 } from './helpers'; @@ -58,6 +58,15 @@ describe('확대기초 배근 입력 (승인 2026-10-26, 시안 ③ 확대기초 expect(rowsAxis.find((r) => r.name === 'B5')!.basis).toBe('default'); }); + it('패널 규칙 값 (P1, H 2000): 상면·2단 250 · Δ₂ 100 · B2·B6 1723 · B9 500·250 · 조립 500 · 3단 @400', () => { + const r = footingRuleValues(2000, DEFAULT_FOOTING_REBAR); + expect([r.upper2sAxis, r.upper2sPerp, r.delta2]).toEqual([250, 250, 100]); + expect(r.b2Leg).toBeCloseTo(1723, 0); + expect(r.b6Leg).toBeCloseTo(1723, 0); + expect([r.b9Pitch, r.b9Stagger]).toEqual([500, 250]); + expect([r.assemblyPlanPitch, r.assemblyVertical, r.assemblyLevels]).toEqual([500, 400, 3]); + }); + it('the 조립 단 배수 n reaches the assembly levels', () => { const H = 2000; const base = assemblyJs(H, DEFAULT_FOOTING_REBAR);