feat(rebar): 계단부 C15·C16을 모델의 실제 계단으로 만든다 (T형·다주형 공통)

T형은 P1 문서의 고정 위치(2.196~12.000 m)·높이·11개를 써서 코핑 길이가 다르면 철근이 코핑 밖으로 나갔고,
다주형은 높은 계단 전체에 C15 하나만 두었다. 공통 규칙 (step-bars.ts):
- 열마다(전·후열 단차가 있으면 각 쪽) 최저 상면 + h_16(100) 이상 구간이 계단, 경계는 mm 반올림
- 이웃보다 모두 높은 구간 C15(⊓), 나머지 계단 C16(L형, 낮은 쪽 다리, 높은 쪽으로 l_d 연장)
- 폭 방향: T형은 주철근 내부 격자 하나 건너, 다주형은 상면 주철근 하나 건너
- 양쪽이 높은 계단은 RB-S01 알림, RB-M04 폐지. 꺾임은 편 좌표의 한 개(꺾은 철근)
P1 문서값(C15 11 × 3852, C16 22 × 4070/4069, 137.5 · 10@250)을 그대로 재현한다.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-11-10 16:53:15 +09:00
1 parent 9f336e2be4
commit 7340f8c1a1
8 files changed
+263 -105

No files matched your search

+8 -13
View File
@@ -8,6 +8,7 @@
*/
import type { PierModel } from '../geometry';
import { DEFAULT_MATERIAL, developmentLength, fyOf, hookLength, type Material, NOMINAL_DIAMETER, spliceLength } from './common';
import { stepBars } from './step-bars';
export type CopingPoint = [number, number, number];
export interface CopingBar { name: string; size: string; diameterMm: number; pts: CopingPoint[]; length: number }
@@ -48,7 +49,7 @@ export const MULTI_REBAR = {
standardMm: 8000,
/** C14: vertical pitch, pitch along the axis, stagger between rows. */
c14Vertical: 0.6, c14Along: 1.2, c14Stagger: 0.6,
/** h_16: a step at least this much above the end top gets C15 (m). */
/** h_16: a step at least this much above the row's lowest top gets C15 / C16 (m, step-bars.ts). */
stepMin: 0.1,
} as const;
@@ -283,18 +284,12 @@ export function multicolCopingInfo(model: PierModel, material: Material = DEFAUL
}
});
// --- C15 계단부 (§8): 끝단보다 h_16 이상 높은 계단 전체에 ⊓ 하나 ------------------------------------------
const front = model.segments;
const endTop = Math.min(front[0].top, front.at(-1)!.top);
const high = front.filter(g => g.top - endTop >= MULTI_REBAR.stepMin - 1e-9);
if (high.length) {
const from = high[0].s0 + c, to = high.at(-1)!.s1 - c;
const z15 = Math.min(...high.map(g => g.top)) - c;
const ld = developmentLength(d('H19'), fy('H19'), material.fck) / 1000;
top.filter((_, i) => i % 2 === 0).forEach(y => add('C15', 'H19', [[from, z15 - ld, y], [from, z15, y], [to, z15, y], [to, z15 - ld, y]]));
const above = Math.max(...front.map(g => g.top)) - (z15 + c);
if (above > MULTI_REBAR.stepMin + 1e-9) warnings.push({ code: 'RB-M04', message: `가장 높은 계단 상면이 C15보다 ${Math.round(above * 1000)} mm 높습니다 (h_16 ${MULTI_REBAR.stepMin * 1000} 초과). C16 병행은 미정 (Q-M7).` });
}
// --- C15 · C16 계단부: T형과 같은 규칙 (결정 2026-11-10, step-bars.ts) ---------------------------------------
// 가장 높은 계단마다 ⊓ (C15), 그 밖의 계단(최저 상면 + h_16 이상)마다 L형 (C16). 폭 방향은 상면 주철근 하나 건너.
const stairs = stepBars(model, { cover: c * 1000, ld: Math.round(developmentLength(d('H19'), fy('H19'), material.fck)), d: d('H19'),
stepMin: MULTI_REBAR.stepMin * 1000, across: top.filter((_, i) => i % 2 === 0) });
for (const b of stairs.bars) add(b.name, 'H19', b.pts.map(([x, z]): CopingPoint => [x / 1000, z, b.n]));
for (const message of stairs.warnings) warnings.push({ code: 'RB-S01', message });
// RB-M03: 상면 1단 외 철근이 12 m를 넘으면 이음 미정 (Q-M12).
const long = [...new Set(bars.filter(x => !['C1', 'C4'].includes(x.name) && x.length > 12000).map(x => x.name))];