From a6f362df5656849e6f269374c91998514d231396 Mon Sep 17 00:00:00 2001 From: ypshim Date: Mon, 26 Oct 2026 14:10:49 +0900 Subject: [PATCH] =?UTF-8?q?feat(rebar):=20range=20checks=20for=20the=20blo?= =?UTF-8?q?ck=20rebar=20settings=20(=EC=8A=B9=EC=9D=B8=202026-10-26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RB01 a setting outside its range (cover 50–100, side cover 50–100, first gap 0–150, spacing S1/S2 100–200, leg 500–1000); RB02 S1 depth at or above the block height; RB03 the legs do not reach into the coping. Co-Authored-By: Claude Haiku 5.5 --- src/core/rebar/block-check.ts | 54 +++++++++++++++++++++++++++++++++ src/core/validate.ts | 2 ++ tests/block-rebar-check.test.ts | 22 ++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 src/core/rebar/block-check.ts create mode 100644 tests/block-rebar-check.test.ts diff --git a/src/core/rebar/block-check.ts b/src/core/rebar/block-check.ts new file mode 100644 index 0000000..0b6a64d --- /dev/null +++ b/src/core/rebar/block-check.ts @@ -0,0 +1,54 @@ +import type { PierModel } from '../geometry'; +import type { Issue, PierInput } from '../types'; +import { barDepths, DEFAULT_BLOCK_REBAR, type BlockRebarSettings } from './bearing-block'; + +/** + * 받침블럭 철근 입력 범위 (승인 2026-10-26, 제안 범위 확인). + * RB01: a setting outside its range. RB02: the S2 bar sits at or below the block top-to-bottom height. + * RB03: the bar legs do not reach into the coping (묻힘 < 0). + */ +export const BLOCK_REBAR_RANGES: Record = { + coverTop: [50, 100], + coverSide: [50, 100], + firstGap: [0, 150], + spacingS1: [100, 200], + spacingS2: [100, 200], + legLength: [500, 1000], +}; + +const LABELS: Record = { + coverTop: '상면 피복', + coverSide: '측면 피복', + firstGap: '첫 철근 최소 여유', + spacingS1: '간격 S1', + spacingS2: '간격 S2', + legLength: '다리 길이', +}; + +export function checkBlockRebar(input: PierInput, model: PierModel): Issue[] { + const s: BlockRebarSettings = { ...DEFAULT_BLOCK_REBAR, ...input.blockRebar }; + const out: Issue[] = []; + const add = (code: string, message: string) => out.push({ code, level: 'W', pier: input.name, message }); + + for (const key of Object.keys(BLOCK_REBAR_RANGES) as (keyof BlockRebarSettings)[]) { + const [lo, hi] = BLOCK_REBAR_RANGES[key]; + if (s[key] < lo || s[key] > hi) add('RB01', `${LABELS[key]} ${s[key]} mm가 범위 ${lo}–${hi} mm를 벗어납니다.`); + } + + const depth = barDepths(s); + for (const b of model.blocks) { + const height = (b.top - b.bottom) * 1000; + if (depth.S1 >= height) { + add('RB02', `블럭 높이 ${height.toFixed(0)} mm보다 S1 깊이 ${depth.S1.toFixed(1)} mm가 깊습니다.`); + break; + } + } + for (const b of model.blocks) { + const height = (b.top - b.bottom) * 1000; + if (s.legLength - (height - depth.S1) < 0) { + add('RB03', `다리 끝이 코핑 안에 묻히지 않습니다 (블럭 ${b.no}).`); + break; + } + } + return out; +} diff --git a/src/core/validate.ts b/src/core/validate.ts index 5877960..e7fcca9 100644 --- a/src/core/validate.ts +++ b/src/core/validate.ts @@ -2,6 +2,7 @@ import { DIM, type PierModel } from './geometry'; import type { Issue, PierInput, PierType } from './types'; import { validateGround, validatePileEmbedment, validateStrataNValues } from './ground'; import { integratedCheck } from './bearing-layout'; +import { checkBlockRebar } from './rebar/block-check'; const EXPECTED_COLUMNS: Record = { 'T형': 1, '2주형': 2, '3주형': 3 }; @@ -94,6 +95,7 @@ export function validatePier(input: PierInput, model: PierModel, issues: Issue[] validateGround(input, issues); validatePileEmbedment(input, model, issues); validateStrataNValues(input, issues); + issues.push(...checkBlockRebar(input, model)); // 일체 (승인 2026-10-26): the model builds one stepped block per pair when allowed; otherwise it keeps 분리. if (input.bearingLayout === '일체') { const check = integratedCheck(model.blocks); diff --git a/tests/block-rebar-check.test.ts b/tests/block-rebar-check.test.ts new file mode 100644 index 0000000..f364087 --- /dev/null +++ b/tests/block-rebar-check.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'vitest'; +import { buildPier } from '../src/core/geometry'; +import { checkBlockRebar } from '../src/core/rebar/block-check'; +import { loadP1 } from './helpers'; + +const codes = (input = loadP1(), extra = {}) => checkBlockRebar({ ...input, ...extra }, buildPier(input)).map((i) => i.code); + +describe('받침블럭 철근 입력 범위 (승인 2026-10-26)', () => { + it('the P1 defaults are inside every range', () => { + expect(codes()).toEqual([]); + }); + + it('RB01: a setting outside its range (상면 피복 120, 간격 S2 300, 다리 1100)', () => { + expect(codes(loadP1(), { blockRebar: { coverTop: 120 } })).toContain('RB01'); + expect(codes(loadP1(), { blockRebar: { spacingS2: 300 } })).toContain('RB01'); + expect(codes(loadP1(), { blockRebar: { legLength: 1100 } })).toContain('RB01'); + }); + + it('RB02: the S1 depth at or below the block height', () => { + expect(codes(loadP1(), { blockRebar: { coverTop: 150 } })).toContain('RB02'); + }); +});