diff --git a/A00_Common/design/design_structure_ford_calc.ts b/A00_Common/design/design_structure_ford_calc.ts index 474f37a92..0cfa6c52d 100644 --- a/A00_Common/design/design_structure_ford_calc.ts +++ b/A00_Common/design/design_structure_ford_calc.ts @@ -15,10 +15,8 @@ * (같은 풀이 · n · S 를 받음) · 거울 시험 test_73_11_b08_hydro_box_ford. * ========================================================================== */ -import { FORD_PAVEMENT_DEFAULT_WIDTH_M } from "@config/config_frontend"; import { fordSection } from "../../B05_Profile/B05_Profile_UI_Drainage_Facility_Fields"; import type { SpecForm } from "./design_structure_form"; -import { CHUTE_ITEMS } from "./design_structure_guard"; import type { SpecContainer, SpecItem } from "./design_structure_spec"; /** 월류 폭(m) → 필요 월류 높이(m) · 0.01 m 올림. 유량 · 폭이 없으면 null. */ @@ -81,39 +79,3 @@ export function fordHeightHint(designFlowM3s: number | null, widthM: number): st if (need !== null) return `필요 수심 ${need.toFixed(2)} 이상`; return designFlowM3s ? "월류 폭을 넣으면 셈" : "설계유량 없음 — 수기 입력"; } - -/** 도수로 · 돌붙임 면적(㎡ · 287-1) = 폭 × 길이 · 0.01 반올림. */ -export function chuteAreaM2(widthM: number, lengthM: number): number { - return Math.round(widthM * lengthM * 100) / 100; -} - -/** - * 물넘이 포장 · 세월교 도수로/돌붙임 길이 ↔ 면적(287-1 · 294-4) — 길이를 넣으면 면적 = 폭 × 길이(폭 = 띠가 - * 도로 방향으로 걸치는 월류 폭 · 비면 물넘이 기본 폭 · 배관 · 박스는 폭 정의 없어 자동 안 함) · 길이를 비우면 면적 = 칸 기본값 · 월류 폭을 바꾸면 길이가 있을 때만 - * 다시 셈. 길이는 그림(사면 따라 그 길이) · 면적은 수량(B08 돌붙임)이 씀. - */ -export function syncChuteArea( - form: Pick, - c: SpecContainer, - item: SpecItem, - /** 도수로 상자 · 월류 폭 상자 — 물넘이 `chute` · `slope` · 세월교 `chute_add` · `ford`(294-4). */ - chuteBox = "chute", - widthBox = "slope", -): void { - const byLength = c.id === chuteBox && item.id === "length_m"; - if (!byLength && !(c.id === widthBox && item.id === "width")) return; - const chute = form.state()[chuteBox]?.[0]; - if (!chute) return; - const length = Number(chute.length_m ?? ""); - if (!(chute.length_m ?? "").trim() || !(length > 0)) { - if (byLength) - form.setValue(chuteBox, "area", CHUTE_ITEMS.find((i) => i.id === "area")?.default ?? ""); - return; - } - const width = Number(form.state()[widthBox]?.[0]?.width ?? ""); - form.setValue( - chuteBox, - "area", - String(chuteAreaM2(width > 0 ? width : FORD_PAVEMENT_DEFAULT_WIDTH_M, length)), - ); -} diff --git a/B05_Profile/B05_Profile_UI_Drainage_Facility.ts b/B05_Profile/B05_Profile_UI_Drainage_Facility.ts index a9a65e47b..7a1b435ea 100644 --- a/B05_Profile/B05_Profile_UI_Drainage_Facility.ts +++ b/B05_Profile/B05_Profile_UI_Drainage_Facility.ts @@ -64,7 +64,7 @@ import { boxOptionsIn, } from "../A00_Common/design/design_structure_box"; import { FORD_SPEC, withLegacyGuards } from "../A00_Common/design/design_structure_ford"; -import { overflowSync, syncChuteArea } from "../A00_Common/design/design_structure_ford_calc"; +import { overflowSync } from "../A00_Common/design/design_structure_ford_calc"; import { pipeDrawn, pipeExtraCount, @@ -384,10 +384,7 @@ export function createFacilityOptionsForm( ); // 유출 흙막이 단 수 = 흙막이 상자 수(관보호공과 같은 방향 · 「+ 흙막이」 없음 · 90-1) const fordForm: SpecForm = createSpecForm(specLinks, { - onChange: (c, item) => ( - syncChuteArea(fordForm, c, item, "chute_add", "ford"), - fordFlowBox.onChange(c, item) - ), + onChange: fordFlowBox.onChange, fields: (c, values) => [...guardFields(c, values), ...fordFlowBox.fields(c)], count: guardSoilCount( () => chainage, @@ -477,7 +474,7 @@ export function createFacilityOptionsForm( emit, ); const paveForm: SpecForm = createSpecForm(specLinks, { - onChange: (c, item) => (syncChuteArea(paveForm, c, item), paveFlowBox.onChange(c, item)), + onChange: paveFlowBox.onChange, fields: paveFlowBox.fields, }); diff --git a/resources/tester/helper_287_ford_pavement.cjs b/resources/tester/helper_287_ford_pavement.cjs index 9b0b2fb48..be81c96c4 100644 --- a/resources/tester/helper_287_ford_pavement.cjs +++ b/resources/tester/helper_287_ford_pavement.cjs @@ -26,16 +26,6 @@ const load = (file, stubs = {}) => { const D = (f) => path.join(ROOT, "A00_Common", "design", f); const calc = load(D("design_structure_ford_calc.ts")); -/** 폼 흉내 — 상태 · setValue 만. */ -const formOf = (slope, chute) => { - const state = { slope: [{ ...slope }], chute: [{ ...chute }] }; - return { state: () => state, setValue: (c, i, v) => (state[c][0][i] = v) }; -}; -const change = (form, c, i) => { - calc.syncChuteArea(form, { id: c }, { id: i }); - return form.state().chute[0].area; -}; - // 287-2 · 287-4 그림 — DOM 흉내(속성 · 자식만) class Node { constructor(tag) { @@ -209,6 +199,32 @@ const overflowRun = (flowValue) => { }; }; +// 301-1 폼 훅 그대로(B05 물넘이 paveForm · 세월교 fordForm onChange = overflowSync onChange) +const areaRun = (widthBox, chuteBox) => { + const state = { [widthBox]: [{ width: "6" }], [chuteBox]: [{ length_m: "", area: "17" }] }; + const form = { + state: () => state, + setValue: (c, i, v) => (state[c][0][i] = v), + refresh: () => undefined, + }; + const sync = calc.overflowSync( + () => form, + widthBox, + () => 14.5776, + () => undefined, + ); + const edit = (box, item, value) => { + state[box][0][item] = value; + sync.onChange({ id: box }, { id: item }); + return state[chuteBox][0].area; + }; + return [ + edit(chuteBox, "length_m", "4"), + edit(widthBox, "width", "8"), + edit(chuteBox, "length_m", ""), + ]; +}; + const out = { overflowFlow: overflowRun(14.5776), overflowNoFlow: overflowRun(null), @@ -235,11 +251,8 @@ const out = { paveSlopeItems: load(D("design_structure_ford_pavement.ts")) .FORD_PAVEMENT_SPEC.containers.find((c) => c.id === "slope") .items.map((i) => i.id), - // 287-1 길이 넣음 = 월류 폭 × 길이 · 비움 = 기본값 · 폭 바꿈 = 길이 있을 때만 다시 셈 · 폭 비면 기본 폭 - areaByLength: change(formOf({ width: "6" }, { length_m: "4", area: "30" }), "chute", "length_m"), - areaCleared: change(formOf({ width: "6" }, { length_m: "", area: "24" }), "chute", "length_m"), - areaByWidth: change(formOf({ width: "8" }, { length_m: "2.5", area: "15" }), "slope", "width"), - areaWidthNoLength: change(formOf({ width: "8" }, { length_m: "", area: "17" }), "slope", "width"), - areaNoWidth: change(formOf({ width: "" }, { length_m: "3", area: "30" }), "chute", "length_m"), + // 301-1 면적 = 늘 손 입력 — 길이 · 월류 폭을 바꾸거나 길이를 비워도 면적 그대로(물넘이 · 세월교 폼 훅) + areaKeptPave: areaRun("slope", "chute"), + areaKeptFord: areaRun("ford", "chute_add"), }; process.stdout.write(JSON.stringify(out)); diff --git a/resources/tester/helper_294_3_ford_extend_chute.cjs b/resources/tester/helper_294_3_ford_extend_chute.cjs index 0a3dc1bf9..c7ea1bf0b 100644 --- a/resources/tester/helper_294_3_ford_extend_chute.cjs +++ b/resources/tester/helper_294_3_ford_extend_chute.cjs @@ -64,10 +64,6 @@ const pts = (lines) => lines.map((l) => l.points.map((p) => [+p.offset.toFixed(4), +p.elevation.toFixed(4)])); const dipGround = hatch.groundLine(input.dipSamples); const fillGround = hatch.groundLine(input.fillSamples); -const calc = load(path.join(ROOT, "A00_Common", "design", "design_structure_ford_calc.ts")); -const formOf = (state) => ({ state: () => state, setValue: (c, i, v) => (state[c][0][i] = v) }); -const fordState = { ford: [{ width: "8" }], chute_add: [{ length_m: "2.5", area: "30" }] }; -calc.syncChuteArea(formOf(fordState), { id: "chute_add" }, { id: "length_m" }, "chute_add", "ford"); const guard = load(path.join(ROOT, "A00_Common", "design", "design_structure_guard.ts")); const fordPave = load(path.join(ROOT, "A00_Common", "design", "design_structure_ford_pavement.ts")); @@ -85,7 +81,6 @@ const out = { fillLine: pave.outletFillLine(input.fillDesign, fillGround), fillLineOld: pave.outletFillLine(input.fillDesign), fillChuteLength: pave.fordChuteLine(input.fillDesign, { length_m: 1 }, fillGround), - fordArea: fordState.chute_add[0].area, chuteItems: guard.CHUTE_ITEMS.map((i) => i.id), fordPaveChuteSame: fordPave.FORD_PAVEMENT_SPEC.containers.find((c) => c.id === "chute").items === diff --git a/resources/tester/test_287_ford_pavement_followup.py b/resources/tester/test_287_ford_pavement_followup.py index 80a7a70c8..01700dfde 100644 --- a/resources/tester/test_287_ford_pavement_followup.py +++ b/resources/tester/test_287_ford_pavement_followup.py @@ -1,8 +1,8 @@ """287 물넘이포장 후속(요구 183-2 · 4 · 6 · 7 · 8 · 9). 못박는 것 - - 287-1 도수로/돌붙임 길이 ↔ 면적 — 길이 넣음 = 월류 폭 × 길이 · 비움 = 칸 기본값 · 월류 폭을 바꾸면 - 길이가 있을 때만 다시 셈 · 폭이 비면 물넘이 기본 폭. + - 301-1 도수로/돌붙임 면적 = 늘 손 입력(요구 192-2 · 287-1 자동 셈 걷음) — 길이 · 월류 폭을 바꾸거나 + 길이를 비워도 면적 그대로 · 길이 = 그림 전용(294-4). - 287-3 월류 폭 → 높이 자동 = 세월교 한 벌(`fordHeightM` · 폼 `overflow(…, "slope")`) · 유량 없으면 손 입력. - 287-5 물넘이포장 형상 누름 = 다른 시설과 같은 고름 창구(`toggleRevet("pipe")` → 폼 · 알약 · 측점선) · 판정면은 보라 손잡이 아래(요구 185 ①) · 끌기(3 px 넘게 움직임) = 고름 아님(②) · 노면공 포장층 @@ -51,17 +51,19 @@ def ts() -> dict: return json.loads(proc.stdout) -def test_도수로_길이_면적(ts): - assert ts["areaByLength"] == "24" # 6 m × 4 m - assert ts["areaCleared"] == "30" # 길이 비움 = 기본값 - assert ts["areaByWidth"] == "20" # 폭 8 m × 2.5 m - assert ts["areaWidthNoLength"] == "17" # 길이 없으면 폭을 바꿔도 그대로 - assert ts["areaNoWidth"] == "30" # 폭 비움 = 기본 폭 10 m × 3 m +def test_도수로_면적은_손_입력_그대로(ts): + # 길이 4 넣음 · 월류 폭 8 · 길이 비움 — 면적은 손 입력 17 그대로 + assert ts["areaKeptPave"] == ["17", "17", "17"] + assert ts["areaKeptFord"] == ["17", "17", "17"] -def test_물넘이_폼이_길이_면적을_잇는다(): +def test_폼이_길이_면적을_잇지_않는다(): form = _read("B05_Profile", "B05_Profile_UI_Drainage_Facility.ts") - assert "syncChuteArea(paveForm, c, item), paveFlowBox.onChange(c, item)" in form + assert "syncChuteArea" not in form + assert "onChange: fordFlowBox.onChange," in form + assert "onChange: paveFlowBox.onChange," in form + calc = _read("A00_Common", "design", "design_structure_ford_calc.ts") + assert "syncChuteArea" not in calc and "chuteAreaM2" not in calc def _pts(text: str) -> list[float]: diff --git a/resources/tester/test_294_3_ford_extend_chute.py b/resources/tester/test_294_3_ford_extend_chute.py index e20bb8560..4d0d4a9a7 100644 --- a/resources/tester/test_294_3_ford_extend_chute.py +++ b/resources/tester/test_294_3_ford_extend_chute.py @@ -7,8 +7,8 @@ 연장」) · 그림 = 콘크리트(포장층)가 늘인 끝까지. - 294-4 도수로/돌붙임 띠 = 낮은 쪽 끝단(늘인 끝 · 아니면 유출 쪽 노견 끝)에서 성토 사면 따라 · 길이 비움 = 원지반 닿으면 끝 · 양측 절토 물넘이 = 띠 없음 · 길이 = 그 길이(사면보다 길면 원지반 따라) · 다른 구조물 - (배관 · 박스 · 세월교) 같은 규칙(`chuteRun` · 원지반 `layout.ground`) + 같은 「길이 (m)」 칸 · 면적 = 세월교만 - 월류 폭 × 길이(배관 · 박스 = 폭 정의 없어 자동 안 함) · 길이는 집계 · B08 수량에 안 실림. + (배관 · 박스 · 세월교) 같은 규칙(`chuteRun` · 원지반 `layout.ground`) + 같은 「길이 (m)」 칸 · 면적 = 늘 + 손 입력(301-1 · 길이 · 폭으로 안 셈) · 길이는 집계 · B08 수량에 안 실림. 진짜 TS 는 `helper_294_3_ford_extend_chute.cjs` 가 Node 로 돌림. 화면은 사용자 확인. """ @@ -155,7 +155,6 @@ def test_성토_쪽_띠는_원지반_닿으면_끝(ts): def test_다른_구조물_같은_규칙과_길이_칸(ts): assert ts["chuteItems"][-1] == "length_m" assert ts["fordPaveChuteSame"] is True - assert ts["fordArea"] == "20" # 세월교 월류 폭 8 × 길이 2.5 hatch = (ROOT / "B06_Section" / "B06_Section_UI_Cross_Wall_Hatch.ts").read_text( encoding="utf-8" ) @@ -168,7 +167,7 @@ def test_다른_구조물_같은_규칙과_길이_칸(ts): form = (ROOT / "B05_Profile" / "B05_Profile_UI_Drainage_Facility.ts").read_text( encoding="utf-8" ) - assert form.count("syncChuteArea(") == 2 # 물넘이 · 세월교만(배관 · 박스 = 폭 정의 없음) + assert "syncChuteArea" not in form # 면적 = 손 입력(301-1) def test_길이는_집계_수량에_안_실림():