Files
Aislo/B05_Profile/logic/B05_Profile_Profile_Straight.ts
T
eomsangdonandClaude Opus 5.5 309415a481 fix(B05 종단): 직선 판정 한 벌(꼭짓점 높이 1e-5 m) · 끝점 꼭짓점 · 기울이기 누름 쌓임 · 편집 측점 수 (426)
- 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
2026-10-18 15:51:08 +09:00

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;
}