From 37068d439d9454c694981632468cddf0d6bf65da Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 30 Oct 2026 14:35:12 +0900 Subject: [PATCH] =?UTF-8?q?feat(D=20=EC=98=88=EC=A0=95=EB=85=B8=EC=84=A0):?= =?UTF-8?q?=20Page=20=EC=A7=80=EC=A0=90=20=EB=84=98=EA=B9=80=20=EA=BC=B4?= =?UTF-8?q?=20=E2=80=94=20=EC=A7=80=EC=A0=90=20=EC=9D=B4=EB=A6=84=20=C2=B7?= =?UTF-8?q?=20commit(points,=20names,=20network)=20=C2=B7=20=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20=EA=B0=80=EC=9A=B4=EB=8D=B0=20center=20(PLAN=20596?= =?UTF-8?q?=20=EC=9D=B4=EC=9D=8C=20=C2=B7=20597=20=EB=B0=9B=EC=9D=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Snap 에 pointNames · 캐시 판 4 - Points 인자 꼴만 맞춤(이름 함께 넘김) · 칸 고침은 597-2 몫 Co-Authored-By: Claude Opus 5.5 --- D03_PlanAnalysis/D03_PlanAnalysis_UI_Page.ts | 32 +++++++++++++------ .../D03_PlanAnalysis_UI_Points.ts | 13 +++++--- 2 files changed, 31 insertions(+), 14 deletions(-) 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]); }, };