diff --git a/B05_Profile/B05_Profile_UI_RouteEdit_Render.ts b/B05_Profile/B05_Profile_UI_RouteEdit_Render.ts index 8bf3c074f..db1b9128b 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit_Render.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit_Render.ts @@ -184,10 +184,17 @@ export function drawRouteEditScene(context: CanvasRenderingContext2D, scene: Rou context.lineWidth = 0.8; drawPreparedLayer(context, scene.contours.layer, view, "dot", everyM); // 고른 가닥은 굵고 다른 색으로 덧그린다 — 지우고 다시 그리지 않고 위에 얹는다. + // 끊어져 여러 가닥으로 나뉜 **같은 높이 줄은 함께** 덧그린다(높이를 모르면 고른 가닥만). if (scene.pickedContour >= 0) { + const { layer } = scene.contours; + const level = layer.features[scene.pickedContour]?.labelValue ?? null; context.strokeStyle = style.getPropertyValue("--map-flow-arrow") || "#7c3aed"; context.lineWidth = 2.6; - drawPreparedFeature(context, scene.contours.layer, scene.pickedContour, view); + layer.features.forEach((feature, index) => { + if (index === scene.pickedContour || (level !== null && feature.labelValue === level)) { + drawPreparedFeature(context, layer, index, view); + } + }); } // 높이값 라벨은 여기서 안 낸다 — 노선·눈금 **뒤에** 그려야 안 묻힌다(맨 아래 참고). } diff --git a/resources/tester/ui_checks/48-3_등고선_같은높이_강조.js b/resources/tester/ui_checks/48-3_등고선_같은높이_강조.js new file mode 100644 index 000000000..178452c79 --- /dev/null +++ b/resources/tester/ui_checks/48-3_등고선_같은높이_강조.js @@ -0,0 +1,66 @@ +/* 48-3 · B05 계획노선 편집 등고선 고르기 — 끊어진 같은 높이 줄을 함께 강조 + * 시작 화면: B05 종단설계 · [계획노선 편집] 창이 떠 있음(저장 안 함 · 재기 모드 꺼짐) + * 부르는 법: orca eval --expression "$(cat 이 파일)" (async 식 · JSON 글을 돌려줌) + * 하는 일: 격자(30px)로 눌러 「고른 등고선 Nm」 가 뜨는 자리를 높이마다 하나씩 모으고, 그 높이를 골랐을 때 + * 강조색(#7c3aed) 픽셀 수를 셈 → 같은 높이의 **다른 가닥 자리**를 눌러도 같은 수가 나오는지 봄 + * 기대 수치: 같은 높이를 다른 가닥에서 골라도 강조 픽셀 수 차 ≤ 12% — 남는 차는 높이 글자 상자(같은 색)가 + * 고른 가닥 자리에 붙어 선과 겹치는 정도가 달라서 · 고치기 전 코드는 최대 57%(2026-10-05 데스크탑_보조) + * · 끝나면 빈 자리를 눌러 고름을 풂 + * 고치기 전 코드와 견줄 때: 같은 높이 두 자리의 강조 픽셀 수가 크게 다름(가닥별로만 칠함) + */ +(async () => { + const wait = (ms) => new Promise((r) => setTimeout(r, ms)); + const o = document.querySelector(".b05-routeedit"); + const c = o.querySelector("canvas"); + const status = o.querySelector(".b05-routeedit__status"); + const r = c.getBoundingClientRect(); + const fire = (type, x, y) => + c.dispatchEvent( + new PointerEvent(type, { + button: 0, + clientX: x, + clientY: y, + pointerId: 1, + pointerType: "mouse", + bubbles: true, + }), + ); + const click = (x, y) => { + fire("pointerdown", x, y); + fire("pointerup", x, y); + }; + const purple = () => { + const d = c.getContext("2d").getImageData(0, 0, c.width, c.height).data; + let n = 0; + for (let i = 0; i < d.length; i += 4) + if (d[i] === 124 && d[i + 1] === 58 && d[i + 2] === 237) n++; + return n; + }; + const byLevel = {}; + for (let y = r.top + 30; y < r.bottom - 30; y += 30) + for (let x = r.left + 30; x < r.right - 30; x += 30) { + click(x, y); + const m = status.textContent.match(/고른 등고선 (-?[\d.]+)m/); + if (!m) continue; + await wait(30); + const list = (byLevel[m[1]] ??= []); + if (list.length < 4) list.push({ x, y, purple: purple() }); + } + // 빈 자리(왼쪽 위 모서리)를 눌러 고름을 푼다. + click(r.left + 2, r.top + 2); + const levels = Object.entries(byLevel).filter(([, list]) => list.length >= 2); + const spread = levels.map(([level, list]) => { + const counts = list.map((entry) => entry.purple); + const max = Math.max(...counts); + const min = Math.min(...counts); + return { level, counts, ratio: max ? (max - min) / max : 0 }; + }); + const worst = spread.reduce((a, b) => (b.ratio > a ? b.ratio : a), 0); + return JSON.stringify({ + levelsFound: Object.keys(byLevel).length, + compared: spread.length, + worst: Math.round(worst * 1000) / 1000, + sample: spread.slice(0, 6), + pass: spread.length > 0 && worst <= 0.12, + }); +})();