/* ============================================================================= * D03_PlanAnalysis_Expect.ts * D03 예상 횡단(PLAN 544-1 · 요구 363-1) — 측점 지반 → 표준 횡단 기본값 설계선 · 절 · 성토 ㎡. * 셈 = 공용 `computeCrossDesign`(① 짝 `compute_cross_design` · 거울 `test_b06_cross_design_mirror.py`) * · 기본 = B 초기 설계와 같음(리핑암 · 산쪽 절토 · 일반 측구 · 암 경계 = 서버 기본) · 계획고 = 측점 중심 지반고 * · 미교차 = `extendUnclosed`(격자 있을 때) · 곡선 확폭 없음 · 보기만(저장 없음). * 3D = 같은 단면을 B05 `buildCorridor` 가 읽는 측점 꼴로(`expectSections`). * 시험 `resources/tester/test_544_1_plan_expect.py`. * ========================================================================== */ import { computeCrossDesign, type CrossDesignOptions, type CrossDesignResult, type StandardCrossSectionSpec, } from "../../common_util/common_util_cross_design"; import type { DtmGrid } from "../../common_util/common_util_surface_sampler"; import { extendUnclosed, type CrossGroundPoint, type StationGround, } from "../../B05_Profile/logic/B05_Profile_Sections_Ground"; /** 서버 `sections/context` 의 표준 횡단(config) · 암 경계 기본 오프셋. */ export interface ExpectStandard { standard: StandardCrossSectionSpec; rockBoundaryM: number | null; } export interface ExpectCross { design: CrossDesignResult; /** 설계에 쓴 지반 표본(미교차로 넓혔으면 넓힌 것). */ samples: CrossGroundPoint[]; } /** B 초기 설계 기본 — 리핑암 · 산쪽 절토(모르면 좌) · 일반 측구. */ export function expectOptions( uphill: "left" | "right" | null, std: ExpectStandard, ): CrossDesignOptions { return { groundType: "ripping_rock", sectionMode: `${uphill ?? "left"}_cut`, ditchType: "standard", standard: std.standard, rockBoundaryOffsetM: std.rockBoundaryM, }; } /** 측점 하나 예상 단면 — 지반고 · 표본이 없으면 null. */ export function expectCross( station: StationGround, std: ExpectStandard, grid?: DtmGrid | null, ): ExpectCross | null { const elevation = station.center_z; if (elevation === null) return null; const options = expectOptions(station.uphill_side, std); try { let samples = station.samples; let design = computeCrossDesign(samples, elevation, options); const wide = design.slope_unclosed && grid ? extendUnclosed( samples, [station.center_x, station.center_y], station.left_xy, (next) => computeCrossDesign(next, elevation, options), grid, ) : null; if (wide) { samples = wide.samples as CrossGroundPoint[]; design = computeCrossDesign(samples, elevation, options); } return { design, samples }; } catch { return null; // 표본 부족 — B 와 같은 태도(그 측점 건너뜀) } } /** B06 `CrossSection` 중 `buildCorridor` 가 읽는 칸(B06 형 파일은 별칭 import 라 여기 안 끌어옴). */ export interface ExpectSection { station_id: string; chainage_m: number; label: string; kind: "regular"; center_z: number | null; azimuth_deg: null; center_x: number; center_y: number; uphill_side: "left" | "right" | null; frame: { left_xy: [number, number] }; samples: CrossGroundPoint[]; design: CrossDesignResult; } /** 3D 코리도 입력 — 측점마다 예상 단면(못 세운 측점 뺌) · 누가 = 연도 노선 위 누가. */ export function expectSections( stations: StationGround[], std: ExpectStandard, grid?: DtmGrid | null, ): ExpectSection[] { return stations.flatMap((station) => { const cross = expectCross(station, std, grid); if (!cross) return []; return [ { station_id: String(station.chainage_m), chainage_m: station.chainage_m, label: "", kind: "regular", center_z: station.center_z, azimuth_deg: null, center_x: station.center_x, center_y: station.center_y, uphill_side: station.uphill_side, frame: { left_xy: station.left_xy }, samples: cross.samples, design: cross.design, }, ]; }); }