From 2d4c65dc7828768661edf79b3d866a5036dd8265 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sat, 10 Oct 2026 11:47:34 +0900 Subject: [PATCH] =?UTF-8?q?fix(b06):=20=ED=9A=A1=EB=8B=A8=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EA=B3=A0=EB=A6=84=20=EB=92=A4=20=EC=A2=85=EB=8B=A8?= =?UTF-8?q?=20=EC=B8=A1=EC=A0=90=20=EC=84=B8=EB=A1=9C=EC=84=A0=20=EC=9C=A0?= =?UTF-8?q?=EC=A7=80=20=C2=B7=20=EC=A2=85=EB=8B=A8=20=EB=88=84=EB=A6=84=20?= =?UTF-8?q?=EB=B0=98=EB=8C=80=20=EA=B8=B8=EB=8F=84=20(177-3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - setSelectedStructure keepStation — B06 카드가 선 주인 · 선 없는 구조물 · 풂도 카드 선 지킴 - 새 시험 test_177_3_station_line_keep Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015sDZSjFvoiDjdGpgj3muc3 --- A00_Common/design/design_profile.ts | 8 +-- .../B06_Section_UI_Section_View_Profile.ts | 4 +- .../tester/helper_177_3_station_line_keep.cjs | 64 +++++++++++++++++ .../tester/test_177_3_station_line_keep.py | 72 +++++++++++++++++++ 4 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 resources/tester/helper_177_3_station_line_keep.cjs create mode 100644 resources/tester/test_177_3_station_line_keep.py diff --git a/A00_Common/design/design_profile.ts b/A00_Common/design/design_profile.ts index fbc325620..cc0b3335e 100644 --- a/A00_Common/design/design_profile.ts +++ b/A00_Common/design/design_profile.ts @@ -622,12 +622,12 @@ export function createDesignProfile(ctx: DesignProfileContext) { syncDrainageSpans(); draw(); }, - /** 사이드 목록에서 고른 구조물을 그래프 알약·측점 세로선 선택에 함께 맞춘다. */ - setSelectedStructure(structureId: string | null) { - // 지금 측점(B06 카드)이 그 구조물 자리면 측점선도 그대로(53-2) + /** 고른 구조물에 알약 · 측점선을 맞춤 — 그 자리면 선 그대로(53-2) · keepStation(B06 카드 = 선 주인) = 선 없는 구조물 · 풂도 카드 선 지킴(177-3). */ + setSelectedStructure(structureId: string | null, keepStation = false) { selectedStructureId = structureId; + const own = stationIdAtStructure(structureId) ?? (keepStation ? selectedStationId : null); const keep = structureId !== null && structureIdAtStation(selectedStationId) === structureId; - selectedStationId = keep ? selectedStationId : stationIdAtStructure(structureId); + selectedStationId = keep ? selectedStationId : own; draw(); }, /** 이어 공사 시작 기준(측점번호·누가거리 오프셋)을 반영해 측점 라벨·누가거리 표시를 옮긴다. */ diff --git a/B06_Section/B06_Section_UI_Section_View_Profile.ts b/B06_Section/B06_Section_UI_Section_View_Profile.ts index 9ea0b7731..0df0e0f12 100644 --- a/B06_Section/B06_Section_UI_Section_View_Profile.ts +++ b/B06_Section/B06_Section_UI_Section_View_Profile.ts @@ -265,7 +265,7 @@ export function createSectionProfile( }, selectMark(structureId) { markedId = structureId; - profile?.setSelectedStructure(structureId); + profile?.setSelectedStructure(structureId, true); // 카드가 측점선 주인 — 선 없는 구조물 · 풂은 카드 선 지킴(177-3) }, showCard(section) { if (!profile) return; @@ -280,7 +280,7 @@ export function createSectionProfile( // 일반 측점은 측점선 짝 알약이 없어 꺼짐 — 고른 구조물이 이 측점을 덮으면 그 알약을 지킴(53-1) const marked = markedId ? marks.find((entry) => entry.structure_id === markedId) : undefined; if (!pipe && marked && coversChainage(marked, section.chainage_m)) - profile.setSelectedStructure(markedId); + profile.setSelectedStructure(markedId, true); }, }; } diff --git a/resources/tester/helper_177_3_station_line_keep.cjs b/resources/tester/helper_177_3_station_line_keep.cjs new file mode 100644 index 000000000..70673c23d --- /dev/null +++ b/resources/tester/helper_177_3_station_line_keep.cjs @@ -0,0 +1,64 @@ +/* 177-3 횡단 카드 ↔ 종단 측점선 헬퍼 — 진짜 design_profile.ts 의 `setSelectedStation` · `setSelectedStructure` + * 본문을 떼어(TS 는 그 자리 트랜스파일) 측점 · 구조물 짝 흉내에 붙이고, B06 카드 고름 차례 + * (showCard → setSelectedStation · 리스너 → selectMark → setSelectedStructure(mark, true))와 B05 기본 길을 돌려 + * 남은 측점선 id 를 표준출력 JSON 한 벌로. + */ +const fs = require("fs"); +const path = require("path"); +const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); + +const ROOT = path.join(__dirname, "..", ".."); +const src = fs.readFileSync(path.join(ROOT, "A00_Common", "design", "design_profile.ts"), "utf8"); +const method = (name) => { + const pattern = String.raw`\r?\n ${name}\([^\r\n]*\) \{\r?\n[\s\S]*?\r?\n \},`; + const match = src.match(new RegExp(pattern)); + if (!match) throw new Error(`본문 없음: ${name}`); + return match[0]; +}; +const body = ` +let selectedStationId = null; +let selectedStructureId = null; +// 관(pipe-80.17)만 측점선(irr-80)이 있음 — 벽 · 노면공은 선 없음(PST stationIdAtStructure) +const structureIdAtStation = (stationId) => (stationId === "irr-80" ? "pipe-80.17" : null); +const stationIdAtStructure = (structureId) => (structureId === "pipe-80.17" ? "irr-80" : null); +const draw = () => {}; +module.exports = { + line: () => selectedStationId, + ${method("setSelectedStation")} + ${method("setSelectedStructure")} +};`; +const js = ts.transpileModule(body, { + compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, +}).outputText; +const box = { exports: {} }; +new Function("exports", "module", js)(box.exports, box); +const dp = box.exports; + +/** B06 카드 고름 — showCard(카드 · 관 측점이면 관 선) 뒤 리스너가 selectMark(mark) = keepStation true. */ +const b06Card = (lineId, mark) => { + dp.setSelectedStation(lineId); + dp.setSelectedStructure(mark, true); + return dp.line(); +}; +/** B05 목록 고름 — 기본값(keepStation 없음). */ +const b05Pick = (lineId, structureId) => { + dp.setSelectedStation(lineId); + dp.setSelectedStructure(structureId); + return dp.line(); +}; + +const out = { + plainCard: b06Card("s40", null), // 일반 카드 — 덮는 것 없음 + wallCard: b06Card("s100", "wall"), // 벽(선 없는 구조물) 카드 + paveCard: b06Card("s140", "pave"), // 노면공 범위 안 카드 + pipeCard: b06Card("irr-80", "pipe-80.17"), // 관 카드 = 관 선 + pipeFromPlain: b06Card("s60", "pipe-80.17"), // 관 알약 = 관 선으로 옮김 + // 반대 길 — 종단 측점선 누름 → 카드 고름 → 같은 리스너(selectMark null) + profileClick: b06Card("s200", null), + // 카드 풂 = showCard(null) → 선 끔 + release: (dp.setSelectedStation(null), dp.line()), + b05Wall: b05Pick("s100", "wall"), // B05 = 지금 그대로(선 없는 구조물 → 선 끔) + b05Pipe: b05Pick("s40", "pipe-80.17"), + b05Keep: b05Pick("irr-80", "pipe-80.17"), +}; +process.stdout.write(JSON.stringify(out)); diff --git a/resources/tester/test_177_3_station_line_keep.py b/resources/tester/test_177_3_station_line_keep.py new file mode 100644 index 000000000..26b2e1f63 --- /dev/null +++ b/resources/tester/test_177_3_station_line_keep.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +"""177-3 B06 횡단 카드 고름 → 종단 측점 세로선 유지(반대 방향도). + +못박는 것 + - 카드 고름 뒤 리스너의 selectMark(mark) 가 측점선을 끄지 않음 — 일반 · 벽 · 노면공 카드 = 그 카드 선. + - 관 카드 · 관 알약 = 관 측점선 · 카드 풂 = 선 끔. + - 종단 측점선 누름(반대 길)도 같은 리스너 — 누른 선 그대로. + - B05(keepStation 기본값) = 지금 그대로(선 없는 구조물 → 선 끔). + +Node 헬퍼 `helper_177_3_station_line_keep.cjs` 가 진짜 design_profile.ts 본문을 돌림. 화면은 사용자 확인. +""" + +import json +import os +import subprocess + +import pytest + +HERE = os.path.dirname(os.path.abspath(__file__)) +ROOT = os.path.abspath(os.path.join(HERE, "..", "..")) + + +def _read(*parts: str) -> str: + with open(os.path.join(ROOT, *parts), encoding="utf-8") as handle: + return handle.read() + + +@pytest.fixture(scope="module") +def out(): + proc = subprocess.run( + ["node", os.path.join(HERE, "helper_177_3_station_line_keep.cjs")], + capture_output=True, + text=True, + encoding="utf-8", + timeout=60, + ) + assert proc.returncode == 0, proc.stderr + return json.loads(proc.stdout) + + +def test_카드_고름은_그_카드_측점선_유지(out): + assert out["plainCard"] == "s40" + assert out["wallCard"] == "s100" + assert out["paveCard"] == "s140" + + +def test_관은_관_측점선(out): + assert out["pipeCard"] == "irr-80" + assert out["pipeFromPlain"] == "irr-80" + + +def test_반대_길_종단_측점선_누름도_유지(out): + assert out["profileClick"] == "s200" + assert out["release"] is None + + +def test_B05_기본_길은_그대로(out): + assert out["b05Wall"] is None + assert out["b05Pipe"] == "irr-80" + assert out["b05Keep"] == "irr-80" + + +def test_배선(): + svp = _read("B06_Section", "B06_Section_UI_Section_View_Profile.ts") + assert "profile?.setSelectedStructure(structureId, true);" in svp + assert "profile.setSelectedStructure(markedId, true);" in svp + cross = _read("A00_Common", "design", "design_view_cross.ts") + assert "sectionView.selectMark(mark, false);" in cross + b05 = _read("A00_Common", "design", "design_view_profile.ts") + assert ", true)" not in "".join( + line for line in b05.splitlines() if "setSelectedStructure(" in line + )