diff --git a/A00_Common/design/design_profile.ts b/A00_Common/design/design_profile.ts index 0c9e13429..5f9804938 100644 --- a/A00_Common/design/design_profile.ts +++ b/A00_Common/design/design_profile.ts @@ -267,7 +267,8 @@ export function createDesignProfile(ctx: DesignProfileContext) { restore: () => void (store.resync() ? rebuild() : draw()), restoreSaved: () => store.restoreSaved(), canRestoreSaved: () => store.canRestoreSaved(), - refresh: () => renderBalance(), + // 고름 직후 종단도도 한 번 — 붉은 강조가 다음 그림을 기다리지 않게(423). + refresh: () => (renderBalance(), drawSoon()), }); /** 편집을 적용한다. 법정 위반 정책이 block이면 새 위반이 생기는 편집을 막는다. false = 막힘. */ diff --git a/B05_Profile/B05_Profile_UI_Profile_Panel_Tools.ts b/B05_Profile/B05_Profile_UI_Profile_Panel_Tools.ts index 428d5e7b8..10473b078 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Panel_Tools.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Panel_Tools.ts @@ -131,7 +131,10 @@ export function createPanelTools(ctx: PanelToolsContext): PanelTools { }); const mountViews = (mode: string): void => { // [쉬프트] 붉은 강조는 그래프를 다시 그릴 때만 지워짐 — 다른 도구로 넘어가면 바로 뗌(271-1) - if (mode !== "shift") lastGeom?.chart.querySelector(".b05-profile-runmark")?.remove(); + if (mode !== "shift") + lastGeom?.chart + .querySelectorAll(".b05-profile-runmark, .b05-profile-runmark__line") + .forEach((node) => node.remove()); freeView.mount(lastGeom, mode === "free"); bandView.mount(lastGeom, mode === "straighten"); }; diff --git a/B05_Profile/B05_Profile_UI_Profile_Render.ts b/B05_Profile/B05_Profile_UI_Profile_Render.ts index b1a92e3d2..1d087f7b3 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Render.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Render.ts @@ -322,8 +322,9 @@ export function renderProfile(ctx: ProfileRenderContext): void { if (ctx.toolActive()) { const marker = (event.target as HTMLElement).closest(".b06-chart__station"); const raw = marker?.getAttribute("data-chainage"); + let picked: boolean; if (raw !== null && raw !== undefined) { - if (ctx.handleToolPick(Number(raw))) return; + picked = ctx.handleToolPick(Number(raw)); } else { const rect = chartWrap.getBoundingClientRect(); const chainage = chainageInverter( @@ -331,8 +332,10 @@ export function renderProfile(ctx: ProfileRenderContext): void { width, layout.originOffset, )(event.clientX - rect.left + chartWrap.scrollLeft); - if (ctx.handleToolPick(Number.isFinite(chainage) ? chainage : null)) return; + picked = ctx.handleToolPick(Number.isFinite(chainage) ? chainage : null); } + // 도구가 먹은 누름은 측점선 자기 클릭(측점 고름)으로 새지 않게 막음(425). + if (picked) event.stopPropagation(); return; } // 원지반선 근처 · 추가 측점 선 누름 = 그 자리 임시 횡단 작은 창(368-3) @@ -478,6 +481,8 @@ export function renderProfile(ctx: ProfileRenderContext): void { axis: yAxis, widthPx: width, heightPx: chartHeight, + // 붉은 선은 계획선 겹 안에 — 스크롤 때 세로 맞춤 변환을 계획선과 같이 받음(424). + windowLayer: chartWrap.querySelector(".b06-chart__profile")?.parentElement, }); if (highlight) chartWrap.append(highlight); } diff --git a/B05_Profile/B05_Profile_UI_Profile_RunHighlight.ts b/B05_Profile/B05_Profile_UI_Profile_RunHighlight.ts index 65e544fc8..af5ae8574 100644 --- a/B05_Profile/B05_Profile_UI_Profile_RunHighlight.ts +++ b/B05_Profile/B05_Profile_UI_Profile_RunHighlight.ts @@ -29,6 +29,11 @@ export interface RunHighlightOptions { axis: { ticks: Array<{ y: number; label: string }> } | null; widthPx: number; heightPx: number; + /** + * 계획선이 든 세로 창 겹 — 붉은 선을 여기 넣으면 스크롤 때 계획선과 같은 변환을 받는다. + * 없으면 덮개 안에 그대로 둔다(세로 맞춤을 못 따라감). + */ + windowLayer?: Element | null; } /** 눈금 라벨(`880m`)에서 표고를 읽는다. 숫자가 아니면 null. */ @@ -84,6 +89,7 @@ export function createRunHighlight(options: RunHighlightOptions): SVGElement | n svg.setAttribute("viewBox", `0 0 ${widthPx} ${heightPx}`); const samples = alignment.samples; + let drawn = 0; for (const run of runs) { const [fromM, toM] = runSpanWithCurves(alignment, run); // 계획선 샘플에는 변화점·BVC·EVC가 모두 들어 있어 라운드 곡률까지 그대로 따라온다. @@ -95,7 +101,8 @@ export function createRunHighlight(options: RunHighlightOptions): SVGElement | n const line = document.createElementNS(SVG_NS, "polyline"); line.setAttribute("class", "b05-profile-runmark__line"); line.setAttribute("points", points.join(" ")); - svg.append(line); + (options.windowLayer ?? svg).append(line); + drawn += 1; // 양 끝 표시 — 어디까지가 이 구간인지(라운드 포함) 한눈에 보이게 세로 표식을 둔다. for (const edge of [fromM, toM]) { const tick = document.createElementNS(SVG_NS, "line"); @@ -107,5 +114,5 @@ export function createRunHighlight(options: RunHighlightOptions): SVGElement | n svg.append(tick); } } - return svg.childElementCount ? svg : null; + return drawn ? svg : null; } diff --git a/resources/tester/test_271_straighten_pick.py b/resources/tester/test_271_straighten_pick.py index 999b7e269..ccd75401d 100644 --- a/resources/tester/test_271_straighten_pick.py +++ b/resources/tester/test_271_straighten_pick.py @@ -199,3 +199,18 @@ def test_줄_수(): "A00_Common/design/design_profile_range_band.ts", ): assert len(_read(relative).splitlines()) <= 700 + + +def test_456_3_고름_즉시_강조_계획선_겹_전파_막음(): + # 도구 고름 뒤 종단도 한 번 다시 그림(423) + profile = _read("A00_Common/design/design_profile.ts") + assert "refresh: () => (renderBalance(), drawSoon())" in profile + # 붉은 선 = 계획선 겹 안(424) · 도구가 먹은 누름은 측점선 자기 클릭으로 안 샘(425) + render = _read("B05_Profile/B05_Profile_UI_Profile_Render.ts") + assert 'windowLayer: chartWrap.querySelector(".b06-chart__profile")?.parentElement' in render + assert "if (picked) event.stopPropagation();" in render + mark = _read("B05_Profile/B05_Profile_UI_Profile_RunHighlight.ts") + assert "(options.windowLayer ?? svg).append(line)" in mark + # 도구를 바꾸면 겹 안 붉은 선도 같이 뗌 + panel = _read("B05_Profile/B05_Profile_UI_Profile_Panel_Tools.ts") + assert ".b05-profile-runmark, .b05-profile-runmark__line" in panel