/* ============================================================================= * B05_Profile_Profile_Straight.ts * 종단 「직선인가」 판정 한 벌(426 · 분석 424 6장 · 10장) — 꼭짓점이 이웃 둘을 잇는 선에서 * `STRAIGHT_HEIGHT_M` 안이면 직선(꺾임 아님). * * 짝: `B05_Profile_Engine_Grade_Alignment.py` `is_straight_bend` · `STRAIGHT_HEIGHT_M` — * 거울 시험 `test_426_profile_straight.py`. * * 기울기 정확 비교(1e-9 · 1e-6)는 꼭짓점 높이를 소수 여섯째 자리로 끊은 값(±5e-7 m)이 측점 * 간격 1 m 안(구조물 측점)에서 기울기 차로 커져 직선을 꺾임으로 읽었다 — 높이로 잰다. * 부르는 곳: 곡선 넣기(`buildCurves`) · ▲▼ 축(`pivotAxes`) · 직선 찾기(`detectStraightRun`) · * 편집 정리(`tidyEdits`) · 편집 측점 수. 표의 대수차 빈칸은 안전망으로 따로 둔다. * ========================================================================== */ import type { AlignmentNode, ProfileAlignment } from "./B05_Profile_UI_Profile_Alignment"; /** 직선으로 볼 꼭짓점 높이 어긋남(m) — 파이썬 `STRAIGHT_HEIGHT_M` 과 같은 값. */ export const STRAIGHT_HEIGHT_M = 1e-5; /** 대수차 · 좌우 마디 길이로 본 직선 — 꼭짓점이 이웃 잇는 선에서 떨어진 높이 = |Δ|·a·b/(a+b). */ export function isStraightBend(delta: number, spanLeft: number, spanRight: number): boolean { const span = spanLeft + spanRight; if (!(spanLeft > 0 && spanRight > 0)) return true; return (Math.abs(delta) * spanLeft * spanRight) / span < STRAIGHT_HEIGHT_M; } /** 세 꼭짓점의 가운데가 직선 위인가. */ export function isStraightNode( prev: AlignmentNode, node: AlignmentNode, next: AlignmentNode, ): boolean { const spanLeft = node.chainage_m - prev.chainage_m; const spanRight = next.chainage_m - node.chainage_m; if (!(spanLeft > 0 && spanRight > 0)) return true; const delta = (next.elevation_m - node.elevation_m) / spanRight - (node.elevation_m - prev.elevation_m) / spanLeft; return isStraightBend(delta, spanLeft, spanRight); } /** index 번째 꼭짓점이 직선 위인가 — 시종점은 꺾임으로 본다(false). */ export function straightAt(pvi: ReadonlyArray, index: number): boolean { if (index <= 0 || index >= pvi.length - 1) return false; return isStraightNode(pvi[index - 1], pvi[index], pvi[index + 1]); } /** 「편집 측점 N개」 — 높이를 고친 꼭짓점 중 실제로 꺾인 것(시종점은 오프셋이 있으면). */ export function bentEditCount(alignment: ProfileAlignment): number { const last = alignment.pvi.length - 1; return alignment.pvi.filter((node, index) => { if (node.source !== "user") return false; if (index === 0 || index === last) return true; return !straightAt(alignment.pvi, index); }).length; }