From d61e5d082f85c751a4c1b0079ec54c365811cc08 Mon Sep 17 00:00:00 2001 From: ypshim Date: Fri, 23 Oct 2026 16:20:37 +0900 Subject: [PATCH] =?UTF-8?q?fix(rebar):=20warn=20when=20the=20pile=20spacin?= =?UTF-8?q?g=20along=20a=20=EC=97=B4=20is=20above=20the=202250=20mm=20maxi?= =?UTF-8?q?mum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The card only checked the minimum spacing. A 12000 mm footing with 5 piles per 열 gives 2650 mm, above the maximum. The card now marks such a row ("최대 2250 mm를 넘습니다. 본수를 늘리세요") and the alert list gets a warning (P02). Tests check 5 and 6 piles on a 12000 mm footing. Co-Authored-By: Claude Haiku 5.5 --- src/core/piles.ts | 7 ++++++- src/main.ts | 13 +++++++++++-- tests/pile-max-spacing.test.ts | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 tests/pile-max-spacing.test.ts diff --git a/src/core/piles.ts b/src/core/piles.ts index caaf6b6..99fe818 100644 --- a/src/core/piles.ts +++ b/src/core/piles.ts @@ -50,12 +50,17 @@ export interface RowLayout { spacing: number; /** True when the spacing is below the minimum (warning, not refused). */ belowMinimum: boolean; + /** True when the spacing is above the maximum 2.25 m (warning: add piles). */ + aboveMaximum: boolean; } +/** Maximum centre spacing of the piles along a 열 (m), spec §4.6 (승인 2026-10-23: 2250 mm). */ +export const PILE_SPACING_MAX = 2.25; + export function rowLayout(counts: number[], g: PileGeometry): RowLayout[] { return counts.map((count) => { const spacing = (g.footingLength - 2 * g.end) / (count - 1); - return { count, spacing, belowMinimum: spacing < g.minSpacing - 1e-9 }; + return { count, spacing, belowMinimum: spacing < g.minSpacing - 1e-9, aboveMaximum: spacing > PILE_SPACING_MAX + 1e-9 }; }); } diff --git a/src/main.ts b/src/main.ts index 175d1f7..c41c858 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,7 @@ import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; import * as XLSX from 'xlsx'; import { parseWorkbook, type SheetReport } from './core/parseWorkbook'; -import { buildPier, type PierModel } from './core/geometry'; +import { buildPier, footingDims, type PierModel } from './core/geometry'; import { NOT_IMPLEMENTED_RULES, validateInput, validatePier } from './core/validate'; import { buildTemplateWorkbook } from './core/template'; import { EDITABLE_FIELDS, FIELD_LABELS, overrideKeys, pendingKeys, revertDraft, type EditableKey, type PierValues } from './core/changes'; @@ -230,6 +230,14 @@ function buildEntry(rawInput: PierInput, checked: boolean): Pier { try { model = buildPier(modelInput); validatePier(input, model, issues); + // Pile rows above the 2250 mm maximum spacing (승인 2026-10-23): a warning in 알림. + if (input.foundationType === '말뚝기초' && model.footings[0]) { + const g = pileGeometry(model.footings[0].length, footingDims(modelInput).W); + const layouts = rowLayout(resolveRowCounts(input.pileRows, g), g); + layouts.forEach((row, j) => { + if (row.aboveMaximum) issues.push({ code: 'P02', level: 'W', pier: input.name, message: `열 ${j + 1}: 말뚝 간격 ${Math.round(row.spacing * 1000)} mm가 최대 2250 mm를 넘습니다. 본수를 늘리세요.` }); + }); + } for (const message of columnDiameterIssues(modelInput.columnDiameterMm ?? 2000, model)) { issues.push({ code: 'D01', level: 'E', pier: input.name, message }); } @@ -801,7 +809,7 @@ function renderPileRows(box: HTMLDivElement, pier: Pier): void { const wrap = el('div', undefined, 'pile-rows'); wrap.appendChild(el('p', `열별 본수 · 합계 ${totalPiles(counts)}본`, 'lbl')); counts.forEach((count, j) => { - const row = el('div', undefined, layout[j].belowMinimum ? 'pile-row warn' : 'pile-row'); + const row = el('div', undefined, layout[j].belowMinimum || layout[j].aboveMaximum ? 'pile-row warn' : 'pile-row'); const minus = el('button', '−', 'btn small'); minus.type = 'button'; minus.setAttribute('aria-label', `열 ${j + 1} 본수 줄이기`); @@ -812,6 +820,7 @@ function renderPileRows(box: HTMLDivElement, pier: Pier): void { plus.addEventListener('click', () => setPileRow(pier.input.name, j, 1)); row.append(el('span', `열 ${j + 1}`), minus, el('span', `${count}본`, 'num'), plus, el('span', `간격 ${Math.round(layout[j].spacing * 1000)} mm`, 'num')); if (layout[j].belowMinimum) row.append(el('span', `최소 ${Math.round(g.minSpacing * 1000)} mm보다 좁습니다`, 'warn-text')); + if (layout[j].aboveMaximum) row.append(el('span', `최대 2250 mm를 넘습니다. 본수를 늘리세요`, 'warn-text')); wrap.appendChild(row); }); wrap.appendChild(el('p', layoutText(counts, g), 'num')); diff --git a/tests/pile-max-spacing.test.ts b/tests/pile-max-spacing.test.ts new file mode 100644 index 0000000..ee9d76f --- /dev/null +++ b/tests/pile-max-spacing.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from 'vitest'; +import { pileGeometry } from '../src/core/geometry'; +import { rowLayout } from '../src/core/piles'; + +describe('말뚝 최대 간격 2250 mm (승인 2026-10-23)', () => { + it('a footing of 12000 mm with 5 piles per 열 gives 2650 mm: above the maximum', () => { + const [row] = rowLayout([5], pileGeometry(12.0, 8.15)); + expect(Math.round(row.spacing * 1000)).toBe(2650); + expect(row.aboveMaximum).toBe(true); + }); + + it('6 piles per 열 give 2120 mm: within the maximum', () => { + const [row] = rowLayout([6], pileGeometry(12.0, 8.15)); + expect(row.aboveMaximum).toBe(false); + expect(row.belowMinimum).toBe(false); + }); +});