- 박스암거 명세(design_structure_box) — 본체 규격 2.0×2.0 · 3.0×3.0 · 사용자 입력(가로 × 세로 숫자 칸) · 유입 · 유출 구조 없음 · 날개벽 · 흙막이 · 컨테이너마다 구조물 조합 잇기(gboxbody · gboxin · gboxout) - 저장 키 = 옛 키 그대로(body_width_m · body_height_m · wing_in · wing_out) · 규격 칸은 저장 안 함(폭 · 높이로 되찾음) · 옛 「있음」 = 날개벽 · 옛 날개벽 치수는 화면에서 빼고 지난 정의 값 - 명세 등록(STRUCTURE_SPECS) · 배관 시험 헬퍼가 같은 폴더 명세를 옮겨 읽음 - B06 날개벽 설치 판정 한 곳(wingInstalled) — 흙막이 = 날개벽 안 세움 · 저장 글을 있음/없음으로 안 덮음 · 날개벽 조정 행은 명세 유입 · 유출 칸으로 - 정의 판 7 — box_culvert 이름 표 · 그룹 셋 · wing_in/out 선택지 · 판 6 이력 보관 - 시험 test_52_structure_spec_box · 레지스트리 · 새 구조물 기본값 기대값 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W4uKaV4ZBhsJN4GHG7yRj4
118 lines
4.4 KiB
TypeScript
118 lines
4.4 KiB
TypeScript
/* =============================================================================
|
||
* design_structure_box.ts
|
||
* 횡단배수 › 박스암거 명세 — 사용자 구조물 표 02 고친 판(`docs/raw/구조물정의_표/02_횡단배수_박스암거.md`) 그대로.
|
||
*
|
||
* 컨테이너 셋(모두 필수): 본체 · 유입 구조 · 유출 구조. 구조 · 정본 = 이 명세 · 이름만 정의(M02)에서 바꿈.
|
||
* 저장 키 = pipe_points.json 옛 키 그대로(B06 횡단 · B07 · B08 이 읽는 자리):
|
||
* · 본체 = `body_width_m` · `body_height_m` 수 — 규격 칸은 저장 안 함(폭 · 높이로 되찾음 · `bodyOf`)
|
||
* · 유입 · 유출 구조 = `wing_in` · `wing_out`(없음 · 날개벽 · 흙막이 · 옛 「있음」 = 날개벽)
|
||
* — 엔진은 날개벽만 세움(`_wing_spec` ↔ `wingSpec` 짝)
|
||
* 표 02 에 없는 옛 날개벽 치수 칸은 화면에서 빼고 값은 남김(지난 정의 값).
|
||
* import 없음(꼴만) — 시험이 이 파일만 옮겨 돌림.
|
||
* ========================================================================== */
|
||
|
||
import type { Instance, SpecItem, StructureSpec } from "./design_structure_spec";
|
||
|
||
export const BOX_CUSTOM = "사용자 입력";
|
||
/** 본체 규격 → [가로, 세로](m) — 실무 관측 규격(2026-08-17 사용자 확정). */
|
||
const BOX_PRESETS: Record<string, readonly [number, number]> = {
|
||
"2.0×2.0": [2, 2],
|
||
"3.0×3.0": [3, 3],
|
||
};
|
||
|
||
/** 유입 · 유출 구조 — 같은 선택지 · 키만 다름. */
|
||
const structureItem = (key: string): SpecItem => ({
|
||
id: "structure",
|
||
label: "구조",
|
||
kind: "select",
|
||
key,
|
||
choices: [
|
||
{ value: "없음", label: "없음" },
|
||
{ value: "날개벽", label: "날개벽" },
|
||
{ value: "흙막이", label: "흙막이" },
|
||
],
|
||
});
|
||
|
||
/** 사용자 입력일 때만 서는 숫자 칸. */
|
||
const customSide = (id: string, label: string, key: string): SpecItem => ({
|
||
id,
|
||
label,
|
||
kind: "number",
|
||
step: 0.1,
|
||
by: { item: "size", map: { [BOX_CUSTOM]: { key } } },
|
||
});
|
||
|
||
export const BOX_SPEC: StructureSpec = {
|
||
typeId: "box_culvert",
|
||
containers: [
|
||
{
|
||
id: "body",
|
||
title: "본체",
|
||
required: true,
|
||
link: "gboxbody",
|
||
items: [
|
||
{
|
||
id: "size",
|
||
label: "본체 규격",
|
||
kind: "select",
|
||
choices: [
|
||
...Object.keys(BOX_PRESETS).map((value) => ({
|
||
value,
|
||
label: value.replace("×", " × "),
|
||
})),
|
||
{ value: BOX_CUSTOM, label: BOX_CUSTOM },
|
||
],
|
||
},
|
||
customSide("width", "가로 (m)", "body_width_m"),
|
||
customSide("height", "세로 (m)", "body_height_m"),
|
||
],
|
||
},
|
||
{
|
||
id: "inlet",
|
||
title: "유입 구조",
|
||
required: true,
|
||
link: "gboxin",
|
||
items: [structureItem("wing_in")],
|
||
},
|
||
{
|
||
id: "outlet",
|
||
title: "유출 구조",
|
||
required: true,
|
||
link: "gboxout",
|
||
items: [structureItem("wing_out")],
|
||
},
|
||
],
|
||
// 표 02 에 없는 옛 날개벽 치수 — 화면에서 빼고 값은 남김(지난 정의 값)
|
||
retired: ["in", "out"].flatMap((side) =>
|
||
["height_m", "length_m", "angle_deg"].map((field) => `wing_${side}_${field}`),
|
||
),
|
||
};
|
||
|
||
/** 옛 저장 → 폼에 줄 옵션 — 설치 「있음」 = 날개벽(같은 뜻 · 저장하면 새 값으로 굳음). */
|
||
export function boxOptionsIn(
|
||
options: Record<string, string | number>,
|
||
): Record<string, string | number> {
|
||
const out = { ...options };
|
||
for (const key of ["wing_in", "wing_out"]) if (out[key] === "있음") out[key] = "날개벽";
|
||
return out;
|
||
}
|
||
|
||
/** 저장 폭 · 높이 → 본체 칸 값 — 규격과 맞으면 그 규격 · 아니면 사용자 입력 · 없으면 빈 값. */
|
||
export function bodyOf(options: Record<string, string | number>): Instance {
|
||
const width = Number(options.body_width_m);
|
||
const height = Number(options.body_height_m);
|
||
if (!(width > 0 && height > 0)) return { size: "" };
|
||
const preset = Object.entries(BOX_PRESETS).find(
|
||
([, [w, h]]) => Math.abs(w - width) < 0.05 && Math.abs(h - height) < 0.05,
|
||
);
|
||
return preset
|
||
? { size: preset[0] }
|
||
: { size: BOX_CUSTOM, width: String(width), height: String(height) };
|
||
}
|
||
|
||
/** 본체 칸 값 → 저장 폭 · 높이 — 규격이면 규격 수(사용자 입력은 명세 키가 적음). */
|
||
export function bodyOptions(body: Instance | undefined): Record<string, number> {
|
||
const preset = BOX_PRESETS[body?.size ?? ""];
|
||
return preset ? { body_width_m: preset[0], body_height_m: preset[1] } : {};
|
||
}
|