diff --git a/B05_Profile/B05_Profile_UI_RouteEdit.ts b/B05_Profile/B05_Profile_UI_RouteEdit.ts index dd62fe6f3..3ea47e00d 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit.ts @@ -48,6 +48,7 @@ import { createViewMemory, fitCanvas, openingView, + restoreSavedView, screenToMetric, viewPxPerMeter, } from "./B05_Profile_UI_RouteEdit_View"; @@ -232,8 +233,12 @@ export async function openRouteEditModal( function resize(): void { if (closed) return; + // 크기가 바뀌어도 가운데 자리 · 배율을 지킨다 — px 이동량을 그대로 두면 지도가 밀려 + // 노선이 화면 밖으로 나가고, 그 자리가 보던 화면으로 저장돼 다시 열어도 빈 화면이 된다. + const kept = meta && viewReady ? captureView(meta, view, rotation.radians()) : null; const { width, height } = fitCanvas(canvas, context); view = { ...view, width, height, mapRect: computeMapRect(meta, width, height) }; + if (meta && kept) view = { ...view, ...restoreSavedView(meta, view, kept) }; draw(); } window.addEventListener("resize", resize); diff --git a/B05_Profile/B05_Profile_UI_RouteEdit_View.ts b/B05_Profile/B05_Profile_UI_RouteEdit_View.ts index 606e2286d..c32688f06 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit_View.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit_View.ts @@ -108,7 +108,15 @@ export function openingView( const saved = readSavedView(projectId); if (saved) { const rotationRad = (saved.rotationDeg * Math.PI) / 180; - return { view: { ...view, ...restoreSavedView(meta, view, saved) }, rotationRad }; + const restored: ViewState = { ...view, ...restoreSavedView(meta, view, saved) }; + // 노선이 한 점도 안 보이는 기억(밀려 저장된 화면)은 버린다 — 빈 화면으로 열리지 않게. + // 가운데에서 짧은 변 절반 안 — 회전축이 가운데라 돌려도 같은 판정이다. + const reach = Math.min(view.width, view.height) / 2; + const seen = nodes.some(([x, y]) => { + const [px, py] = metricToScreen(meta, restored, x, y); + return Math.hypot(px - view.width / 2, py - view.height / 2) < reach; + }); + if (seen) return { view: restored, rotationRad }; } const xs = nodes.map((vertex) => vertex[0]); const ys = nodes.map((vertex) => vertex[1]); diff --git a/resources/tester/helper_b05_routeedit_view.cjs b/resources/tester/helper_b05_routeedit_view.cjs index 02dc8ab6d..65087b7a1 100644 --- a/resources/tester/helper_b05_routeedit_view.cjs +++ b/resources/tester/helper_b05_routeedit_view.cjs @@ -94,7 +94,33 @@ const fresh = viewMod.openingView("p-none", meta, viewOf(1000, 700, 1, 0, 0), [ [200300, 500300], [200600, 500500], ]); -const withSaved = viewMod.openingView("p-1", meta, viewOf(1000, 700, 1, 0, 0), [[200300, 500300]]); +const withSaved = viewMod.openingView("p-1", meta, viewOf(1000, 700, 1, 0, 0), [ + [saved.cx, saved.cy], +]); +// ③-2 기억한 화면에 노선이 한 점도 안 보이면(밀려 저장된 화면) 버리고 노선에 맞춘다. +const strayMemory = viewMod.createViewMemory("p-stray", () => ({ + cx: 201150, + cy: 500850, + pxPerMeter: 8, + rotationDeg: 45, +})); +strayMemory.flush(); +const strayNodes = [ + [200300, 500300], + [200400, 500350], +]; +const stray = viewMod.openingView("p-stray", meta, viewOf(1000, 700, 1, 0, 0), strayNodes); +const strayOnScreen = strayNodes.some(([x, y]) => { + const [px, py] = render.metricToScreen(meta, stray.view, x, y); + return px >= 0 && px <= 1000 && py >= 0 && py <= 700; +}); + +// ③-3 창 크기가 바뀌어도(편집 중 resize) 가운데 · 배율이 그대로 — 편집 창 `resize()` 와 같은 셈. +const zoomed = viewOf(1500, 1000, 6, -900, 400); +const keptBefore = viewMod.captureView(meta, zoomed, 0); +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); // ④ 노드 끼우기·빼기 — 편집값 여섯 줄이 같은 자리에서 함께 움직인다. const columns = { @@ -123,6 +149,9 @@ process.stdout.write( storedKeys, fresh: { rotationRad: fresh.rotationRad, scale: fresh.view.scale }, withSaved: { rotationRad: withSaved.rotationRad }, + stray: { rotationRad: stray.rotationRad, onScreen: strayOnScreen }, + keptBefore, + keptAfter, afterInsert, afterRemove, }), diff --git a/resources/tester/test_b05_routeedit_view.py b/resources/tester/test_b05_routeedit_view.py index 4bfdae690..7f3a2d5e6 100644 --- a/resources/tester/test_b05_routeedit_view.py +++ b/resources/tester/test_b05_routeedit_view.py @@ -49,6 +49,18 @@ def test_보던_화면이_없으면_노선에_맞추고_회전_0(results): assert results["withSaved"]["rotationRad"] == pytest.approx(math.radians(30)) +def test_노선이_안_보이는_기억은_버리고_노선에_맞춤(results): + assert results["stray"]["rotationRad"] == 0 + assert results["stray"]["onScreen"] is True + + +def test_창_크기가_바뀌어도_가운데와_배율_그대로(results): + before, after = results["keptBefore"], results["keptAfter"] + assert after["cx"] == pytest.approx(before["cx"], abs=1e-6) + assert after["cy"] == pytest.approx(before["cy"], abs=1e-6) + assert after["pxPerMeter"] == pytest.approx(before["pxPerMeter"], rel=1e-9) + + def test_노드_끼우기_빼기는_편집값이_같은_자리로(results): inserted = results["afterInsert"] assert inserted["planned"][1] == [5, 5]