- TS `logic/B05_Profile_Profile_Straight.ts` ↔ 파이썬 `is_straight_bend` 짝 · 곡선 넣기 · ▲▼ 축 · 직선 찾기 - 적용 한 곳 `tidyEdits` — 직선 위 기억 R · 0 오프셋 지움(모양 그대로) - 직선화 · 자유지정 끝이 꼭짓점 아니면 지금 높이로 세움 · 고른 직선 기울이기 = 지금 경첩 높이 + 걸음 - 「편집 측점 N개」 = 꺾인 편집 꼭짓점 수 · 시험 test_426 + 헬퍼 스텁 넷 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWh5wNNycMkbwMpACz1rte
57 lines
2.8 KiB
TypeScript
57 lines
2.8 KiB
TypeScript
/* =============================================================================
|
|
* 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<AlignmentNode>, 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;
|
|
}
|