- 391-1 측점 맴돔 · 고름 = 한 함수(drawStationNormal) · 좌우 20 m 고정 하늘색 · 짧은 12 px 선 · 절성토 색 지움 · 구조물 마름모 그대로 - 391-2 고른 곡선 = 굵은 호 + 교각점→BC · EC 접선 + BC · MC · EC 세 점(새 파일 RouteEdit_Curve_Pick · MC = curveMidVertex) · 곡선 없는 교각점 = 같은 크기 삼각형 - 391-3 잠금 = 황금(#ca8a04 · 어두움 #facc15) · 굵기 4 + 흰 테 - 시험 test_391_routeedit_plan_draw 새로 · test_381_3 맴돔 배선 고쳐 씀 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
91 lines
3.5 KiB
TypeScript
91 lines
3.5 KiB
TypeScript
/* =============================================================================
|
|
* B05_Profile_UI_RouteEdit_Hover.ts
|
|
* 노선 편집 평면 **맴돔 밝힘**(요구 241-14 · PLAN 381-3).
|
|
*
|
|
* 맴도는 자리는 `_Pointer` 가 `_Input.pickAtScreen`(집기와 같은 셈)으로 적고, 여기서 이길 쪽만
|
|
* 그린다 — 노드 = 바깥 고리 · 횡단 눈금 = 고른 측점과 같은 좌우 20 m 법선(`drawStationNormal` ·
|
|
* 391-1). `_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";
|
|
|
|
/** 측점 법선 색 — 노선(주황)·재기(초록)·등고선(연보라)과 겹치지 않는 하늘색. */
|
|
const STATION_NORMAL_COLOR = "#0ea5e9";
|
|
/** 법선 좌우 길이(m) — 서버 `SECTION_CROSS_HALF_WIDTH_M` 과 같은 값 · 고정(391-1). */
|
|
const STATION_NORMAL_HALF_M = 20;
|
|
|
|
/** 측점 법선(391-1) — 맴돔 · 고름 한 그림: 측점에서 노선에 직각 좌우 20 m · 끝 점 · 측점 고리.
|
|
* 방향은 지금 그려진 노선으로 매 프레임 다시 짚음(`stationFrameAt`) — 끄는 동안 바로 따라감. */
|
|
export function drawStationNormal(
|
|
context: CanvasRenderingContext2D,
|
|
scene: RouteEditScene,
|
|
line: ReadonlyArray<Vertex>,
|
|
chainageM: number,
|
|
): void {
|
|
const frame = stationFrameAt(line, chainageM);
|
|
if (!frame) return;
|
|
const [ox, oy] = frame.origin;
|
|
const [lx, ly] = frame.left;
|
|
const half = STATION_NORMAL_HALF_M;
|
|
const [cx, cy] = scene.toScreen(frame.origin);
|
|
const [ax, ay] = scene.toScreen([ox + lx * half, oy + ly * half]);
|
|
const [bx, by] = scene.toScreen([ox - lx * half, oy - ly * half]);
|
|
context.save();
|
|
context.setLineDash([]);
|
|
context.strokeStyle = STATION_NORMAL_COLOR;
|
|
context.fillStyle = STATION_NORMAL_COLOR;
|
|
context.lineWidth = 2.4;
|
|
context.beginPath();
|
|
context.moveTo(ax, ay);
|
|
context.lineTo(bx, by);
|
|
context.stroke();
|
|
for (const [x, y] of [
|
|
[ax, ay],
|
|
[bx, by],
|
|
]) {
|
|
context.beginPath();
|
|
context.arc(x, y, 3, 0, Math.PI * 2);
|
|
context.fill();
|
|
}
|
|
context.lineWidth = 2;
|
|
context.beginPath();
|
|
context.arc(cx, cy, 6, 0, Math.PI * 2);
|
|
context.stroke();
|
|
context.restore();
|
|
}
|
|
|
|
/** 맴도는 자리 밝힘(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 {
|
|
drawStationNormal(context, scene, line, hover.chainage);
|
|
}
|
|
context.restore();
|
|
}
|