From 98ab68d4b04f4c04f5757fcdd54fa2aaa0b11f9a Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 16 Oct 2026 15:17:48 +0900 Subject: [PATCH] =?UTF-8?q?fix(B05=20=EB=85=B8=EC=84=A0=ED=8E=B8=EC=A7=91)?= =?UTF-8?q?:=20349-0h=20B06=20=EB=8B=A4=EB=85=80=EC=98=A8=20=EB=92=A4=20?= =?UTF-8?q?=ED=8F=89=EB=A9=B4=20=EC=9E=91=EA=B2=8C=20=C2=B7=20=EA=B5=AC?= =?UTF-8?q?=EC=84=9D=EC=9C=BC=EB=A1=9C=20=EA=B7=B8=EB=A0=A4=EC=A7=90=20?= =?UTF-8?q?=EA=B3=A0=EC=B9=A8=20=E2=80=94=20=EC=88=A8=EC=9D=80=20=EB=8F=99?= =?UTF-8?q?=EC=95=88(=EC=B9=B8=20=ED=81=AC=EA=B8=B0=200)=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=8B=A4=EC=8B=9C=20=EC=85=88=20=C2=B7=20=EB=B3=B4?= =?UTF-8?q?=EB=8D=98=20=ED=99=94=EB=A9=B4=20=EC=A0=81=EA=B8=B0=20=EA=B1=B4?= =?UTF-8?q?=EB=84=88=EB=9C=80=20=C2=B7=20=EB=8B=A4=EC=8B=9C=20=EB=B3=B4?= =?UTF-8?q?=EC=9D=B4=EB=A9=B4=20=EB=96=A0=EB=82=98=EA=B8=B0=20=EC=A0=84=20?= =?UTF-8?q?=EA=B0=80=EC=9A=B4=EB=8D=B0=20=C2=B7=20=EB=B0=B0=EC=9C=A8=20?= =?UTF-8?q?=EA=B7=B8=EB=8C=80=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 원인: 숨김(크기 0) 알림에 크기 0 으로 화면을 되살려 가운데가 도엽 구석 · 배율 1/8 로 바뀌고 그 값이 기억에도 적힘 - resize 크기 0 이면 그대로 돌아감 · 기억 꾼은 칸 너비 0 이면 안 잡음 · 처음 열기(기억 없음) = 등고 영역 맞춤 그대로 - 시험: test_349_0h_routeedit_hidden_keep 3 · test_b05_routeedit_view 숨김 1(옛 길 재현) · 관련 20 통과 · typecheck 0 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Se7ytqbohmBQnfYpwYFnYf --- B05_Profile/B05_Profile_UI_RouteEdit.ts | 3 +- .../tester/helper_b05_routeedit_view.cjs | 26 +++++++++ .../test_349_0h_routeedit_hidden_keep.py | 54 +++++++++++++++++++ resources/tester/test_b05_routeedit_view.py | 11 ++++ 4 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 resources/tester/test_349_0h_routeedit_hidden_keep.py diff --git a/B05_Profile/B05_Profile_UI_RouteEdit.ts b/B05_Profile/B05_Profile_UI_RouteEdit.ts index 842080a4e..eff95e9b5 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit.ts @@ -263,7 +263,7 @@ export async function mountRouteEditor( /** 보던 화면 기억(계획서 18 ③) — 자료를 읽고 화면을 되살린 **뒤부터** 적는다. */ let viewReady = false; const viewMemory = createViewMemory(projectId, () => - meta && viewReady ? captureView(meta, view, rotation.radians()) : null, + meta && viewReady && view.width > 0 ? captureView(meta, view, rotation.radians()) : null, ); /** 떠날 때 — 페이지가 버린다(345-3 · 닫기 단추 없음). */ @@ -298,6 +298,7 @@ export async function mountRouteEditor( // 노선이 화면 밖으로 나가고, 그 자리가 보던 화면으로 저장돼 다시 열어도 빈 화면이 된다. const kept = meta && viewReady ? captureView(meta, view, rotation.radians()) : null; const { width, height } = fitCanvas(canvas, context); + if (width <= 0 || height <= 0) return; // 숨은 동안(크기 0) — 화면 · 기억 그대로 · 보이면 다시 셈(349-0h). view = { ...view, width, height, mapRect: computeMapRect(meta, width, height) }; if (meta && kept) view = { ...view, ...restoreSavedView(meta, view, kept) }; if (!viewReady) openView(); // 숨은 채 섰으면(크기 0) 처음 보일 때 맞춘다. diff --git a/resources/tester/helper_b05_routeedit_view.cjs b/resources/tester/helper_b05_routeedit_view.cjs index 8bfa65999..f42d35c7f 100644 --- a/resources/tester/helper_b05_routeedit_view.cjs +++ b/resources/tester/helper_b05_routeedit_view.cjs @@ -122,6 +122,31 @@ const shrunk = viewOf(1280, 760, zoomed.scale, zoomed.offsetX, zoomed.offsetY); const refit = { ...shrunk, ...viewMod.restoreSavedView(meta, shrunk, keptBefore) }; const keptAfter = viewMod.captureView(meta, refit, 0); +// ③-4 숨김(크기 0) 뒤 다시 보임(349-0h) — 크기 0 으로 셈한 화면은 엉뚱함 · 그 셈을 건너뛰고 +// 떠나기 전 화면을 다시 보일 때 크기로 되살리면 그대로. +const shown = viewOf(1500, 1000, 4, -600, 250); +const keptShown = viewMod.captureView(meta, shown, 0); +const zero = viewOf(0, 0, shown.scale, shown.offsetX, shown.offsetY); +const zeroView = { ...zero, ...viewMod.restoreSavedView(meta, zero, keptShown) }; +const zeroBack = viewOf(1500, 1000, 1, 0, 0); +const fromZero = viewMod.captureView( + meta, + { + ...zeroBack, + ...viewMod.restoreSavedView(meta, zeroBack, viewMod.captureView(meta, zeroView, 0)), + }, + 0, +); +const back = viewOf(1500, 1000, shown.scale, shown.offsetX, shown.offsetY); +const keptBack = viewMod.captureView( + meta, + { + ...back, + ...viewMod.restoreSavedView(meta, back, keptShown), + }, + 0, +); + // ④ 노드 끼우기·빼기 — 편집값 여섯 줄이 같은 자리에서 함께 움직인다. const columns = { planned: [ @@ -152,6 +177,7 @@ process.stdout.write( stray: { rotationRad: stray.rotationRad, onScreen: strayOnScreen }, keptBefore, keptAfter, + hidden: { keptShown, fromZero, keptBack }, afterInsert, afterRemove, }), diff --git a/resources/tester/test_349_0h_routeedit_hidden_keep.py b/resources/tester/test_349_0h_routeedit_hidden_keep.py new file mode 100644 index 000000000..8f8a450ac --- /dev/null +++ b/resources/tester/test_349_0h_routeedit_hidden_keep.py @@ -0,0 +1,54 @@ +"""349-0h — B06 다녀와도 노선 편집 평면은 떠나기 전 화면 그대로. + +숨은 동안(크기 0) 칸 크기 알림에 화면을 다시 셈하지 않고 기억도 적지 않음 · +다시 보이면 떠나기 전 화면(가운데 · 배율)을 새 칸 크기로 되살림 · 처음 열기는 등고 영역 맞춤. +배선은 글로 봄. +./venv/Scripts/python.exe -m pytest resources/tester/test_349_0h_routeedit_hidden_keep.py -q +""" + +from __future__ import annotations + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +B05 = ROOT / "B05_Profile" +EDITOR = (B05 / "B05_Profile_UI_RouteEdit.ts").read_text(encoding="utf-8") +VIEW = (B05 / "B05_Profile_UI_RouteEdit_View.ts").read_text(encoding="utf-8") + + +def _body(text: str, head: str) -> str: + at = text.index(head) + return text[at : text.index("\n }", at)] + + +def test_숨은_동안_화면을_다시_셈하지_않음() -> None: + resize = _body(EDITOR, "function resize(): void {") + kept = resize.index("const kept = meta && viewReady ? captureView(") + fit = resize.index("fitCanvas(canvas, context);") + guard = resize.index("if (width <= 0 || height <= 0) return;") + assign = resize.index("view = { ...view, width, height,") + restore = resize.index("restoreSavedView(meta, view, kept)") + opening = resize.index("if (!viewReady) openView();") + # 떠나기 전 화면을 잡고 · 크기 0 이면 그대로 둠 · 보이면 새 크기로 되살림 · 처음이면 맞춤 + assert kept < fit < guard < assign < restore < opening + assert len(EDITOR.splitlines()) <= 700 + + +def test_숨은_동안_기억을_적지_않음() -> None: + assert ( + "meta && viewReady && view.width > 0 ? captureView(meta, view, rotation.radians())" + in EDITOR + ) + # 보임 · 숨김 알림 — 보이면 크기 다시 셈 · 숨으면 그 전 화면을 적음 + assert "host.onShow(() => (historyControls.listen(true), resize()));" in EDITOR + assert "host.onHide(() => (historyControls.listen(false), viewMemory.flush()));" in EDITOR + + +def test_처음_열기는_기억_없으면_등고_영역_맞춤() -> None: + opening = VIEW[ + VIEW.index("export function openingView(") : VIEW.index("export function fitView(") + ] + assert "const saved = readSavedView(projectId);" in opening + assert "return { view: fitView(meta, view, nodes, contours), rotationRad: 0 };" in opening + open_view = _body(EDITOR, "function openView(): void {") + assert "view.width <= 0) return;" in open_view # 크기 0 이면 맞춤도 미룸 diff --git a/resources/tester/test_b05_routeedit_view.py b/resources/tester/test_b05_routeedit_view.py index 7f3a2d5e6..b3ddbfc5b 100644 --- a/resources/tester/test_b05_routeedit_view.py +++ b/resources/tester/test_b05_routeedit_view.py @@ -73,3 +73,14 @@ def test_노드_끼우기_빼기는_편집값이_같은_자리로(results): assert removed["planned"] == [[0, 0], [10, 0], [20, 0]] assert removed["curveRadius"] == [None, 30, None] assert removed["apexLock"] == [False, True, False] + + +def test_숨김_크기_0_뒤_다시_보이면_그대로(results): + """349-0h — 크기 0 으로 셈하면 구석 · 작게(옛 흠) · 건너뛰고 되살리면 떠나기 전 그대로.""" + hidden = results["hidden"] + kept, from_zero, back = hidden["keptShown"], hidden["fromZero"], hidden["keptBack"] + assert from_zero["pxPerMeter"] < kept["pxPerMeter"] / 4 # 옛 길 — 아주 작게 + assert from_zero["cx"] != pytest.approx(kept["cx"], abs=1.0) # 옛 길 — 가운데가 구석으로 + assert back["cx"] == pytest.approx(kept["cx"], abs=1e-6) + assert back["cy"] == pytest.approx(kept["cy"], abs=1e-6) + assert back["pxPerMeter"] == pytest.approx(kept["pxPerMeter"], rel=1e-9)