/* ============================================================================= * B06_Section_Section_Canon.ts * 세션 상세(`section-detail`) = **저장 정본만**(387-7) — 서버에서 받은 그대로 한 벌 · 갈아끼움 · 되돌림 · * 다시 셈 결과는 안 씀(메모리 캐시 몫). 세션 글은 **한 번만** 풂 — 상세 가게(`_Section_Store`)와 계산 묶음 * cross 입력(`design_pipeline_cross`)이 이 파일을 함께 봄(예전에는 둘이 따로 1.5 MB 를 풂). * 가벼운 자리 — 상세 가게의 화면 줄기를 안 끌고 묶음 입력이 부름. * ========================================================================== */ import { readState } from "../A00_Common/b_page_state"; import type { CrossSection, SectionDetailResponse } from "./B06_Section_Api_Fetch"; /** 정본 측점 설계 — 받을 때 뜬 사본(다시 셈 · 초안 얹기가 고치는 캐시 객체와 따로) · 자리 · 표본(제자리로 돌아온 * 측점이 정본 표본을 다시 씀 · 467 · 배열은 받은 것 그대로 · 다시 셈은 새 배열로 갈아 끼움). */ export type CanonRow = { chainage_m: number; design: CrossSection["design"] } & Partial< Pick< CrossSection, | "center_x" | "center_y" | "center_z" | "frame" | "samples" | "azimuth_deg" | "plan_radius_m" | "curve_outer_side" | "curve_widening_m" | "uphill_side" > >; const canon = new Map(); /** 묶음이 먼저 푼 세션 상세 — 상세 가게가 처음 읽을 때 가져감(다시 안 풂). */ const parsed = new Map(); const keyOf = (projectId: string, routeId: number): string => `${projectId}:${routeId}`; /** 서버에서 받은(또는 세션에서 푼) 정본의 설계 사본을 떠 둠. */ export function keepCanon(projectId: string, routeId: number, detail: SectionDetailResponse): void { const key = keyOf(projectId, routeId); parsed.delete(key); canon.set( key, detail.cross_sections.map((section) => ({ chainage_m: section.chainage_m, design: section.design ? { ...section.design } : section.design, center_x: section.center_x, center_y: section.center_y, center_z: section.center_z, frame: section.frame, samples: section.samples, azimuth_deg: section.azimuth_deg, plan_radius_m: section.plan_radius_m, curve_outer_side: section.curve_outer_side, curve_widening_m: section.curve_widening_m, uphill_side: section.uphill_side, })), ); } /** 새로고침 — 세션 상세(묶음이 먼저 풀었으면 그 객체) · 정본 사본도 뜸. */ export function sessionDetail(projectId: string, routeId: number): SectionDetailResponse | null { const key = keyOf(projectId, routeId); const ready = parsed.get(key); if (ready) { parsed.delete(key); return ready; } const stored = readState("section-detail", projectId, routeId); if (stored) keepCanon(projectId, routeId, stored); return stored; } /** 정본 측점 설계(묶음 cross 입력 · 365-5) — 아직 안 읽었으면 세션에서 한 번(상세 가게가 그 객체를 이어 씀). */ export function savedDesigns(projectId: string, routeId: number): CanonRow[] | null { const key = keyOf(projectId, routeId); if (!canon.has(key)) { const stored = readState("section-detail", projectId, routeId); if (!stored) return null; keepCanon(projectId, routeId, stored); parsed.set(key, stored); } return canon.get(key) ?? null; } /** 정본이 바뀜(새로 받을 것) — 사본을 버림. 노선을 안 주면 그 프로젝트 전부. */ export function forgetCanon(projectId: string, routeId?: number): void { for (const map of [canon, parsed]) for (const key of [...map.keys()]) if ( routeId === undefined ? key.startsWith(`${projectId}:`) : key === keyOf(projectId, routeId) ) map.delete(key); } /* ── 상세 가게 칸 셋(472-5 5-B) — 상세 객체의 주인은 상세 가게 하나 · 계산 묶음 페이지는 지문(노선 · 표본)만 쥠 ── * 저장 정본 상세 = 가게가 받거나(새로 받음 · 세션) 되돌린 객체(측점 갈아끼움 제외 — 그것은 정본 아님) * 초안 전 상세 = 노선 초안을 시작할 때 떠 둔 객체(버리면 이것으로 · 판이 바뀌면 새 판) * 노선 초안 상세 = 계산 묶음이 새 노선으로 지은 객체(초안 동안 두 보기가 그림) */ const savedDetails = new Map(); const heldDetails = new Map(); const draftDetails = new Map(); const put = ( map: Map, key: string, detail: SectionDetailResponse | null, ) => void (detail ? map.set(key, detail) : map.delete(key)); export const keepSavedDetail = ( projectId: string, routeId: number, detail: SectionDetailResponse | null, ): void => put(savedDetails, keyOf(projectId, routeId), detail); export const savedSectionDetail = ( projectId: string, routeId: number, ): SectionDetailResponse | null => savedDetails.get(keyOf(projectId, routeId)) ?? null; export const keepHeldDetail = ( projectId: string, routeId: number, detail: SectionDetailResponse | null, ): void => put(heldDetails, keyOf(projectId, routeId), detail); export const heldSectionDetail = ( projectId: string, routeId: number, ): SectionDetailResponse | null => heldDetails.get(keyOf(projectId, routeId)) ?? null; export const setDraftSectionDetail = ( projectId: string, detail: SectionDetailResponse | null, ): void => put(draftDetails, projectId, detail); export const draftDetailOf = (projectId: string): SectionDetailResponse | null => draftDetails.get(projectId) ?? null; /** 메모리 상세를 버림(페이지 떠남 · 정본 바뀜) — 칸 셋. 노선을 안 주면 그 프로젝트 전부. */ export function forgetDetailSlots(projectId: string, routeId?: number): void { for (const map of [savedDetails, heldDetails]) for (const key of [...map.keys()]) if ( routeId === undefined ? key.startsWith(`${projectId}:`) : key === keyOf(projectId, routeId) ) map.delete(key); if (routeId === undefined) draftDetails.delete(projectId); }