/* ============================================================================= * B05_Profile_Profile_CurveLaw.ts * 종단곡선 초기값 직선 · 법정 미달 판정(432 · 요구 262). * * 짝: 파이썬 `B05_Profile_Engine_Grade_CurveLaw.py`(`flat_bends` · `straighten_held` · * `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) 꼭짓점 묶음을 앞뒤 경계(진짜 꺾임 · 시종점)를 * 잇는 직선 위로(438 · 종단 직선화 단추와 같은 규칙) — 직선 판정(426)이라 곡선 없음 · 되풀이는 * `passThrough`(`B05_Profile_Grade_Ground.ts`). * - 법정 최소 R · L 미달은 막지 않고 경고 글만 · 비포장 대수차 5 % 이하(생략 가능)는 뺌. * ========================================================================== */ import type { AlignmentCurve, 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; /** 법정 미달 판정 오차(m) — R = L/Δ 를 반올림 높이로 되셈한 L 19.99998 거짓 미달 막음(271-1) · 파이썬 짝. */ export const SHORTFALL_TOLERANCE_M = 1e-3; /** 안쪽 꼭짓점 중 기본 L 기준 R > 500 m(|대수차| < L/500) 인 것 — 파이썬 `flat_bends` · 시종점 false. */ export function flatBends( baseS: ReadonlyArray, baseZ: ReadonlyArray, curveLengthM: number | null | undefined, ): boolean[] { const flat = baseS.map(() => false); if (!curveLengthM || baseS.length < 3) return flat; const limit = curveLengthM / INITIAL_STRAIGHT_RADIUS_M; const grade = (index: number): number => { const span = baseS[index + 1] - baseS[index]; return (baseZ[index + 1] - baseZ[index]) / (span > 0 ? span : 1); }; for (let index = 1; index < baseS.length - 1; index += 1) { flat[index] = Math.abs(grade(index) - grade(index - 1)) < limit; } return flat; } /** `held` 꼭짓점 묶음마다 앞뒤 경계(held 아닌 꼭짓점)를 잇는 직선 위로 — 파이썬 `straighten_held`. */ export function straightenHeld( baseS: ReadonlyArray, baseZ: ReadonlyArray, held: ReadonlyArray, ): number[] { const z = [...baseZ]; let index = 1; while (index < z.length - 1) { if (!held[index]) { index += 1; continue; } let end = index; while (end + 1 < z.length - 1 && held[end + 1]) end += 1; const [a, b] = [index - 1, end + 1]; for (let k = index; k < b; k += 1) { z[k] = z[a] + ((z[b] - z[a]) * (baseS[k] - baseS[a])) / (baseS[b] - baseS[a]); } index = b; } return z; } /** 법정 최소 R · L 을 못 채운 종단곡선 경고 글 — 파이썬 `vertical_curve_shortfalls` 와 같은 글. */ export function verticalCurveShortfalls( curves: ReadonlyArray, 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 - SHORTFALL_TOLERANCE_M || curve.l_m < minL - SHORTFALL_TOLERANCE_M, ) .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 미달`, ); }