Merge remote-tracking branches 'origin/main_desktop_1' and 'origin/sub_desktop_1' into main_laptop_1
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
/* 48-1 · B05 계획노선 편집 구간 재기 — 첫 점 뒤 커서 자리 값 실시간 표시
|
||||
* 시작 화면: B05 종단설계 · [계획노선 편집] 창이 떠 있음(노선이 있는 프로젝트 · 저장 안 함)
|
||||
* 부르는 법: orca eval --expression "$(cat 이 파일)" (async 식 · JSON 글을 돌려줌)
|
||||
* 하는 일: 격자(40px)로 계획노선 자리 둘(a · b)을 찾고 → a 를 찍음 → b 로 커서 이동 → 높이 표가
|
||||
* 올 때까지 기다림 → 커서를 b 근처에서 여러 번 움직이며 서버 부름 수를 셈 → b 를 찍어 고정 값과 비교
|
||||
* 기대 수치: hover 글에 「길이」·「종단기울기」·「누르면 고정」 · 표가 온 뒤 커서 이동 중 /route/elevations
|
||||
* 부름 0 · 고정 글의 길이 = 미리보기 길이 · 기울기 차 ≤ 0.5%p(보간 vs 두 점 정밀 조회)
|
||||
* · 끝나면 잰 것 지우고 재기 단추 끔
|
||||
*/
|
||||
(async () => {
|
||||
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
const o = document.querySelector(".b05-routeedit");
|
||||
const c = o.querySelector("canvas");
|
||||
const box = o.querySelector(".b05-routeedit__measure");
|
||||
const text = o.querySelector(".b05-routeedit__measure-text");
|
||||
const btn = o.querySelector('[data-act="measure"]');
|
||||
const close = o.querySelector(".b05-routeedit__measure-close");
|
||||
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 ev = (type, x, y) => {
|
||||
fire(type, x, y);
|
||||
if (type === "pointerdown") fire("pointerup", x, y);
|
||||
};
|
||||
let calls = 0;
|
||||
const realFetch = window.fetch;
|
||||
window.fetch = (...args) => {
|
||||
if (String(args[0]).includes("/route/elevations")) calls += 1;
|
||||
return realFetch(...args);
|
||||
};
|
||||
const out = {};
|
||||
try {
|
||||
if (!btn.classList.contains("is-active")) btn.click();
|
||||
const planned = [];
|
||||
for (let y = r.top + 20; y < r.bottom - 20 && planned.length < 40; y += 40)
|
||||
for (let x = r.left + 20; x < r.right - 20; x += 40) {
|
||||
ev("pointerdown", x, y);
|
||||
if (/시작/.test(text.textContent) && !/^예상노선/.test(text.textContent))
|
||||
planned.push([x, y]);
|
||||
if (!box.hidden) close.click();
|
||||
if (!btn.classList.contains("is-active")) btn.click();
|
||||
}
|
||||
const a = planned[0];
|
||||
const b = planned.find((p) => Math.hypot(p[0] - a[0], p[1] - a[1]) > 200);
|
||||
out.found = planned.length;
|
||||
if (!a || !b) return JSON.stringify({ ...out, error: "자리 못 찾음" });
|
||||
ev("pointerdown", ...a);
|
||||
ev("pointermove", ...b);
|
||||
out.hoverFirst = text.textContent;
|
||||
for (let i = 0; i < 150 && !/종단기울기|못 냅니다/.test(text.textContent); i += 1)
|
||||
await wait(100);
|
||||
out.hover = text.textContent;
|
||||
const before = calls;
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
ev("pointermove", b[0] + (i % 3) - 1, b[1] + (i % 2));
|
||||
await wait(30);
|
||||
}
|
||||
ev("pointermove", ...b);
|
||||
out.hoverCallsWhileMoving = calls - before;
|
||||
out.hoverAgain = text.textContent;
|
||||
ev("pointerdown", ...b);
|
||||
for (let i = 0; i < 150 && !/종단기울기|못 냅니다/.test(text.textContent); i += 1)
|
||||
await wait(100);
|
||||
out.final = text.textContent;
|
||||
const len = (s) => (s.match(/길이 ([\d.]+)m/) || [])[1];
|
||||
const grade = (s) => Number((s.match(/종단기울기 (-?[\d.]+)%/) || [])[1]);
|
||||
out.pass =
|
||||
/길이/.test(out.hoverAgain) &&
|
||||
/종단기울기/.test(out.hoverAgain) &&
|
||||
/누르면 고정/.test(out.hoverAgain) &&
|
||||
out.hoverCallsWhileMoving === 0 &&
|
||||
len(out.hoverAgain) === len(out.final) &&
|
||||
Math.abs(grade(out.hoverAgain) - grade(out.final)) <= 0.5;
|
||||
} finally {
|
||||
window.fetch = realFetch;
|
||||
if (!box.hidden) close.click();
|
||||
if (btn.classList.contains("is-active")) btn.click();
|
||||
}
|
||||
return JSON.stringify(out);
|
||||
})();
|
||||
@@ -0,0 +1,48 @@
|
||||
/* 48-2 · B05 계획노선 편집 [노선 추천] 모달 — 오른쪽 세로 구분선 + 설명 칸
|
||||
* 시작 화면: B05 종단설계 · [계획노선 편집] 창이 떠 있음(노선 추천 모달은 닫힘)
|
||||
* 부르는 법: orca eval --expression "$(cat 이 파일)" (async 식 · JSON 글을 돌려줌)
|
||||
* 하는 일: [노선 추천] 열기 → 설명 칸 자리 · 구분선 · 갈래 여덟 확인 → 「비중 계산」 누름 → 강조가 따라가고
|
||||
* 보이는 자리에 있는지 확인 → 셈이 끝나면 [닫기](미리 보기 버림 · 편집값 안 바뀜)
|
||||
* 기대 수치: 설명 칸이 왼쪽 칸 오른쪽(left > 지도 right) · 왼쪽 테두리 1px · 갈래 글 8 · 처음 강조 follow
|
||||
* · 누른 뒤 강조 weighted 1개 · 강조 글이 설명 칸 안에 보임 · 설명 칸 높이 = 본문 높이(창을 키우지 않음)
|
||||
*/
|
||||
(async () => {
|
||||
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
const editor = document.querySelector(".b05-routeedit");
|
||||
[...editor.querySelectorAll("button")].find((b) => b.textContent.trim() === "노선 추천").click();
|
||||
await wait(1500);
|
||||
const modal = document.querySelector(".ui-modal.b05-routerec");
|
||||
const body = modal.querySelector(".b05-routerec__body");
|
||||
const help = modal.querySelector(".b05-routerec__help");
|
||||
const map = modal.querySelector(".b05-routerec__main").getBoundingClientRect();
|
||||
const box = help.getBoundingClientRect();
|
||||
const out = {
|
||||
rightOfLeft: box.left > map.right,
|
||||
border: getComputedStyle(help).borderLeftWidth,
|
||||
items: help.querySelectorAll("[data-help-logic]").length,
|
||||
first: help.querySelector(".is-on")?.dataset.helpLogic,
|
||||
sameHeight: Math.abs(box.height - body.clientHeight) <= 2,
|
||||
};
|
||||
modal.querySelector('[data-logic="weighted"]').click();
|
||||
await wait(300);
|
||||
const on = help.querySelectorAll(".is-on");
|
||||
const onBox = on[0].getBoundingClientRect();
|
||||
out.after = on[0].dataset.helpLogic;
|
||||
out.onCount = on.length;
|
||||
out.visible = onBox.top >= box.top - 1 && onBox.bottom <= box.bottom + 1;
|
||||
for (let i = 0; i < 300 && modal.querySelector('[data-logic="weighted"]').disabled; i += 1)
|
||||
await wait(100);
|
||||
modal.querySelector('[data-act="close"]').click();
|
||||
out.closed = !document.querySelector(".ui-modal.b05-routerec");
|
||||
out.pass =
|
||||
out.rightOfLeft &&
|
||||
out.border === "1px" &&
|
||||
out.items === 8 &&
|
||||
out.first === "follow" &&
|
||||
out.sameHeight &&
|
||||
out.after === "weighted" &&
|
||||
out.onCount === 1 &&
|
||||
out.visible &&
|
||||
out.closed;
|
||||
return JSON.stringify(out);
|
||||
})();
|
||||
@@ -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