/* ============================================================================= * B05_Profile_UI_Profile_Pivot.ts * 종단 ▲▼ 축 찾기(355-1 · 368-1 · 요구 235-15 · 240-20) — 누른 점에서 좌우로 같은 * 기울기 마디를 건너 다음 꺾임(R 있는 점 · 없으면 시점 · 종점)을 축으로 삼는다. 사이 * 측점은 새 직선(축 → 누른 점 → 축) 위로 따라 돈다(`adjustStation`). 직선 안 점 · * 꼭짓점 아닌 자리(추가 측점)도 같은 규칙 — 누른 점이 새 꺾임(기본 L 곡선)이 된다. * * 처음 계획선은 마디마다 꺾여 축 = 누른 점 ±1 → 사이 측점 없음(옛 동작 그대로). * `B05_Profile_UI_Profile_Alignment` 를 타입으로만 읽는다(순환 import 없음). * ========================================================================== */ import type { ProfileAlignment } from "./B05_Profile_UI_Profile_Alignment"; import { straightAt } from "./B05_Profile_Profile_Straight"; const SAME_STATION_M = 1e-6; export interface PivotAxes { fromM: number; fromZ: number; toM: number; toZ: number; /** 두 축 사이(끝 · 누른 점 제외) 변화점 chainage. */ inner: number[]; } /** 누른 점의 두 축과 사이 변화점 — 시종점 · 계획선 밖이면 null. */ export function pivotAxes(alignment: ProfileAlignment, chainageM: number): PivotAxes | null { const { pvi } = alignment; const index = pvi.findIndex((node) => Math.abs(node.chainage_m - chainageM) < SAME_STATION_M); // 꼭짓점 아닌 자리 = 그 점을 품은 마디 하나가 좌우 마디 const right = index >= 0 ? index : pvi.findIndex((node) => node.chainage_m > chainageM); if (right <= 0 || (index >= 0 && index >= pvi.length - 1)) return null; const [leftSeg, rightSeg] = index >= 0 ? [index - 1, index] : [right - 1, right - 1]; // 이웃끼리 — 꼭짓점이 직선 위(높이 기준 · 426)면 건넘 let from = leftSeg; while (from > 0 && straightAt(pvi, from)) from -= 1; let to = rightSeg + 1; while (to < pvi.length - 1 && straightAt(pvi, to)) to += 1; return { fromM: pvi[from].chainage_m, fromZ: pvi[from].elevation_m, toM: pvi[to].chainage_m, toZ: pvi[to].elevation_m, inner: pvi .slice(from + 1, to) .map((node) => node.chainage_m) .filter((chainage) => Math.abs(chainage - chainageM) >= SAME_STATION_M), }; }