fix(B05): 계획노선 편집 등고선 고르기 — 끊어진 같은 높이 줄을 함께 강조
- 고른 가닥과 높이가 같은 가닥을 모두 덧그림 · 높이를 모르면 고른 가닥만 - 검증 시나리오 ui_checks/48-3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
This commit is contained in:
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 높이값 라벨은 여기서 안 낸다 — 노선·눈금 **뒤에** 그려야 안 묻힌다(맨 아래 참고).
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user