feat(rebar): 현장타설 말뚝 · 다주형 코핑 형상과 철근 계산

- 현장타설 기둥·말뚝 형상(cast-in-place-shape)과 주철근·횡철근·커플러 계산(rebar/cast-in-place, cast-layout)
- 다주형 코핑 철근(multicol-coping), 기둥 높이 · 양단부 연장
- 철근표 요약 · 재료 수량 · 3D 뷰 연결과 삽도(cast-rebar-svg)
- 검사 기준 갱신과 회귀 테스트, 검증 스크립트 보관

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-11-06 13:15:01 +09:00
1 parent 3922b330d5
commit 2e0bfb6fc8
29 files changed
+1750 -71

No files matched your search

+51
View File
@@ -0,0 +1,51 @@
import type { PierInput } from './types';
/** EL in metres, lengths in mm. User image and clarifications, 2026-11-05. */
export interface CastInPlaceShapeSettings {
betaLengthMm?: number;
weatheredRockEl?: number;
bedrockEl?: number;
casingOn?: boolean;
casingTopEl?: number;
casingBottomEl?: number;
clayBottomEl?: number;
}
export function castShape(input: Pick<PierInput, 'groundEl' | 'pileLengthMm' | 'castInPlaceShape'>) {
const s = input.castInPlaceShape ?? {};
const pileBottomEl = input.groundEl - input.pileLengthMm / 1000;
const bedrockEl = s.bedrockEl ?? pileBottomEl + 1;
const betaLengthMm = s.betaLengthMm ?? 9000;
return {
pileTopEl: input.groundEl, pileBottomEl,
betaLengthMm, betaEl: input.groundEl - betaLengthMm / 1000,
weatheredRockEl: s.weatheredRockEl ?? pileBottomEl + 5,
bedrockEl,
casingOn: s.casingOn ?? false,
casingTopEl: s.casingTopEl ?? input.groundEl + 3,
casingBottomEl: s.casingBottomEl ?? bedrockEl,
clayBottomEl: s.clayBottomEl,
};
}
export function castShapeIssues(value: unknown): string[] {
if (value === undefined) return [];
if (!value || typeof value !== 'object' || Array.isArray(value)) return ['현장타설 형상 입력 형식이 맞지 않습니다.'];
const out: string[] = [];
for (const [key, v] of Object.entries(value)) {
if (v === undefined) continue;
if (key === 'casingOn') { if (typeof v !== 'boolean') out.push('희생강관 적용 여부가 맞지 않습니다.'); }
else if (!['betaLengthMm', 'weatheredRockEl', 'bedrockEl', 'casingTopEl', 'casingBottomEl', 'clayBottomEl'].includes(key) || typeof v !== 'number' || !Number.isFinite(v) || key === 'betaLengthMm' && v <= 0) out.push(`현장타설 형상 ${key} 값이 맞지 않습니다.`);
}
return out;
}
/** DXF note 4. Do not substitute weathered-rock EL for the independent clay-layer EL. */
export function castLowerZone(input: Pick<PierInput, 'groundEl' | 'pileLengthMm' | 'castInPlaceShape' | 'columnDiameterMm'>) {
const s = castShape(input);
if (s.clayBottomEl === undefined) return null;
const d = (input.columnDiameterMm ?? 2000) / 1000;
const a = s.betaEl - 3 * d;
const b = s.clayBottomEl + d;
return { bottomEl: a, topEl: b, valid: a < b };
}