108 lines
4.8 KiB
TypeScript
108 lines
4.8 KiB
TypeScript
/* =============================================================================
|
|
* B05_Profile/logic/B05_Profile_Sections_Generate.ts
|
|
* 측점 생성 한 벌(PLAN 511-B B-2) — 노선 + 관 · 구조물 · 추가 측점 + DTM 격자 → 서버 `generate_sections`
|
|
* 꼴(종단 표본 · 측점 · 횡단 표본). 서버 측점 생성 길(`B05_Profile_Engine_Sections_Node.py` → Node
|
|
* `stations.sections`)이 부름 — 화면 묶음과 같은 측점(`buildStations`) · 같은 지반(`sectionsGround`).
|
|
*
|
|
* 측점 = `buildStations`(열쇠 · 종류 · 반경 · 확폭) · 지반 · 틀 · 상단측 = `sectionsGround` · 이름 =
|
|
* `plantedStations`(카드 이름) · 칸 차례 · 자릿수 = 파이썬 `generate_sections` 그대로 + 측점 열쇠 `key`.
|
|
* 머리(schema_version · source · coordinate_reference · cad_exchange · options)는 파이썬이 붙임.
|
|
* 순수 — 화면 · fetch · 세션 없음.
|
|
* ========================================================================== */
|
|
|
|
import type { DtmGrid } from "../../common_util/common_util_surface_sampler";
|
|
import { stationsAlong } from "./B05_Profile_UI_RouteEdit_Frame";
|
|
import { formatStation } from "../../B06_Section/logic/B06_Section_Station_Swap";
|
|
import { stationIdOf } from "./B05_Profile_Pipeline_Grade";
|
|
import {
|
|
buildStations,
|
|
lineLength,
|
|
plantedStations,
|
|
SECTION_INCLUDE_ENDPOINT,
|
|
SECTION_STATION_INTERVAL_M,
|
|
type StationsOptions,
|
|
} from "./B05_Profile_Sections_Stations";
|
|
import { sectionsGround, type GroundOptions } from "./B05_Profile_Sections_Ground";
|
|
import type { PlanPipe, PlanStructure } from "../../common_util/common_util_structure_station";
|
|
import type { DesignCurve, Vertex } from "../../A00_Common/design/logic/design_pipeline_types";
|
|
|
|
export type GenerateOptions = Omit<StationsOptions, "registry"> & GroundOptions;
|
|
|
|
const KIND: Record<string, string> = {
|
|
grid: "regular",
|
|
curve: "curve",
|
|
pipe: "irregular",
|
|
structure: "irregular",
|
|
};
|
|
|
|
/** 서버 `generate_sections` 꼴 몸통 — 종단(길이 · 표본 · 측점) · 횡단 · 요약 · 도면 기준고. */
|
|
export function generateSections(
|
|
line: ReadonlyArray<Vertex>,
|
|
curves: ReadonlyArray<DesignCurve>,
|
|
pipes: ReadonlyArray<PlanPipe>,
|
|
structures: ReadonlyArray<PlanStructure>,
|
|
grid: DtmGrid,
|
|
options: GenerateOptions,
|
|
) {
|
|
const stations = buildStations(line, curves, pipes, structures, options);
|
|
if (stations.length < 2) throw new Error("경로는 최소 2개의 (x, y, z) 좌표로 구성되어야 합니다.");
|
|
const gridM = stationsAlong(
|
|
line,
|
|
options.intervalM ?? SECTION_STATION_INTERVAL_M,
|
|
options.includeEndpoint ?? SECTION_INCLUDE_ENDPOINT,
|
|
);
|
|
const planted = plantedStations(gridM, lineLength(line), pipes, structures, options);
|
|
const chainages = stations.map((station) => station.chainageM);
|
|
const ground = sectionsGround(line, chainages, grid, options);
|
|
const crossSections = stations.map((station, index) => {
|
|
const at = ground.stations[index];
|
|
const mark = [...planted].find(([m]) => Math.abs(m - station.chainageM) < 1e-6)?.[1];
|
|
const kind = index === 0 ? "bp" : station.kind === "end" ? "ep" : KIND[station.kind];
|
|
const head: Record<string, unknown> = {
|
|
station_id: stationIdOf(station.chainageM),
|
|
chainage_m: at.chainage_m,
|
|
label: formatStation(station.chainageM) + (kind === "curve" ? " (MC)" : ""),
|
|
kind,
|
|
center_x: at.center_x,
|
|
center_y: at.center_y,
|
|
center_z: at.center_z,
|
|
azimuth_deg: station.azimuthDeg,
|
|
plan_radius_m: station.planRadiusM,
|
|
curve_outer_side: station.curveOuterSide,
|
|
curve_widening_m: station.curveWideningM,
|
|
uphill_side: at.uphill_side,
|
|
frame: {
|
|
origin: { x: at.center_x, y: at.center_y, z: at.center_z },
|
|
tangent_xy: at.tangent_xy,
|
|
left_xy: at.left_xy,
|
|
up_xyz: [0.0, 0.0, 1.0],
|
|
},
|
|
};
|
|
if (mark) {
|
|
head.structure = mark.title;
|
|
if (gridM.some((m) => Math.abs(m - station.chainageM) < 1e-6)) head.grid = true;
|
|
}
|
|
head.key = station.key;
|
|
return { head, samples: at.samples };
|
|
});
|
|
const z = ground.samples.flatMap((sample) => (sample.z === null ? [] : [sample.z]));
|
|
return {
|
|
longitudinal: {
|
|
length_m: ground.length_m,
|
|
samples: ground.samples,
|
|
stations: crossSections.map(({ head }) => head),
|
|
},
|
|
cross_sections: crossSections.map(({ head, samples }) => ({ ...head, samples })),
|
|
summary: {
|
|
station_count: stations.length,
|
|
cross_sample_count: crossSections.reduce((sum, { samples }) => sum + samples.length, 0),
|
|
invalid_longitudinal_samples: ground.samples.filter((sample) => !sample.valid).length,
|
|
invalid_cross_samples: crossSections.reduce(
|
|
(sum, { samples }) => sum + samples.filter((sample) => !sample.valid).length,
|
|
0,
|
|
),
|
|
},
|
|
datum: z.length ? Math.floor(Math.min(...z) / 10) * 10 : null,
|
|
};
|
|
}
|