/* ============================================================================= * design_structure_ford_calc.ts * 세월교 명세 계산 — 월류 폭 ↔ 월류 높이 연동(PLAN 52-4). * * 높이 = 담당 유역 설계유량을 그 월류 폭으로 넘기는 필요 수심(Manning 개수로 · `fordSection` 그대로). * 근거: Manning 공식 `resources/knowledge/technical_info/01_임도/03_계산정보/통수단면.md:23~33` * (KDS 44 40 00 2.7.1~2.7.2) · 조도계수 0.017 = 같은 파일 :59(콘크리트 수로 보통 · 울진 물넘이) · * 경사 10 % = 실무 수리계산서 역산값(`config_frontend.FORD_SLOPE` · 지식DB 수치 없음). * 폭을 바꾸면 높이 칸을 이 값으로 채움 · 사용자는 올려 적을 수 있고 이 값 아래로는 안 받음. * 높이는 저장되는 입력값 — 서버는 받은 높이로 노면을 내림(`B06_Section_Engine_Culvert._ford_set`) · 재계산 안 함. * 설계유량이 없으면 null = 손 입력. * 짝: 자동 배치 새 세월교 빈 높이 = 서버 `B04_PreProcess_Router_Basins.ford_height_m`(같은 풀이 · 65-4) · * 거울 시험 test_65_ford_defaults. * 짝: B08 수리집수면적표 세월교 · 물넘이 월류 높이 = 서버 `B08_Quantity_Engine_Hydro.ford_depth` * (같은 풀이 · n · S 를 받음) · 거울 시험 test_73_11_b08_hydro_box_ford. * ========================================================================== */ import { fordSection } from "../../B05_Profile/B05_Profile_UI_Drainage_Facility_Fields"; import type { SpecForm } from "./design_structure_form"; import type { SpecContainer, SpecItem } from "./design_structure_spec"; /** 월류 폭(m) → 필요 월류 높이(m) · 0.01 m 올림. 유량 · 폭이 없으면 null. */ export function fordHeightM(designFlowM3s: number | null, widthM: number): number | null { const section = fordSection(designFlowM3s, widthM); return section ? Math.ceil(section.depthM * 100 - 1e-9) / 100 : null; } /** * 월류 폭 ↔ 높이 연동 — 세월교 · 물넘이 포장 한 벌(52-4 · 65-4 · 65-5 · 287-3). * · 폭을 바꾸면 높이 = 필요 수심 · 높이를 비우거나 그 아래로 넣으면 필요 수심으로. * · 새 시설(`arm(true)`) 빈 높이 = 유량이 오면 필요 수심(사용자가 넣은 것처럼 — [추가] · [수정]이 읽음). * · 상자 끝 안내 — 「필요 수심 n 이상」 · 「설계유량 없음 — 수기 입력」(유량이 없으면 손 입력). * `box` = 폭 · 높이 칸이 든 상자 id(세월교 `ford` · 물넘이 `slope`) · `flow` = 담당 유역 설계유량. */ export function overflowSync( form: () => Pick, box: string, flow: () => number | null, emit: () => void, ) { let pending = false; const values = () => form().state()[box]?.[0] ?? {}; const width = () => Number(values().width ?? ""); const need = (): number | null => fordHeightM(flow(), width()); return { arm: (fresh: boolean): void => { pending = fresh; }, onChange: (c: SpecContainer, item: SpecItem): void => { if (c.id === box && item.id === "height") pending = false; const want = c.id === box ? need() : null; const height = values().height ?? ""; const low = height === "" || !(Number(height) >= (want ?? 0)); if (want !== null && (item.id === "width" || (item.id === "height" && low))) form().setValue(box, "height", want.toFixed(2)); emit(); }, /** 유량이 왔거나 시설을 올림 — 새 시설 빈 높이를 채우고 안내를 다시 그림. */ fill: (): void => { const want = pending && !values().height ? need() : null; if (want === null) return form().refresh(); form().setValue(box, "height", want.toFixed(2)); pending = false; }, /** 상자 끝 안내 글(폼 `fields` 훅). */ fields: (c: SpecContainer): HTMLElement[] => { if (c.id !== box) return []; const hint = document.createElement("p"); hint.className = "b05-structure__flow-hint"; hint.textContent = fordHeightHint(flow(), width()); return [hint]; }, }; } /** 높이 빈 칸 안내. */ export function fordHeightHint(designFlowM3s: number | null, widthM: number): string { const need = fordHeightM(designFlowM3s, widthM); if (need !== null) return `필요 수심 ${need.toFixed(2)} 이상`; return designFlowM3s ? "월류 폭을 넣으면 셈" : "설계유량 없음 — 수기 입력"; }