Files
Aislo/B05_Profile/logic/B05_Profile_Profile_CurveLaw.ts
T
eomsangdonandClaude Opus 5.5 0714e9653e feat(B05 종단): 종단곡선 기본 L 20 m · 초기값 R 500 m 넘는 꼭짓점 곡선 없음 · 법정 R · L 미달 경고 (432-1)
- config 기본 L 20 · 초기값 직선 문턱 500 m ↔ TS `logic/B05_Profile_Profile_CurveLaw.ts` 상수 짝
- 초기값(1차 지반 종단 · 추천 [적용])만 정책 `no_curve_keys` 로 얼림 · 손 편집 · R 이면 기본 곡선
- 법정 종단곡선 최소 R · L 미달 = 경고 글(파이썬 · TS 같은 글) + 종단 요약줄 「종단곡선 미달」
- 노선 [저장]은 옛 정책(L · 열쇠) 이어받음 · 바뀐 자리 새 꼭짓점만 새 지반 열쇠(`carry_policy` ↔ `carryNoCurveKeys`)
- 시험 test_432_1 + 헬퍼 스텁 넷

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XWh5wNNycMkbwMpACz1rte
2026-10-18 16:20:03 +09:00

71 lines
3.3 KiB
TypeScript

/* =============================================================================
* B05_Profile_Profile_CurveLaw.ts
* 종단곡선 초기값 직선 · 법정 미달 판정(432 · 요구 262).
*
* 짝: 파이썬 `B05_Profile_Engine_Grade_CurveLaw.py`(`initial_no_curve_keys` ·
* `vertical_curve_shortfalls`) · config `FOREST_ROAD_PROFILE_ALIGNMENT` 의 `default_curve_length_m` ·
* `initial_straight_radius_m` — 거울 시험 `test_432_1_curve_law.py`.
*
* - 초기값에서만 기본 L 기준 R > 500 m(|대수차| < L/500) 꼭짓점은 곡선 없음 — 정책 `no_curve_keys`
* 로 얼려 두고, 사용자가 그 꼭짓점을 고치거나(source "user") R 을 넣으면 다시 기본 곡선.
* - 법정 최소 R · L 미달은 막지 않고 경고 글만 · 비포장 대수차 5 % 이하(생략 가능)는 뺌.
* ========================================================================== */
import type {
AlignmentCurve,
AlignmentNode,
AlignmentPolicy,
} from "./B05_Profile_UI_Profile_Alignment";
/** 기본 종단곡선 길이 L(m) — config `default_curve_length_m` 짝(새 정책에 담겨 옴 · 옛 저장분은 저장값). */
export const DEFAULT_CURVE_LENGTH_M = 20;
/** 초기값 직선 문턱 R(m) — config `initial_straight_radius_m` 짝. */
export const INITIAL_STRAIGHT_RADIUS_M = 500;
/** 안쪽 꼭짓점 중 기본 L 기준 R > 500 m 인 것의 열쇠(소수 3자리) — 파이썬 `initial_no_curve_keys`. */
export function initialNoCurveKeys(
nodes: ReadonlyArray<AlignmentNode>,
curveLengthM: number | null | undefined,
): string[] {
if (!curveLengthM || nodes.length < 3) return [];
const limit = curveLengthM / INITIAL_STRAIGHT_RADIUS_M;
const keys: string[] = [];
for (let index = 1; index < nodes.length - 1; index += 1) {
const [a, b, c] = [nodes[index - 1], nodes[index], nodes[index + 1]];
const spanIn = b.chainage_m - a.chainage_m;
const spanOut = c.chainage_m - b.chainage_m;
const gradeIn = (b.elevation_m - a.elevation_m) / (spanIn > 0 ? spanIn : 1);
const gradeOut = (c.elevation_m - b.elevation_m) / (spanOut > 0 ? spanOut : 1);
if (Math.abs(gradeOut - gradeIn) < limit) keys.push(b.chainage_m.toFixed(3));
}
return keys;
}
/** 초기값 직선 꼭짓점이라 곡선을 안 넣는가 — 사용자가 고친 꼭짓점 · R 지정이면 넣음. */
export function skipsInitialCurve(
policy: AlignmentPolicy,
key: string,
node: AlignmentNode & { source?: string },
hasRequested: boolean,
): boolean {
return !hasRequested && node.source !== "user" && (policy.no_curve_keys ?? []).includes(key);
}
/** 법정 최소 R · L 을 못 채운 종단곡선 경고 글 — 파이썬 `vertical_curve_shortfalls` 와 같은 글. */
export function verticalCurveShortfalls(
curves: ReadonlyArray<AlignmentCurve>,
policy: AlignmentPolicy,
): string[] {
const minR = policy.min_vertical_radius_m ?? 0;
const minL = policy.min_curve_length_m ?? 0;
if (minR <= 0 && minL <= 0) return [];
return curves
.filter((curve) => !curve.omitted && !curve.skip_allowed)
.filter((curve) => curve.r_m < minR - 1e-6 || curve.l_m < minL - 1e-6)
.map(
(curve) =>
`${curve.chainage_m.toFixed(1)}m: 종단곡선 R ${curve.r_m.toFixed(0)}m · L ${curve.l_m.toFixed(1)}m — ` +
`법정 최소 R ${minR.toFixed(0)}m · L ${minL.toFixed(0)}m 미달`,
);
}