merge: main_laptop_1 → sub_laptop_7 52-3 — 충돌 다섯 = 양쪽 다 살림
- 명세 목록 = 박스암거 · 세월교 · 물넘이 포장 · 노면공 · 기타 전부 - B05 시설 폼 = BOX 명세 폼(메인) + 물넘이 포장 명세 폼(52-3) · 옛 BOX 프리셋 · 월류 칸 뺌 - 병합 키 = BOX 명세 키 + 물넘이 포장 경사각도 · 도수로/돌붙임 머리 - 표 채우기 컨테이너 = 물넘이 포장 도수로/돌붙임 + 기타 상세 내용 - 구조물정의 = 양쪽 그룹(box_culvert · ford_pavement) · 판 12 · 직전 메인 판 11 은 이력 판_11 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYJJRCfHfdCGNhmXTjQKV7
This commit is contained in:
@@ -18,11 +18,9 @@
|
||||
* 유입·유출 길이가 정해지면 위치 칸(시작·종료 측점)을 기준 − 유입 ~ 기준 + 유출로
|
||||
* 자동 채운다. 형식·치수 제원은 B06/B07 필수 승격 대상이라 보이되 비필수다.
|
||||
*
|
||||
* BOX암거(2026-08-17 사용자 확정): 본체는 실무 관측 규격 2.0×2.0·3.0×3.0을 프리셋
|
||||
* 셀렉트로 받고(기본 2.0×2.0), 그 밖의 규격만 "사용자 지정"으로 폭·높이를 직접
|
||||
* 받는다 — 저장 키는 어느 쪽이든 body_width_m·body_height_m 숫자 그대로다.
|
||||
* 날개벽은 유입·유출이 같은 조각(createWingFields)을 쓰고 설치 있음·짧은쪽 높이
|
||||
* 1m·길이 2m·각도 45°가 기본이며, 설치를 "없음"으로 바꾸면 제원 칸이 접힌다.
|
||||
* BOX암거 = 사용자 구조물 표 02 명세(PLAN 52-2 · `design_structure_box`) — 본체 규격 ·
|
||||
* 유입 · 유출 구조. 저장 키는 body_width_m·body_height_m 숫자 · wing_in·wing_out 그대로.
|
||||
* 날개벽 조각(createWingFields)은 숨김 — B06 조정 행 자리만 BOX · 세월교 명세 유입 · 유출 칸으로 옮겨 붙음.
|
||||
*
|
||||
* 짝: 시설 속성 붙이기 `carryIndices` ↔ 서버 `common_util/common_util_drainage_pipes.py`
|
||||
* `carry_indices` — 같은 규칙(허용 오차 안 · 한 원본 한 번) · 거울 시험 test_b05_facility_carry.
|
||||
@@ -35,8 +33,6 @@ import type {
|
||||
} from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
|
||||
|
||||
import {
|
||||
BOX_SIZE_CUSTOM,
|
||||
BOX_SIZE_PRESETS,
|
||||
createRevetSideGroup,
|
||||
createWingFields,
|
||||
grid,
|
||||
@@ -44,15 +40,19 @@ import {
|
||||
INLET_REVET_KEYS,
|
||||
type InletStructureKind,
|
||||
labeled,
|
||||
numberInput,
|
||||
optionalSelect,
|
||||
OUTLET_REVET_KEYS,
|
||||
putNumber,
|
||||
WING_IN_KEYS,
|
||||
WING_OUT_KEYS,
|
||||
} from "./B05_Profile_UI_Drainage_Facility_Fields";
|
||||
import { createSpecForm, type SpecLinks } from "../A00_Common/design/design_structure_form";
|
||||
import { PIPE_SPEC } from "../A00_Common/design/design_structure_pipe";
|
||||
import {
|
||||
BOX_SPEC,
|
||||
bodyOf,
|
||||
bodyOptions,
|
||||
boxOptionsIn,
|
||||
} from "../A00_Common/design/design_structure_box";
|
||||
import { FORD_SPEC, withLegacyGuards } from "../A00_Common/design/design_structure_ford";
|
||||
import { fordHeightHint, fordHeightM } from "../A00_Common/design/design_structure_ford_calc";
|
||||
import { guardHeightHint, pipeLengthM } from "../A00_Common/design/design_structure_pipe_calc";
|
||||
@@ -359,23 +359,9 @@ export function createFacilityOptionsForm(
|
||||
return merged;
|
||||
};
|
||||
|
||||
// ── BOX암거 — 본체 규격(프리셋 + 사용자 지정)·날개벽(유입·유출 개별) ────
|
||||
// 폭·높이 자유 입력은 "사용자 지정"을 골랐을 때만 펼친다(2026-08-17 사용자 확정).
|
||||
const boxSize = optionalSelect("— 안 정함 —", [
|
||||
...BOX_SIZE_PRESETS.map(([label]) => label),
|
||||
BOX_SIZE_CUSTOM,
|
||||
]);
|
||||
const boxWidth = numberInput("0.1", "0");
|
||||
const boxHeight = numberInput("0.1", "0");
|
||||
const boxCustomRow = grid(
|
||||
labeled("본체 폭 (m)", boxWidth, "body_width_m"),
|
||||
labeled("본체 높이 (m)", boxHeight, "body_height_m"),
|
||||
);
|
||||
const boxWrap = document.createElement("div");
|
||||
boxWrap.append(
|
||||
grid(labeled("본체 규격 (m)", boxSize, ["body_width_m", "body_height_m"])),
|
||||
boxCustomRow,
|
||||
);
|
||||
// ── BOX암거 = 사용자 구조물 표 02 명세(PLAN 52-2) — 본체 · 유입 구조 · 유출 구조 ──
|
||||
const boxForm = createSpecForm(specLinks, { onChange: () => emit() });
|
||||
// 날개벽 칸 — 세월교 · BOX 가 같은 조각을 쓰던 자리 · 조정 행은 명세 유입 · 유출 칸으로 옮겨 붙음
|
||||
const wingInFields = createWingFields("날개벽(유입)", WING_IN_KEYS);
|
||||
const wingOutFields = createWingFields("날개벽(유출)", WING_OUT_KEYS);
|
||||
const wingInHome = wingInFields.slot.parentElement!;
|
||||
@@ -383,18 +369,6 @@ export function createFacilityOptionsForm(
|
||||
|
||||
const hiddenOptions = createHiddenOptions(root);
|
||||
|
||||
/** 고른 프리셋을 폭·높이 칸에 실어 둔다 — 저장도, "사용자 지정"으로 바꿨을 때의
|
||||
* 출발값도 이 두 칸이 정본이다. 사용자 지정 상태에서는 손대지 않는다. */
|
||||
function syncBoxSize(): void {
|
||||
const preset = BOX_SIZE_PRESETS.find(([label]) => label === boxSize.value);
|
||||
if (preset) {
|
||||
boxWidth.value = String(preset[1]);
|
||||
boxHeight.value = String(preset[2]);
|
||||
}
|
||||
// 「안 정함」도 폭·높이 칸을 접음 — 값이 없는 상태 그대로.
|
||||
boxCustomRow.hidden = boxSize.value !== BOX_SIZE_CUSTOM;
|
||||
}
|
||||
|
||||
// ── 물넘이 포장 = 사용자 구조물 표 03 명세(PLAN 52-3) — 경사각도 · 도수로/돌붙임 ·
|
||||
// 옛 월류 폭 · 높이 · 바닥 경사 · 두께 · 길이 칸은 뺌(지난 정의 값) ──
|
||||
const paveForm = createSpecForm(specLinks, { onChange: () => emit() });
|
||||
@@ -416,7 +390,7 @@ export function createFacilityOptionsForm(
|
||||
revetTopRow,
|
||||
revetInlet.root,
|
||||
revetOutlet.root,
|
||||
boxWrap,
|
||||
boxForm.root,
|
||||
wingInFields.root,
|
||||
wingOutFields.root,
|
||||
);
|
||||
@@ -430,14 +404,14 @@ export function createFacilityOptionsForm(
|
||||
const isBox = current === "box_culvert";
|
||||
const isBridge = current === "ford_bridge";
|
||||
pipeForm.root.hidden = !isPipe;
|
||||
boxForm.root.hidden = !isBox;
|
||||
fordForm.root.hidden = !isBridge;
|
||||
boxWrap.hidden = !isBox;
|
||||
// 날개벽 칸 = BOX암거 · 세월교는 유입 · 유출 구조 컨테이너(52-4) — B06 조정 행 자리만 그 컨테이너 끝으로
|
||||
const hasWing = isBox;
|
||||
wingInFields.root.hidden = !hasWing;
|
||||
wingOutFields.root.hidden = !hasWing;
|
||||
(isBridge ? fordForm.slot("inlet") : wingInHome).append(wingInFields.slot);
|
||||
(isBridge ? fordForm.slot("outlet") : wingOutHome).append(wingOutFields.slot);
|
||||
// 날개벽 칸 = 옛 자리 — BOX(52-2) · 세월교(52-4)는 명세 유입 · 유출 구조 컨테이너 · B06 조정 행 자리만 그 컨테이너 끝으로
|
||||
const spec = isBox ? boxForm : isBridge ? fordForm : null;
|
||||
wingInFields.root.hidden = true;
|
||||
wingOutFields.root.hidden = true;
|
||||
(spec ? spec.slot("inlet") : wingInHome).append(wingInFields.slot);
|
||||
(spec ? spec.slot("outlet") : wingOutHome).append(wingOutFields.slot);
|
||||
paveForm.root.hidden = current !== "ford_pavement";
|
||||
const isRevet = current === "revetment";
|
||||
revetTopRow.hidden = !isRevet;
|
||||
@@ -450,11 +424,6 @@ export function createFacilityOptionsForm(
|
||||
// 추가 기슭막이 칸은 **내용이 있을 때만** 선다 — 고른 다단 벽의 행이 여기 오면 뜨고,
|
||||
// 선택이 풀리면 스스로 사라진다.
|
||||
extraGroup.root.hidden = extraSlot.childElementCount === 0;
|
||||
if (isBox) syncBoxSize();
|
||||
if (hasWing) {
|
||||
wingInFields.syncVisibility();
|
||||
wingOutFields.syncVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
function emit(): void {
|
||||
@@ -465,7 +434,7 @@ export function createFacilityOptionsForm(
|
||||
revetInlet.fields.onChange(emit);
|
||||
revetOutlet.fields.onChange(emit);
|
||||
revetSide.addEventListener("change", emit);
|
||||
[boxSize, boxWidth, boxHeight, ...wingInFields.inputs, ...wingOutFields.inputs].forEach((input) =>
|
||||
[...wingInFields.inputs, ...wingOutFields.inputs].forEach((input) =>
|
||||
input.addEventListener("change", emit),
|
||||
);
|
||||
|
||||
@@ -497,11 +466,13 @@ export function createFacilityOptionsForm(
|
||||
bindLinks(links) {
|
||||
linkSource = links;
|
||||
pipeForm.paintLinks();
|
||||
boxForm.paintLinks();
|
||||
fordForm.paintLinks();
|
||||
paveForm.paintLinks();
|
||||
},
|
||||
paintLinks: () => {
|
||||
pipeForm.paintLinks();
|
||||
boxForm.paintLinks();
|
||||
fordForm.paintLinks();
|
||||
paveForm.paintLinks();
|
||||
},
|
||||
@@ -527,30 +498,17 @@ export function createFacilityOptionsForm(
|
||||
options[key] !== undefined ? String(options[key]) : "";
|
||||
// 받은 값만 칸에 — 없으면 「안 정함」(새 시설은 부르는 쪽이 등록부 기본값을 실어 줌 · 46-7).
|
||||
const groups = Object.fromEntries((definition?.groups ?? []).map((g) => [g.id, g.name]));
|
||||
if (facility === "pipe")
|
||||
pipeForm.write(PIPE_SPEC, options, definition?.spec_names ?? {}, groups);
|
||||
const names = definition?.spec_names ?? {};
|
||||
if (facility === "pipe") pipeForm.write(PIPE_SPEC, options, names, groups);
|
||||
if (facility === "box_culvert") {
|
||||
// 본체 규격은 저장 안 함 — 폭 · 높이로 되찾음 · 옛 설치 「있음」 = 날개벽
|
||||
boxForm.write(BOX_SPEC, boxOptionsIn(options), names, groups);
|
||||
for (const [id, value] of Object.entries(bodyOf(options)))
|
||||
boxForm.setValue("body", id, value);
|
||||
}
|
||||
if (facility === "ford_bridge")
|
||||
fordForm.write(FORD_SPEC, withLegacyGuards(options), definition?.spec_names ?? {}, groups);
|
||||
if (facility === "ford_pavement")
|
||||
paveForm.write(FORD_PAVEMENT_SPEC, options, definition?.spec_names ?? {}, groups);
|
||||
// 저장된 폭·높이가 프리셋과 맞으면 그 프리셋을, 아니면 "사용자 지정"을 고른다.
|
||||
// 값이 없으면 「안 정함」.
|
||||
const savedWidth = Number.parseFloat(text("body_width_m"));
|
||||
const savedHeight = Number.parseFloat(text("body_height_m"));
|
||||
const hasSaved =
|
||||
Number.isFinite(savedWidth) &&
|
||||
savedWidth > 0 &&
|
||||
Number.isFinite(savedHeight) &&
|
||||
savedHeight > 0;
|
||||
const preset = hasSaved
|
||||
? BOX_SIZE_PRESETS.find(
|
||||
([, width, height]) =>
|
||||
Math.abs(width - savedWidth) < 0.05 && Math.abs(height - savedHeight) < 0.05,
|
||||
)
|
||||
: undefined;
|
||||
boxSize.value = hasSaved ? (preset?.[0] ?? BOX_SIZE_CUSTOM) : "";
|
||||
boxWidth.value = hasSaved ? String(savedWidth) : "";
|
||||
boxHeight.value = hasSaved ? String(savedHeight) : "";
|
||||
fordForm.write(FORD_SPEC, withLegacyGuards(options), names, groups);
|
||||
if (facility === "ford_pavement") paveForm.write(FORD_PAVEMENT_SPEC, options, names, groups);
|
||||
wingInFields.write(options);
|
||||
wingOutFields.write(options);
|
||||
revetSide.value = text("side");
|
||||
@@ -570,11 +528,8 @@ export function createFacilityOptionsForm(
|
||||
if (current === "pipe") {
|
||||
Object.assign(options, pipeForm.read()); // 명세 컨테이너 값 = 옛 키 그대로(PLAN 52-1)
|
||||
} else if (current === "box_culvert") {
|
||||
// 프리셋을 골랐든 사용자 지정을 적었든 정본은 폭·높이 두 칸이다.
|
||||
putNumber(options, "body_width_m", boxWidth.value);
|
||||
putNumber(options, "body_height_m", boxHeight.value);
|
||||
wingInFields.read(options);
|
||||
wingOutFields.read(options);
|
||||
// 정본은 폭 · 높이 두 칸 — 규격이면 규격 수 · 사용자 입력이면 명세 숫자 칸(PLAN 52-2)
|
||||
Object.assign(options, boxForm.read(), bodyOptions(boxForm.state().body?.[0]));
|
||||
} else if (current === "ford_pavement") {
|
||||
Object.assign(options, paveForm.read()); // 표 03 명세 값 · 옛 월류 · 경사 · 두께 · 길이는 병합이 이어 붙임(52-3)
|
||||
} else if (current === "ford_bridge") {
|
||||
|
||||
@@ -461,18 +461,6 @@ export function createBasinSplitFields(length: HTMLInputElement): {
|
||||
};
|
||||
}
|
||||
|
||||
/* ── BOX암거 ────────────────────────────────────────────────────────────── */
|
||||
|
||||
/** 본체 규격 프리셋 — 실무 관측 규격이다(2026-08-17 사용자 확정). 그 밖의 규격은
|
||||
* "사용자 지정"으로 폭·높이를 직접 받는다. 저장 키는 프리셋이든 자유 입력이든
|
||||
* body_width_m·body_height_m 숫자 그대로다 — 프리셋 전용 키를 만들지 않아야
|
||||
* 하류(B06 횡단도·수량)가 폭·높이만 읽고 끝난다. */
|
||||
export const BOX_SIZE_PRESETS: ReadonlyArray<readonly [string, number, number]> = [
|
||||
["2.0×2.0", 2, 2],
|
||||
["3.0×3.0", 3, 3],
|
||||
];
|
||||
export const BOX_SIZE_CUSTOM = "사용자 지정";
|
||||
|
||||
/** 날개벽 옵션 키 묶음 — 유입·유출이 같은 조각을 쓰되 키만 다르다. */
|
||||
interface WingKeys {
|
||||
install: string;
|
||||
|
||||
@@ -17,12 +17,6 @@
|
||||
|
||||
const revetKeys = (side: "inlet" | "outlet"): string[] =>
|
||||
["form", "length_m", "height_m", "before_m", "after_m"].map((key) => `${side}_revet_${key}`);
|
||||
const wingKeys = (side: "in" | "out"): string[] => [
|
||||
`wing_${side}`,
|
||||
`wing_${side}_height_m`,
|
||||
`wing_${side}_length_m`,
|
||||
`wing_${side}_angle_deg`,
|
||||
];
|
||||
|
||||
/** 시설 종류별로 **폼이 적는 칸** — `createFacilityOptionsForm().readOptions()` 가 쓰는 키 전부. */
|
||||
export const FORM_OPTION_KEYS: Readonly<Record<string, readonly string[]>> = {
|
||||
@@ -40,7 +34,8 @@ export const FORM_OPTION_KEYS: Readonly<Record<string, readonly string[]>> = {
|
||||
"outlet_type",
|
||||
...revetKeys("outlet"),
|
||||
],
|
||||
box_culvert: ["body_width_m", "body_height_m", ...wingKeys("in"), ...wingKeys("out")],
|
||||
// BOX = 표 02 명세 키(PLAN 52-2) — 옛 날개벽 치수는 폼에서 빠져 이어 붙임(지난 정의 값)
|
||||
box_culvert: ["body_width_m", "body_height_m", "wing_in", "wing_out"],
|
||||
// 물넘이 포장 = 표 03 명세 키(PLAN 52-3 · `design_structure_ford_pavement`) — 옛 월류 · 경사 · 두께 · 길이는
|
||||
// 폼에서 빠져 이어 붙임 · 도수로/돌붙임 키는 `FORM_OPTION_PREFIXES`
|
||||
ford_pavement: ["ford_slope_deg"],
|
||||
|
||||
Reference in New Issue
Block a user