Files
Aislo/A00_Common/design/design_structure_guard.ts
T
eomsangdonandClaude Opus 5.5 209383ed17 feat(b05,b06,b07): 도수로/돌붙임 띠 = 낮은 쪽 끝단에서 · 원지반 닿으면 끝 · 길이 = 그 길이 · 모든 시설 같은 규칙과 길이 칸 (294-4)
- 공용 chuteRun(Wall_Hatch) — 길이 비움 = 원지반 닿으면 끝 · 넣음 = 사면 따라 그 길이 · 사면보다 길면 원지반 따라 이어 그 길이 · 끝단이 이미 원지반이면 안 그림
- 물넘이 — 띠 시작 = 늘인 노면 끝(294-3) · 아니면 유출 쪽 노견 끝 · 원지반 닿으면 끝(양측 절토 = 띠 없음)
- 배관 · 박스 · 세월교 — 띠 계산이 원지반(layout.ground)을 받아 같은 규칙 · 3D 띠도 같음 · 코리도 해시에 길이
- 명세 CHUTE_ITEMS 맨 아래 「길이 (m)」(chute_add_{n}_length_m · 그림 전용) · 물넘이는 공용 칸 그대로
- 면적 자동 = 물넘이 · 세월교만 월류 폭 × 길이(배관 · 박스 = 폭 정의 없어 손 입력 그대로 · 브레인 정함)
- 길이는 집계 열 · B08 수량(면적만)에 안 실림 · 마스터 정의 이름 표는 안 건드림(이름 표 대조 시험에서 이 칸만 밖)
- 시험 test_294_3 · 286_1 · 52 · 65 · 152_3 기대값

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
2026-10-12 21:58:16 +09:00

161 lines
6.0 KiB
TypeScript

