feat(D03): 연도 구역 마커 끌어 놓기 · 선 밖으로 끌어내 지움 · 긋는 해 걷음 · 갈래 시점 지정 (PLAN 585-3 · 요구 445 A6 · 446)

- 도구줄 「연도 마커」(공용 지도 도구줄 year-mark · 깃발 아이콘)를 선 위로 끌어 놓음 = 그 자리 구간을 둘로 — 구역만 나눔 · 해 그대로
- 끄는 동안 마름모 미리보기 · 선 끝 · 다른 마커 1 m 안 · 선 밖 = 경고 색 · 못 놓음
- 놓인 마커 끌기 = 경계 옮김 · 선 밖으로 끌어냄 · 오른쪽 누름 · Delete = 지움(앞 구간과 합침) · 같은 해 양쪽 = 속 빈 마름모
- 좌측 [연도 구분] · 「긋는 해」 걷음 · 새 선 = 사업연도 시작 해 · 새 갈래 = 붙은 자리 구간 해
- 속성 판 [갈래 시점 지정] = 다른 선에 닿은 끝을 시점(No.0)으로 갈래 · 꼬리에 닿은 머리 = 한 선 · 본선 = [방향 바꾸기]
- 시험: 585-2 시험에 나눔 · 합침 · 갈래 해 · 갈래 시점 덧 · 530-3 갈래 해 · 553-4 속성 판 · 568-1 도구줄 목록 맞춤

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-29 18:51:04 +09:00
co-authored by Claude Opus 5.5
parent e6951b5c73
commit cc909d107e
13 changed files with 259 additions and 103 deletions
+42 -1
View File
@@ -28,6 +28,9 @@ import { createMapRotation } from "../B05_Profile/B05_Profile_UI_RouteEdit_Rotat
import { fitCanvas, screenToMetric } from "../B05_Profile/B05_Profile_UI_RouteEdit_View";
import type { Vertex } from "./logic/D03_PlanAnalysis_Plan";
/** 도구줄 「연도 마커」 끌어 놓기 자료 꼴(585-3). */
export const YEAR_MARK_DRAG = "application/x-aislo-year-mark";
/** 측점 간격(m) — B 규칙 측점과 같음. */
export const PLAN_STATION_INTERVAL_M = 20;
/** 겹 그림 색(그림 전용 값 `--draw-*` · 밝음 · 어두움 같음) — 선 · 그리는 중 · 경고 · 글 테두리. */
@@ -78,6 +81,9 @@ export interface PlanMapLayer {
press?: (px: number, py: number, map: ScreenMap, button: 0 | 2, shift: boolean) => boolean;
hover?: (px: number, py: number, map: ScreenMap) => void;
release?: (map: ScreenMap) => void;
/** 「연도 마커」를 끌고 지나감(참 = 받을 겹) · 놓음(참 = 받음) — 585-3. */
dragOver?: (px: number, py: number, map: ScreenMap) => boolean;
drop?: (px: number, py: number, map: ScreenMap) => boolean;
leave?: () => void;
}
@@ -94,6 +100,8 @@ export interface PlanTools {
view3d: boolean;
/** 거리재기 켜짐(눌림). */
measure: boolean;
/** 「연도 마커」 끌 수 있음(고칠 수 있고 선 있음). */
yearMark: boolean;
}
export interface PlanMap {
@@ -161,6 +169,12 @@ export function createPlanMap(events: PlanMapEvents): PlanMap {
const redoButton = mapToolButton("redo", { onClick: events.onRedo });
const viewButton = mapToolButton("view3d", { onClick: events.on3d });
const measureButton = mapToolButton("measure", { onClick: events.onMeasure });
const yearMarkButton = mapToolButton("year-mark", { disabled: true });
yearMarkButton.draggable = true;
yearMarkButton.addEventListener("dragstart", (event) => {
event.dataTransfer?.setData(YEAR_MARK_DRAG, "1");
if (event.dataTransfer) event.dataTransfer.effectAllowed = "copy";
});
const tools = createMapTools({
label: "평면 도구",
hint: {
@@ -174,7 +188,8 @@ export function createPlanMap(events: PlanMapEvents): PlanMap {
"Shift+누름 = 구간 더함 · 뺌",
"빈 곳 오른쪽으로 끌기 = 창 안 구간 · 왼쪽으로 = 걸친 구간까지",
"고른 선 위 끌기 = 꺾임점 더해 옮김",
"꺾임점 · 마크 끌기 = 옮김",
"「연도 마커」 선 위로 끌어 놓기 = 구역 나눔",
"꺾임점 · 마크 끌기 = 옮김 · 마크를 선 밖으로 = 지움(앞 구간과 합침)",
"오른쪽 누름 = 꺾임점 · 마크 지움",
"Delete = 고른 선 · 마크 지움",
"가운데 끌기 = 옮기기",
@@ -186,6 +201,7 @@ export function createPlanMap(events: PlanMapEvents): PlanMap {
[undoButton, redoButton],
[viewButton, "rotate-ccw", "rotate-cw"],
[markButton("stations", "측점"), markButton("slopes", "구간 길이 · 기울기"), measureButton],
[yearMarkButton],
],
});
const toolbar = tools.root;
@@ -340,6 +356,30 @@ export function createPlanMap(events: PlanMapEvents): PlanMap {
for (const layer of layers) layer.leave?.();
drawTop();
});
const markDrag = (event: DragEvent): boolean =>
!!meta && !!event.dataTransfer?.types.includes(YEAR_MARK_DRAG);
canvas.addEventListener("dragover", (event) => {
if (!markDrag(event)) return;
const [px, py] = at(event);
const map = screenMap();
if (layers.some((layer) => layer.dragOver?.(px, py, map))) {
event.preventDefault();
event.dataTransfer!.dropEffect = "copy";
}
drawTop();
});
canvas.addEventListener("drop", (event) => {
if (!markDrag(event)) return;
event.preventDefault();
const [px, py] = at(event);
const map = screenMap();
layers.some((layer) => layer.drop?.(px, py, map));
drawTop();
});
canvas.addEventListener("dragleave", () => {
for (const layer of layers) layer.leave?.();
drawTop();
});
canvas.addEventListener("contextmenu", (event) => {
event.preventDefault();
if (!meta) return;
@@ -378,6 +418,7 @@ export function createPlanMap(events: PlanMapEvents): PlanMap {
if (tools.view3d !== undefined) viewButton.disabled = !tools.view3d;
if (tools.measure !== undefined)
measureButton.setAttribute("aria-pressed", String(tools.measure));
if (tools.yearMark !== undefined) yearMarkButton.disabled = !tools.yearMark;
},
redraw: drawTop,
redrawBase: draw,