- 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
71 lines
3.3 KiB
TypeScript
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 미달`,
|
|
);
|
|
}
|