feat(view,rebar): column diameter D as a free input on ② (1200–3000 mm, 50 mm steps), with three checks

D is an input on the column card (default 2000 mm). The 3D column, the piles of a cast-in pier and the column rebar follow it;
n_div stays 46. Three checks run on each regenerate and appear in 알림: D below the Level width (2100), D + 2c within the
coping width, and the 주철근 arc spacing ≥ max(1.5·d_b, 40). Spec §5.2 records the rule (approved 2026-10-23).

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-10-23 15:50:52 +09:00
1 parent a6b92ae341
commit 65c53f2b5d
7 files changed
+80 -4

No files matched your search

+2
View File
@@ -7,6 +7,7 @@ import type { PierInput } from './types';
export const EDITABLE_FIELDS = [
{ key: 'copingWidthMm', label: '코핑 폭 B', unit: 'mm' },
{ key: 'copingLengthMm', label: '코핑 길이 L', unit: 'mm' },
{ key: 'columnDiameterMm', label: '기둥 직경 D', unit: 'mm' },
{ key: 'copingH1Mm', label: '끝단 높이 h1', unit: 'mm' },
{ key: 'copingH2Mm', label: '테이퍼 높이 h2', unit: 'mm' },
{ key: 'levelWidthMm', label: 'Level 폭', unit: 'mm' },
@@ -21,6 +22,7 @@ export type EditableKey = (typeof TRACKED_KEYS)[number];
export const FIELD_LABELS: Record<EditableKey, string> = {
copingWidthMm: '코핑 폭 B',
copingLengthMm: '코핑 길이 L',
columnDiameterMm: '기둥 직경 D',
copingH1Mm: '끝단 높이 h1',
copingH2Mm: '테이퍼 높이 h2',
levelWidthMm: 'Level 폭',
+30
View File
@@ -0,0 +1,30 @@
/**
* 기둥 직경 D (승인 2026-10-23): free input 1200–3000 mm in 50 mm steps. Checks, all in mm:
* - D < Level 폭 (the column meets the Level zone);
* - D + 2·c ≤ 코핑 폭 B (c = 125, 기둥철근.md: 기둥 면 ~ P1 중심 피복);
* - the arc spacing of the 주철근 (2π·r1 / n_div, n_div = 46 kept) ≥ max(1.5·d_b, 40), with d_b = 32 (S32).
* Pure: gives the messages for the 알림 list.
*/
import type { PierModel } from './geometry';
export const COLUMN_D_RANGE = { min: 1200, max: 3000, step: 50 } as const;
export const COLUMN_COVER_MM = 125;
const N_DIV = 46;
const DB_MAIN = 31.8;
export function columnDiameterIssues(dMm: number, model: PierModel): string[] {
const out: string[] = [];
if (!Number.isFinite(dMm) || dMm < COLUMN_D_RANGE.min || dMm > COLUMN_D_RANGE.max || Math.round(dMm) % COLUMN_D_RANGE.step !== 0) {
out.push(`기둥 직경은 ${COLUMN_D_RANGE.min}~${COLUMN_D_RANGE.max} mm, ${COLUMN_D_RANGE.step} mm 단위여야 합니다.`);
return out;
}
const levelW = model.copingGeom.levelWidth;
if (dMm >= levelW) out.push(`기둥 직경 ${dMm} mm가 Level 폭 ${levelW} mm 이상입니다.`);
const copingW = model.copingB * 1000;
if (dMm + 2 * COLUMN_COVER_MM > copingW) out.push(`기둥 직경 ${dMm} mm와 피복이 코핑 폭 ${copingW} mm를 넘습니다.`);
const r1 = dMm / 2 - COLUMN_COVER_MM;
const arc = (2 * Math.PI * r1) / N_DIV;
const minArc = Math.max(1.5 * DB_MAIN, 40);
if (arc < minArc) out.push(`주철근 순간격 ${arc.toFixed(1)} mm가 최소 ${minArc.toFixed(1)} mm보다 작습니다.`);
return out;
}
+7 -2
View File
@@ -166,6 +166,8 @@ export interface PierModel {
endBottom: number;
/** Coping heights and Level width in mm, as used by the coping rebar (1단계). */
copingGeom: CopingGeom;
/** Column diameter D (mm) used by the model and the column rebar. */
columnDiameterMm: number;
bearings: BearingLocal[];
blocks: BlockModel[];
columns: ColumnModel[];
@@ -225,6 +227,8 @@ export function buildPier(input: PierInput): PierModel {
const copingH1 = (input.copingH1Mm ?? P1_COPING_GEOM.h1) / 1000;
const copingH2 = (input.copingH2Mm ?? P1_COPING_GEOM.h2) / 1000;
const levelW = (input.levelWidthMm ?? P1_COPING_GEOM.levelWidth) / 1000;
// Column diameter (m): the input, with 2.0 m when absent (승인 2026-10-23).
const colD = (input.columnDiameterMm ?? DIM.columnDiameter * 1000) / 1000;
const planHalfB = B / 2;
const ip1v: Vec2 = { e: ip1.x, n: ip1.y };
const ip2v: Vec2 = { e: ip2.x, n: ip2.y };
@@ -367,7 +371,7 @@ export function buildPier(input: PierInput): PierModel {
k: colK[i],
top: levelBottom,
bottom: footingTop,
diameter: DIM.columnDiameter,
diameter: colD,
}));
const footings: FootingModel[] = [];
@@ -378,7 +382,7 @@ export function buildPier(input: PierInput): PierModel {
if (input.foundationType === '현장타설말뚝') {
columns.forEach((c) => {
piles.push({ s: c.s, k: c.k, n: 0, top: input.footingBottomEl, bottom: pileTip, diameter: DIM.columnDiameter });
piles.push({ s: c.s, k: c.k, n: 0, top: input.footingBottomEl, bottom: pileTip, diameter: colD });
});
} else {
for (const f of footingSets) {
@@ -448,6 +452,7 @@ export function buildPier(input: PierInput): PierModel {
levelBottom,
endBottom,
copingGeom: { h1: copingH1 * 1000, h2: copingH2 * 1000, levelWidth: levelW * 1000 },
columnDiameterMm: colD * 1000,
bearings: [...frontPlaced, ...backPlaced].map((b) => ({ no: b.no, row: b.row, k: b.k, s: b.s, g: b.g, n: b.n, z: b.z })),
blocks,
columns,
+1 -1
View File
@@ -54,7 +54,7 @@ export function columnInputFor(model: PierModel, base: ColumnInput = COLUMN_P1):
const eP1 = Math.round((seg.top - (c3 - 0.1)) * 1000);
// e_top: the coping middle level (Level 하면 + 계단 상면)/2 is the top of P3 and P6 (승인 2026-10-23).
const eTop = Math.round((seg.top - (model.levelBottom + seg.top) / 2) * 1000);
return { ...base, Hc, hcop, eP1, eTop };
return { ...base, Hc, hcop, eP1, eTop, D: model.columnDiameterMm };
}
export const COLUMN_P1: ColumnInput = {
+2
View File
@@ -30,6 +30,8 @@ export interface PierInput {
foundationType: FoundationType;
pileLengthMm: number;
copingWidthMm: number;
/** 기둥 직경 D (mm, 1200–3000 in 50 mm steps, 승인 2026-10-23). Absent = 2000. */
columnDiameterMm?: number;
copingLengthMm: number;
/** 끝단 높이 h1, 테이퍼 높이 h2, Level 폭 (mm). Absent = the P1 values (1000 · 1500 · 2100). */
copingH1Mm?: number;