/* ============================================================================= * 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 & GroundOptions; const KIND: Record = { grid: "regular", curve: "curve", pipe: "irregular", structure: "irregular", }; /** 서버 `generate_sections` 꼴 몸통 — 종단(길이 · 표본 · 측점) · 횡단 · 요약 · 도면 기준고. */ export function generateSections( line: ReadonlyArray, curves: ReadonlyArray, pipes: ReadonlyArray, structures: ReadonlyArray, 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 = { 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, }; }