100 lines
4.9 KiB
TypeScript
100 lines
4.9 KiB
TypeScript
/* =============================================================================
|
|
* A00_Common/design/logic/design_calc_node_cross.ts
|
|
* 계산 Node 입구(`design_calc_node.ts`)의 횡단 몫(PLAN 511-B B-4) — 서버 GET · [저장] 이 부르는 횡단 셈을
|
|
* 브라우저와 같은 TS 로(파이썬 짝을 걷는 자리).
|
|
*
|
|
* facility.attach 측점 그림 자료(관 세트 · 벽 · 포장 · 맹암거 · 빈칸 경고 · `attachShapes` · F4 F5 F6 C6)
|
|
* 입력 attach { sections: [{ chainage_m · center_x · center_y }] · pipes · structures · types }
|
|
* 출력 { shapes: 측점마다 그림 자료 칸만 · misses: { outside · far } }
|
|
* cross.enforce [저장] 횡단 한 바퀴(`enforceSavedCross` · 설계선 → 미교차 넓힘 → 포장 → 세월교 하강 · G2 C3)
|
|
* 입력 cross_enforce = `SaveCrossInput` + grid(DTM 격자 이진 경로 · 넓힘 때만 읽음)
|
|
* 출력 측점마다 { design · samples?(넓힘) · extended? } | null
|
|
* cross.refresh 계산 자리 cross = server 의 저장 노선 횡단 다시 셈(`refreshDesigns` + 구조물 폐회로 면적)
|
|
* 입력 cross_refresh { detail · inputs: `RefreshInputs` · grid(서버 창구가 채움) }
|
|
* 출력 { design_profiles · sections: [{ design · samples?(넓힘) }] · rows: 구조물 면적 줄 }
|
|
* — 등록부 밖(`PAGE_IDS` · 파이썬 `calc_runner.PAGE_IDS`) · 화면 cross 단계 길
|
|
* 구조물 표 = 서버 정의(`definition` · M02 반영) — 받으면 갈아 끼움(파이썬 `catalog_row` 와 같은 표).
|
|
* ========================================================================== */
|
|
|
|
import { attachShapes, SHAPE_KEYS, type AttachType } from "@util/common_util_structure_attach";
|
|
import { useCatalogDefinition, type CatalogDefinition } from "@util/common_util_structure_catalog";
|
|
import type { PlanPipe, PlanStructure } from "@util/common_util_structure_station";
|
|
import {
|
|
enforceSavedCross,
|
|
type SaveCrossInput,
|
|
} from "../../../B06_Section/logic/B06_Section_Cross_Save";
|
|
import {
|
|
refreshDesigns,
|
|
type RefreshInputs,
|
|
} from "../../../B06_Section/logic/B06_Section_Cross_Refresh_Calc";
|
|
import type { SectionDetailResponse } from "../../../B06_Section/B06_Section_Api_Fetch";
|
|
import { structureAreaRows } from "../../../B06_Section/B06_Section_Structure_Layouts";
|
|
import { gridAt } from "./design_calc_node_stages";
|
|
|
|
export interface CrossInputs {
|
|
definition?: CatalogDefinition | null;
|
|
attach?: {
|
|
sections: Array<{ chainage_m: number; center_x?: number | null; center_y?: number | null }>;
|
|
pipes: PlanPipe[];
|
|
structures: PlanStructure[];
|
|
types: AttachType[];
|
|
};
|
|
cross_enforce?: SaveCrossInput & { grid?: string | null };
|
|
cross_refresh?: { detail: SectionDetailResponse; inputs: RefreshInputs; grid?: string | null };
|
|
}
|
|
|
|
/** 등록부 밖 화면 길 id(`available` 에 안 넣음 · 파이썬 `PAGE_IDS` 와 같음). */
|
|
export const PAGE_IDS = ["cross.refresh"];
|
|
|
|
let defined = "";
|
|
|
|
/** 서버 정의로 구조물 표를 갈아 끼움 — 같은 정의면 그대로(늘 떠 있는 Node · 요청마다 입구가 부름). */
|
|
export function useDefinition(definition: CatalogDefinition | null | undefined): void {
|
|
if (!definition) return;
|
|
const key = JSON.stringify(definition);
|
|
if (key === defined) return;
|
|
useCatalogDefinition(definition);
|
|
defined = key;
|
|
}
|
|
|
|
const SHAPE_FIELDS = [...SHAPE_KEYS, "blank"];
|
|
|
|
export function crossRunners(inputs: CrossInputs): Record<string, () => unknown> {
|
|
return {
|
|
"facility.attach": () => {
|
|
const attach = inputs.attach;
|
|
if (!attach) throw new Error("형상 얹기 입력(attach) 없음");
|
|
const sections = attach.sections.map((section) => ({ ...section })) as Array<
|
|
Record<string, unknown>
|
|
>;
|
|
const misses = attachShapes(sections, attach.pipes, attach.structures, attach.types);
|
|
const shapes = sections.map((section) =>
|
|
Object.fromEntries(
|
|
SHAPE_FIELDS.filter((key) => key in section).map((k) => [k, section[k]]),
|
|
),
|
|
);
|
|
return { shapes, misses };
|
|
},
|
|
"cross.enforce": () => {
|
|
const input = inputs.cross_enforce;
|
|
if (!input) throw new Error("횡단 한 바퀴 입력(cross_enforce) 없음");
|
|
return enforceSavedCross(input, () => (input.grid ? gridAt(input.grid) : null));
|
|
},
|
|
"cross.refresh": () => {
|
|
const job = inputs.cross_refresh;
|
|
if (!job) throw new Error("횡단 다시 셈 입력(cross_refresh) 없음");
|
|
const { detail } = job;
|
|
const before = detail.cross_sections.map((section) => section.samples);
|
|
refreshDesigns(detail, job.inputs, job.grid ? gridAt(job.grid) : null);
|
|
return {
|
|
design_profiles: detail.longitudinal.design_profiles,
|
|
sections: detail.cross_sections.map((section, index) => ({
|
|
design: section.design,
|
|
...(section.samples !== before[index] ? { samples: section.samples } : {}),
|
|
})),
|
|
rows: structureAreaRows(detail.cross_sections),
|
|
};
|
|
},
|
|
};
|
|
}
|