- 상세 가게 칸 셋(B06_Section_Section_Canon.ts · 가벼운 자리): 저장 정본 상세(받을 때 · 세션 · 판 바뀜 · 되돌릴 때 · 갈아끼움 제외) · 초안 전 hold 상세(판 바뀌면 새 판) · 노선 초안 상세(= pipelineDetail) - 페이지: canon.detail · held.detail · shown 사본 걷음 — 저장 기준 셈(savedEdits · storedPvi · crossNow)은 가게 칸을 읽음 · 초안 버림 = 가게 hold 상세로 정본 잡음 - 떠남(forgetSectionMemory) · invalidate 때 칸 셋 비움 · savedStations 는 sub2 관 · 구조물 줄로 셈 → 남김 - 시험: test_472_5b_detail_owner(옛 코드 빨강 3) · 묶음 헬퍼 넷(365 · 346_2 · 349_0j · 451_drop)이 종단도 흉내 정본을 가게 칸에도 채움 - 위키 design_page 값 · 규칙 · 층 줄 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
144 lines
6.2 KiB
TypeScript
144 lines
6.2 KiB
TypeScript
/* =============================================================================
|
|
* 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<string, CanonRow[]>();
|
|
/** 묶음이 먼저 푼 세션 상세 — 상세 가게가 처음 읽을 때 가져감(다시 안 풂). */
|
|
const parsed = new Map<string, SectionDetailResponse>();
|
|
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<SectionDetailResponse>("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<SectionDetailResponse>("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<string, SectionDetailResponse>();
|
|
const heldDetails = new Map<string, SectionDetailResponse>();
|
|
const draftDetails = new Map<string, SectionDetailResponse>();
|
|
|
|
const put = (
|
|
map: Map<string, SectionDetailResponse>,
|
|
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);
|
|
}
|