diff --git a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Page.ts b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Page.ts index a3c8746c4..6d5284cf0 100644 --- a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Page.ts +++ b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Page.ts @@ -79,12 +79,13 @@ const SITE_RADIUS_M = 1000; /** 되돌리기 쌓는 수. */ const UNDO_MAX = 100; /** 캐시 꼴 판 — 옛 꼴(지점 없음 · 갈래 고름)은 안 읽음. */ -const DRAFT_VERSION = 3; +const DRAFT_VERSION = 4; -/** 한 걸음 = 지점 + 노선(되돌리기 · 캐시 · 고침 판정). */ +/** 한 걸음 = 지점(+ 이름 · 차례 같음 · 빈 값 = 「지점 N」) + 노선(되돌리기 · 캐시 · 고침 판정). */ interface Snap { network: PlanNetwork; points: Vertex[]; + pointNames: string[]; } interface Draft extends Snap { @@ -99,15 +100,17 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise { const projectId = currentProjectId(); let editable = false; let points: Vertex[] = []; // 지점(긋기에 붙는 점 · 591-2) + let pointNames: string[] = []; // 지점 이름(596 · 597-2) let epsg = 0; let yearStart = new Date().getFullYear(); // 새 선 처음 해 let network: PlanNetwork = emptyNetwork(); - let savedJson = JSON.stringify({ network, points }); // 못 읽은 채 떠남 = 고침 없음 + let savedJson = JSON.stringify({ network, points, pointNames }); // 못 읽은 채 떠남 = 고침 없음 let undoStack: Snap[] = []; let redoStack: Snap[] = []; let surface: PlanSurface | null = null; let have: [number, number, number, number] | null = null; let viewCore: Vertex[] = []; // 지도 보기 가운데 상자 두 귀(넓으면 빈 것) + let viewMid: Vertex | null = null; // 지도 보기 가운데(지점 [추가] 자리) let viewTimer = 0; let lastTried = ""; // 같은 받기를 거듭하지 않음 let ready = false; // 들어올 때 받기 판단이 끝남(그 앞 보기 바뀜은 안 받음) @@ -150,9 +153,12 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise { const marks = createPlanPoints({ points: () => points, network: () => network, + names: () => pointNames, epsg: () => epsg, draw, - commit: (nextPoints, next) => step({ network: next, points: nextPoints }), + commit: (nextPoints, nextNames, next) => + step({ network: next, points: nextPoints, pointNames: nextNames }), + center: () => viewMid, show: (nextPoints, next) => { [points, network] = [nextPoints, next]; map.redraw(); @@ -172,6 +178,7 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise { ].map(([x, y]) => screen.toMetric(...screen.unrotate(x, y))); const xs = corners.map((c) => c[0]); const ys = corners.map((c) => c[1]); + viewMid = w ? screen.toMetric(...screen.unrotate(w / 2, h / 2)) : null; const box: Vertex[] = [ [Math.min(...xs), Math.min(...ys)], [Math.max(...xs), Math.max(...ys)], @@ -246,7 +253,7 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise { root.querySelector(".ui-workflow-layout")?.classList.add("d03-plan-layout"); const allVertices = (): Vertex[] => network.lines.flatMap((line) => line.vertices); - const snap = (): Snap => ({ network, points }); + const snap = (): Snap => ({ network, points, pointNames }); const dirty = (): boolean => JSON.stringify(snap()) !== savedJson; const keepDraft = (): void => dirty() @@ -257,21 +264,25 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise { function step(next: Snap): void { undoStack = [...undoStack.slice(1 - UNDO_MAX), snap()]; redoStack = []; - [network, points] = [normalizePieces(next.network, yearStart), next.points]; + [network, points, pointNames] = [ + normalizePieces(next.network, yearStart), + next.points, + next.pointNames, + ]; keepDraft(); refresh(); void ensureCovered(); } function commit(next: PlanNetwork): void { - step({ network: next, points }); + step({ network: next, points, pointNames }); } function undo(): void { const last = undoStack.pop(); if (!last) return; redoStack.push(snap()); - ({ network, points } = last); + ({ network, points, pointNames } = last); keepDraft(); refresh(); } @@ -280,7 +291,7 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise { const next = redoStack.pop(); if (!next) return; undoStack.push(snap()); - ({ network, points } = next); + ({ network, points, pointNames } = next); keepDraft(); refresh(); } @@ -422,6 +433,7 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise { ? fromServer(server.lines, server.marks, server.pieces, yearStart) : emptyNetwork(); points = view.base.points.map((p): Vertex => [p.x, p.y]); + pointNames = view.base.points.map(() => ""); // 594-1 이름 칸이 들어오면 `p.name ?? ""` if (view.base.epsg) epsg = view.base.epsg; savedJson = JSON.stringify(snap()); } @@ -553,7 +565,7 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise { takeServer(view); const draft = readState("d03-plan", projectId); if (draft?.version === DRAFT_VERSION && draft.network && Array.isArray(draft.points)) - ({ network, points } = draft); + ({ network, points, pointNames } = draft); const tm = tmParams(epsg); const siteXy = site && tm ? lonLatToTm(tm, site.lon, site.lat) : null; const around: Vertex[] = siteXy diff --git a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Points.ts b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Points.ts index 3cb3b662f..7565f8f35 100644 --- a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Points.ts +++ b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Points.ts @@ -27,11 +27,15 @@ const PIN_HEAD_PX = 18; export interface PlanPointsParams { points: () => Vertex[]; + /** 지점 이름(차례 = `points` · 빈 값 = 「지점 N」). */ + names: () => string[]; network: () => PlanNetwork; epsg: () => number; draw: PlanDraw; - /** 한 걸음 — 지점 · 노선 함께. */ - commit: (points: Vertex[], network: PlanNetwork) => void; + /** 한 걸음 — 지점 · 이름 · 노선 함께. */ + commit: (points: Vertex[], names: string[], network: PlanNetwork) => void; + /** 지도 보기 가운데(작업 좌표 · 못 잼 = null) — [추가] 자리. */ + center: () => Vertex | null; /** 끌기 중 — 화면만. */ show: (points: Vertex[], network: PlanNetwork) => void; /** 쉴 때(긋기 · 재기 아님)만 핀을 끎. */ @@ -86,6 +90,7 @@ export function createPlanPoints(params: PlanPointsParams): PlanPoints { function remove(i: number): void { params.commit( params.points().filter((_, k) => k !== i), + params.names().filter((_, k) => k !== i), params.network(), ); } @@ -100,7 +105,7 @@ export function createPlanPoints(params: PlanPointsParams): PlanPoints { .filter((item) => item.gap <= SNAP_PX) .sort((a, b) => a.gap - b.gap)[0]; const at = node?.point ?? map.toMetric(px, py); - params.commit([...params.points(), [at[0], at[1]]], params.network()); + params.commit([...params.points(), [at[0], at[1]]], [...params.names(), ""], params.network()); } function openMenu(px: number, py: number, map: ScreenMap): boolean { @@ -154,7 +159,7 @@ export function createPlanPoints(params: PlanPointsParams): PlanPoints { drag = null; if (!done?.last) return; params.show(done.points, done.network); - params.commit(...done.last); + params.commit(done.last[0], params.names(), done.last[1]); }, };