Files
Aislo/B05_Profile/B05_Profile_UI_RouteEdit_Hover.ts
T
eomsangdonandClaude Opus 5.5 8a9ae2113b feat(381-3 B05 평면): 교각점 크게 · 라벨 위 · 맴돔 강조 · 노드와 횡단 눈금 집기 한 길
- 노드 반지름 4 → 6(고름 +2) · 노드 · 손잡이를 라벨 뒤 맨 위로
- 집기 문턱 노드 9 → 12 · pickAtScreen: 노드 가운데 6px 안 = 노드 · 밖 = 노드와 눈금 중 가까운 쪽
- stationAtScreen → {chainage, distance} · 맴돔은 집기와 같은 셈 · 노드 = 고리 · 눈금 = 파랑(새 _Hover)
- 누를 때 · 끄는 동안 · 재는 중 · 캔버스 밖 = 맴돔 끔 · [측점] 끔이면 눈금 안 봄
- 시험 test_b05_routeedit_pick 겹침 경우 · test_381_3_routeedit_node_pick

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
2026-10-17 14:50:03 +09:00

68 lines
2.7 KiB
TypeScript

/* =============================================================================
* B05_Profile_UI_RouteEdit_Hover.ts
* 노선 편집 평면 **맴돔 밝힘**(요구 241-14 · PLAN 381-3).
*
* 맴도는 자리는 `_Pointer` 가 `_Input.pickAtScreen`(집기와 같은 셈)으로 적고, 여기서 이길 쪽만
* 그린다 — 노드 = 바깥 고리 · 횡단 눈금 = 파란 짧은 직각선. `_Render` 가 노드 그린 뒤 부름.
* ========================================================================== */
import { pickHover } from "./B05_Profile_UI_RouteEdit_Input";
import type { RouteEditScene } from "./B05_Profile_UI_RouteEdit_Render";
import { stationFrameAt } from "./logic/B05_Profile_UI_RouteEdit_Frame";
import type { Vertex } from "./logic/B05_Profile_UI_RouteEdit_Curve";
/** 횡단 눈금 맴돔 색 — 횡단 반폭 선(`_Render`)과 같은 하늘색. */
const STATION_HOVER_COLOR = "#0ea5e9";
/** 맴도는 자리 밝힘(381-3) — 누르면 집힐 쪽만: 노드 = 바깥 고리 · 횡단 눈금 = 파란 짧은 직각선. */
export function drawPickHover(
context: CanvasRenderingContext2D,
scene: RouteEditScene,
line: ReadonlyArray<Vertex>,
style: CSSStyleDeclaration,
nodeR: number,
): void {
const hover = pickHover(context.canvas);
if (!hover) return;
context.save();
context.setLineDash([]);
if ("node" in hover) {
const vertex = scene.planned[hover.node];
if (vertex) {
const [x, y] = scene.toScreen(vertex);
const radius = (hover.node === scene.picked ? nodeR + 2 : nodeR) + 4;
context.beginPath();
context.arc(x, y, radius, 0, Math.PI * 2);
context.strokeStyle = style.getPropertyValue("--map-halo") || "rgba(255,255,255,0.9)";
context.lineWidth = 4;
context.stroke();
context.strokeStyle = style.getPropertyValue("--map-route") || "#f97316";
context.lineWidth = 2;
context.stroke();
}
} else {
const frame = stationFrameAt(line, hover.chainage);
if (frame) {
const [ax, ay] = scene.toScreen(frame.origin);
const [bx, by] = scene.toScreen([
frame.origin[0] + frame.left[0],
frame.origin[1] + frame.left[1],
]);
const length = Math.hypot(bx - ax, by - ay) || 1;
const ux = ((bx - ax) / length) * 12;
const uy = ((by - ay) / length) * 12;
context.strokeStyle = STATION_HOVER_COLOR;
context.fillStyle = STATION_HOVER_COLOR;
context.lineWidth = 3;
context.beginPath();
context.moveTo(ax - ux, ay - uy);
context.lineTo(ax + ux, ay + uy);
context.stroke();
context.beginPath();
context.arc(ax, ay, 4, 0, Math.PI * 2);
context.fill();
}
}
context.restore();
}