/* =============================================================================
* design_structure_guard.ts
* 흙막이 공통 양식 — 배관(표 01) · 박스암거(표 02) · 세월교(표 04)가 같은 칸을 씀(PLAN 65-2).
*
* · 흙막이 = 형태 6 · 길이 · 높이 · 기준측점 전 · 후(길이 = 전 + 후 맞물림)
* · 필수 보호공(유입 · 유출)은 구조 갈래마다 저장 키(`{side}_revet_*` 옛 키) · 선택 흙막이 · 도수로/돌붙임은
* 인스턴스 키(`soil_add_{n}_*` · `chute_add_{n}_*`)
* import = 꼴만 — 시험이 이 폴더 파일만 옮겨 돌림.
* ========================================================================== */
import type { SpecChoice, SpecContainer, SpecItem } from "./design_structure_spec";
export const SOIL_FORMS: SpecChoice[] = [
{ value: "돌쌓기(찰)", label: "돌 쌓기(찰)" },
{ value: "돌쌓기(메)", label: "돌 쌓기(메)" },
{ value: "콘크리트", label: "콘크리트" },
{ value: "돌망태", label: "돌망태" },
{ value: "통나무·목재틀", label: "통나무/목재틀" },
{ value: "바자", label: "바자" },
];
/** 흙막이 기본값(54-36 · 사면안정 흙막이 정의 기본값과 같음). */
const SOIL_DEFAULTS: Record<string, string> = {
form: "돌쌓기(찰)",
length: "10",
height: "1.5",
before: "5",
after: "5",
};
/** 형태 · 길이 · 높이 · 전 · 후 — 필수 보호공은 구조 갈래마다 옛 키(`keys`) · 선택 흙막이는 인스턴스 키.
* `forms` = 갈래별 형태 선택지(배관 집수정 등) · `defaults` = 새 상자 · 흙막이를 고를 때 칸에 실제 값. */
export function guardItems(
keys?: Record<string, Record<string, string>>,
forms?: Record<string, SpecChoice[]>,
defaults = !keys,
): SpecItem[] {
const by = (field: string, choices?: Record<string, SpecChoice[]>) =>
keys
? {
item: "structure",
map: Object.fromEntries(
// 키 없는 갈래 = 그 구조에 없는 칸(집수정 길이 · 전 · 후) — 가림
Object.entries(keys)
.filter(([, k]) => k[field])
.map(([structure, k]) => [
structure,
{ key: k[field], choices: choices?.[structure] },
]),
),
}
: undefined;
const items: SpecItem[] = [
{ id: "form", label: "형태", kind: "select", choices: SOIL_FORMS, by: by("form", forms) },
{ id: "length", label: "길이 (m)", kind: "number", split: "length", step: 1, by: by("length") },
{ id: "height", label: "높이 (m)", kind: "number", step: 0.1, by: by("height") },
{
id: "before",
label: "기준측점 전 (m)",
kind: "number",
split: "before",
step: 0.5,
by: by("before"),
},
{
id: "after",
label: "기준측점 후 (m)",
kind: "number",
split: "after",
step: 0.5,
by: by("after"),
},
];
return defaults ? items.map((item) => ({ ...item, default: SOIL_DEFAULTS[item.id] })) : items;
}
export const revetKeys = (side: "inlet" | "outlet"): Record<string, string> => ({
form: `${side}_revet_form`,
length: `${side}_revet_length_m`,
height: `${side}_revet_height_m`,
before: `${side}_revet_before_m`,
after: `${side}_revet_after_m`,
});
/** 세월교 · 박스 유입 · 유출 흙막이 칸(79-7) — 높이 빈 칸 = 자동(`heightHint` · 횡단도가 셈) ·
* 유입만 절토부 앞 수평공간 칸(`inlet_revet_berm_m` · 유출은 없음 · 89-4)을 더함 · `wingBerm` = 세월교
* 유입 날개벽에도 같은 칸(수평 여유 · 107-3). */
export function bodyGuardItems(
side: "inlet" | "outlet",
heightHint: string,
wingBerm = false,
): SpecItem[] {
const items = guardItems({ 흙막이: revetKeys(side) }, undefined, true).map((item) =>
item.id === "height" ? { ...item, default: undefined, hint: heightHint } : item,
);
if (side === "outlet") return items;
const branch = { key: `${side}_revet_berm_m` };
const berm: SpecItem = {
id: "berm",
label: "수평공간 (m)",
kind: "number",
step: 0.1,
default: "1",
// 세월교 유입 날개벽 = 같은 칸 · 같은 키(끝이 묻히면 수평 여유 뒤 절토 · 면적 밖)
by: {
item: "structure",
map: wingBerm ? { 흙막이: branch, 날개벽: branch } : { 흙막이: branch },
},
};
return [...items, berm];
}
/** 추가 흙막이(선택 · 여러 개) — 사면안정 흙막이(표 09 · 54-26)와 같은 칸 · 구조 고르기 없음(54-30). */
export const SOIL_ADD: SpecContainer = {
id: "soil_add",
title: "흙막이",
required: false,
prefix: "soil_add",
link: "gsoiladd",
items: guardItems(),
};
/** 도수로/돌붙임 칸 — 구조(기본 도수로) · 형태(기본 메붙임) · 두께(cm · 기본 45) · 면적(㎡ · 기본 30 · 98-3) · 물넘이 포장(`ford_chute`)도 같은 칸(173-5). */
export const CHUTE_ITEMS: SpecItem[] = [
{
id: "structure",
label: "구조",
kind: "select",
choices: [
{ value: "도수로", label: "도수로" },
{ value: "돌붙임", label: "돌붙임" },
],
default: "도수로",
},
{
id: "bond",
label: "형태",
kind: "select",
choices: [
{ value: "찰붙임", label: "찰붙임" },
{ value: "메붙임", label: "메붙임" },
],
default: "메붙임",
},
{ id: "thickness", label: "두께 (cm)", kind: "number", step: 1, default: "45" },
{ id: "area", label: "면적 (㎡)", kind: "number", step: 0.1, default: "30" },
// 길이(294-4 · `{접두}_{n}_length_m`) — 띠를 사면 따라 그릴 길이 · 그림 전용(수량 · 내역 · 일위대가에 안 씀)
{ id: "length_m", label: "길이 (m)", kind: "number", step: 0.1 },
];
/** 도수로/돌붙임(선택 · 하나만 · 54-48) — 옛 둘 이상은 보이되 [추가]만 숨김. */
export const CHUTE_ADD: SpecContainer = {
id: "chute_add",
title: "도수로/돌붙임",
required: false,
prefix: "chute_add",
link: "gchuteadd",
max: 1,
items: CHUTE_ITEMS,
};