Files
Aislo/A00_Common/design/design_structure_box.ts
T
eomsangdonandClaude Opus 5.5 1a9055444f fix(box): 본체 「사용자 입력」 빈 칸 저장 뒤 다시 열어도 사용자 입력 · B06 다리가 유입·유출 구조 글 유지(52-19)
- 규격 판정(bodyOf): 가로 · 세로 중 손 값 하나라도 있으면 사용자 입력 + 빈 칸은 빈 채
- 둘 다 비면 표시 키 body_custom 만 저장 · 폼 칸 목록에 넣어 숫자 채우면 지워짐
- B06 폼→조정창 다리: 흙막이 · 날개벽 글을 있음/없음으로 바꿔 적던 것 → 글 그대로
- 시험: 52-2 헬퍼 한 칸 · 두 칸 빈 경우 · ui_checks 52-19

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W4uKaV4ZBhsJN4GHG7yRj4
2026-10-06 01:43:24 +09:00

125 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* design_structure_box.ts
* 횡단배수 › 박스암거 명세 — 사용자 구조물 표 02 고친 판(`docs/raw/구조물정의_표/02_횡단배수_박스암거.md`) 그대로.
*
* 컨테이너 셋(모두 필수): 본체 · 유입 구조 · 유출 구조. 구조 · 정본 = 이 명세 · 이름만 정의(M02)에서 바꿈.
* 저장 키 = pipe_points.json 옛 키 그대로(B06 횡단 · B07 · B08 이 읽는 자리):
* · 본체 = `body_width_m` · `body_height_m` 수 — 규격 칸은 저장 안 함(폭 · 높이로 되찾음 · `bodyOf`)
* · 사용자 입력에 두 칸 다 비면 `body_custom` = 「사용자 입력」 표시만
* · 유입 · 유출 구조 = `wing_in` · `wing_out`(없음 · 날개벽 · 흙막이 · 옛 「있음」 = 날개벽)
* — 엔진은 날개벽만 세움(`_wing_spec` ↔ `wingSpec` 짝)
* 표 02 에 없는 옛 날개벽 치수 칸은 화면에서 빼고 값은 남김(지난 정의 값).
* import 없음(꼴만) — 시험이 이 파일만 옮겨 돌림.
* ========================================================================== */
import type { Instance, SpecItem, StructureSpec } from "./design_structure_spec";
export const BOX_CUSTOM = "사용자 입력";
/** 사용자 입력에 가로 · 세로 둘 다 빈 채 저장했다는 표시(폭 · 높이 수가 없을 때만 적음). */
export const BOX_CUSTOM_KEY = "body_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);
const preset = Object.entries(BOX_PRESETS).find(
([, [w, h]]) => Math.abs(w - width) < 0.05 && Math.abs(h - height) < 0.05,
);
if (preset) return { size: preset[0] };
if (!(width > 0) && !(height > 0) && options[BOX_CUSTOM_KEY] !== BOX_CUSTOM) return { size: "" };
const side = (n: number): string => (n > 0 ? String(n) : "");
return { size: BOX_CUSTOM, width: side(width), height: side(height) };
}
/** 본체 칸 값 → 저장 폭 · 높이 — 규격이면 규격 수(사용자 입력은 명세 키가 적음) · 사용자 입력에 두 칸 다
* 비면 표시 키만(다시 열 때 「사용자 입력」). */
export function bodyOptions(body: Instance | undefined): Record<string, string | number> {
const preset = BOX_PRESETS[body?.size ?? ""];
if (preset) return { body_width_m: preset[0], body_height_m: preset[1] };
const blank = !(body?.width ?? "").trim() && !(body?.height ?? "").trim();
return body?.size === BOX_CUSTOM && blank ? { [BOX_CUSTOM_KEY]: BOX_CUSTOM } : {};
}