Files
Pier_v1/src/core/shape-options.ts
T
ypshimandClaude Haiku 5.5 2af5cfb144 feat(bearing): 일체 choice gated by the pair eccentricity rule (승인 2026-10-26)
- 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>
2026-10-26 11:56:26 +09:00

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;
}