feat(774-1): 다시 긋기 = 중간 노드 고름으로 켬 · 다른 중간 노드에 이어야 끝

- 노드 속성 판 · 우클릭 = 중간 노드에 [다시 긋기](선 토막 고름의 [다시 긋기] 뺌) · B05 긋기 중 중간 노드 누름 = 그대로 켬
- 끝 = 같은 선 다른 중간 노드만(rejoinMiss) · 시작 노드 · 끝점 = 못 찍음 + 안내(계속 긋기) · 빈 곳 = 점 찍고 계속
- 고리: 시작 노드 · 이을 중간 노드 · 마우스 올린 중간 노드(paintRejoin · --draw-map-*) · 옛 구간 흐림(dim) 뺌
- 시험: test_771_2 에 끝 판정 · 중간 노드 대조 더함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-03 20:31:21 +09:00
co-authored by Claude Opus 5.5
parent ea6207d587
commit 2a67a7092d
7 changed files with 127 additions and 98 deletions
@@ -168,8 +168,7 @@ export function lineAt(
return best;
}
/** 그은 선 · 측점 · 구간 글 · 꺾임점 · 마크 · 연도 · (extra = 미리보기).
* `active` = 그리는 선(굵은 보라) · `dim` = 다시 긋는 구간(599-3 · 흐리게). */
/** 그은 선 · 측점 · 구간 글 · 꺾임점 · 마크 · 연도 · (extra = 미리보기) · `active` = 그리는 선(굵은 보라). */
export function paintNetwork(
context: CanvasRenderingContext2D,
map: ScreenMap,
@@ -177,7 +176,6 @@ export function paintNetwork(
active: number | null,
z: HeightAt,
extra: () => void,
dim: { line: number; from: number; to: number } | null = null,
): void {
const colors = planColors();
context.lineCap = "round";
@@ -186,22 +184,13 @@ export function paintNetwork(
if (line.vertices.length < 2) return;
const color = index === active ? colors.active : routeColor(index);
const width = 3;
const path = (vertices: ReadonlyArray<Vertex>): void => {
context.beginPath();
vertices.forEach((v, i) => {
const [sx, sy] = map.toScreen(v);
if (i) context.lineTo(sx, sy);
else context.moveTo(sx, sy);
});
strokeCased(context, color, width); // 밝은 바탕 대비 = 검은 테(600-1 · 463-24)
};
if (dim?.line === index) {
path(line.vertices.slice(0, dim.from + 1));
path(line.vertices.slice(dim.to));
context.globalAlpha = 0.3;
path(line.vertices.slice(dim.from, dim.to + 1));
context.globalAlpha = 1;
} else path(line.vertices);
context.beginPath();
line.vertices.forEach((v, i) => {
const [sx, sy] = map.toScreen(v);
if (i) context.lineTo(sx, sy);
else context.moveTo(sx, sy);
});
strokeCased(context, color, width); // 밝은 바탕 대비 = 검은 테(600-1 · 463-24)
if (map.stations)
drawStationTicks(
context,
@@ -245,6 +234,29 @@ export function paintNodes(
});
}
/** 다시 긋기 고리(774) — `start` 시작 노드 = 굵은 그리기 색 · 그 밖 중간 노드 = 이을 수 있음(얇은 재기 색) ·
* `hover` = 마우스 올린 중간 노드(그리기 색) · 끝점 = 고리 없음. */
export function paintRejoin(
context: CanvasRenderingContext2D,
map: Pick<ScreenMap, "toScreen">,
vertices: ReadonlyArray<Vertex>,
start: number | null,
hover: number | null,
): void {
const colors = planColors();
const ring = (i: number, pad: number, color: string, width: number): void => {
const [sx, sy] = map.toScreen(vertices[i]);
context.beginPath();
context.arc(sx, sy, NODE_DOT_PX + pad, 0, Math.PI * 2);
strokeCased(context, color, width);
};
if (start !== null)
for (let i = 1; i < vertices.length - 1; i += 1)
if (i !== start) ring(i, 4, colors.measure, 1.5);
if (start !== null) ring(start, 5, colors.active, 3);
if (hover !== null) ring(hover, 6, colors.active, 2.5);
}
/** 지점 핀 겹(585-1) — 꼭지 = 지점 · 둥근 머리 · 「지점 N」 · 화면 px 고정 크기 · 돌린 지도에서도 섬. */
export function pointLayer(points: () => Vertex[], names: () => string[] = () => []): PlanMapLayer {
return {