- PierInput.bearingLayout: 분리 (default) or 일체, tracked for changes. - 일체 is selectable only when there is at least one pair and every pair is within 100 mm; otherwise the chip is disabled with the reason. - Validation: B01 (info) when 일체 is chosen and allowed, the block shape is still the 분리 shape; B02 (warning) when it is not allowed and the model falls back to 분리. - The 일체 block shape (2500 × 1200) is not modelled yet. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
63 lines
2.7 KiB
TypeScript
63 lines
2.7 KiB
TypeScript
/**
|
|
* 형상 선택지 (spec §5.2 · plan C5). Only the default of each group can be chosen until the rules for the other
|
|
* options are confirmed (Q6). The other options are shown but cannot be selected.
|
|
*/
|
|
export type MemberId = 'bearing' | 'coping' | 'column' | 'footing' | 'ground';
|
|
|
|
export interface ShapeOption {
|
|
label: string;
|
|
/** 굵은 글씨 in the spec: the default that the 1단계 model builds. */
|
|
isDefault: boolean;
|
|
/** The rule for this option is confirmed (승인 2026-10-26: 하상), so it can be chosen even if it is not the default. */
|
|
confirmed?: boolean;
|
|
}
|
|
|
|
export interface ShapeGroup {
|
|
title: string;
|
|
options: ShapeOption[];
|
|
}
|
|
|
|
const d = (label: string): ShapeOption => ({ label, isDefault: true });
|
|
const x = (label: string): ShapeOption => ({ label, isDefault: false });
|
|
const ok = (label: string): ShapeOption => ({ label, isDefault: false, confirmed: true });
|
|
|
|
export const SHAPE_GROUPS: Record<MemberId, ShapeGroup[]> = {
|
|
bearing: [{ title: '배치', options: [d('분리'), ok('일체')] }],
|
|
coping: [
|
|
{ title: '교좌면', options: [d('직각'), x('모따기'), x('라운드')] },
|
|
{ title: '하면', options: [d('직선'), x('곡선 오목'), x('곡선 볼록')] },
|
|
],
|
|
column: [
|
|
{ title: '단면', options: [d('원형'), x('트랙'), x('사각')] },
|
|
{ title: '중공', options: [d('중실'), x('중공')] },
|
|
],
|
|
footing: [
|
|
{ title: '형상', options: [d('직사각'), x('경사')] },
|
|
{ title: '모따기', options: [d('없음'), x('있음')] },
|
|
{ title: '말뚝 종류', options: [d('강관'), x('PHC'), x('복합')] },
|
|
{ title: '경사말뚝', options: [d('없음'), x('있음')] },
|
|
],
|
|
ground: [{ title: '위치', options: [d('육상'), ok('하상'), ok('해상')] }],
|
|
};
|
|
|
|
export const UNDEFINED_REASON = '규칙 미정 (Q6 확정 전 선택 불가)';
|
|
|
|
/** Options currently selectable: the defaults and the options whose rule is confirmed. */
|
|
export function isSelectable(option: ShapeOption): boolean {
|
|
return option.isDefault || option.confirmed === true;
|
|
}
|
|
|
|
/** The default summary of a member, e.g. "교좌면 직각 · 하면 직선". */
|
|
export function defaultSummary(member: MemberId): string {
|
|
return SHAPE_GROUPS[member].map((g) => g.options.find((o) => o.isDefault)!.label).join(' · ');
|
|
}
|
|
|
|
/** Chooses an option in a group. Refuses options whose rules are not confirmed. */
|
|
export function choose(member: MemberId, groupIndex: number, optionLabel: string): string {
|
|
const group = SHAPE_GROUPS[member][groupIndex];
|
|
const option = group?.options.find((o) => o.label === optionLabel);
|
|
if (!option) throw new Error(`알 수 없는 선택지: ${optionLabel}`);
|
|
if (!isSelectable(option)) throw new Error(`${optionLabel}: ${UNDEFINED_REASON}`);
|
|
return option.label;
|
|
}
|