- 옮김: B04 2 · B05 23 · B06 11 · A00_Common/design 13 (9,299 줄) · 이미 logic/ 에 있던 16 파일 그대로 - 안 옮김: 묶음 입구(package.json build:*) · 파이썬 · 순수지만 계산 아닌 8 (보기 결선 · 콜백 · API 타입 · 갈아끼움 알림판) - import 266 줄(142 파일) · 시험 · 주석 · 짝 표시 문자열 경로 고침 · B05 · B06 CLAUDE.md 짝 줄 logic/ - 시험 도우미 새 경로: 가짜 require 열쇠 · B05()/B06() 부름 · 형제 import 자리 · tsc 묶어 돌리는 시험 · 정규식 · rglob - design_view_cross 줄 한도 시험 685 → 688 (긴 import 경로 줄바꿈 3줄) - 확인: typecheck 0 · 서버 묶음 넷(corridor · server-calc · b07-extent · b07-note) 다시 지어 코드 같음(//#region 경로 주석만 다름) · 관련 시험 270 파일 — 남은 실패 19 는 옮기기 전과 같음 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
62 lines
2.7 KiB
TypeScript
62 lines
2.7 KiB
TypeScript
/* =============================================================================
|
||
* B05_Profile_UI_Profile_Guard.ts
|
||
* 종단도가 편집 · 경고에 쓰는 **셈만** — 계획선 샘플 보간, 평면 최소곡선반지름 경고
|
||
* (구조물 측점 맞추기는 측점 한 벌 `oneStationList` 로 갈음 · 272).
|
||
*
|
||
* 종단도 한 벌(`A00_Common/design/design_profile.ts`)이 700줄을 넘겨 떼어낸 조각이다
|
||
* (계획서 24-2c). 본문 로직과 수치는 그대로이고 화면 상태를 인자로 받는다.
|
||
* ========================================================================== */
|
||
|
||
import type { SectionDetailResponse } from "../../B06_Section/B06_Section_Api_Fetch";
|
||
import type { ProfileAlignment } from "./B05_Profile_UI_Profile_Alignment";
|
||
|
||
/** 계획선 샘플에서 chainage로 계획고·지반고를 되짚는 보간기(최소고 판정 입력). */
|
||
export function sampleAt(
|
||
alignment: ProfileAlignment | null,
|
||
chainageM: number,
|
||
field: "elevation_m" | "ground_elevation_m",
|
||
): number | null {
|
||
const samples = alignment?.samples;
|
||
if (!samples?.length) return null;
|
||
if (chainageM <= samples[0].chainage_m) return samples[0][field];
|
||
for (let i = 1; i < samples.length; i += 1) {
|
||
if (chainageM <= samples[i].chainage_m) {
|
||
const span = samples[i].chainage_m - samples[i - 1].chainage_m;
|
||
const t = span <= 1e-12 ? 0 : (chainageM - samples[i - 1].chainage_m) / span;
|
||
return samples[i - 1][field] + (samples[i][field] - samples[i - 1][field]) * t;
|
||
}
|
||
}
|
||
return samples[samples.length - 1][field];
|
||
}
|
||
|
||
/**
|
||
* 법정 평면 최소곡선반지름을 밑도는 측점을 센다(별표2 Ⅰ.2.다.(1), 2026-09-06).
|
||
* 반경은 횡단 측점마다 실려 오고(`plan_radius_m`), 하한은 계획선 정책이 들고 있다.
|
||
* **경고만** 낸다 — 노선을 고칠지는 사용자 판단이다.
|
||
*/
|
||
export function planCurveWarning(
|
||
alignment: ProfileAlignment | null,
|
||
detail: SectionDetailResponse | null,
|
||
): {
|
||
count: number;
|
||
worstRadiusM: number;
|
||
limitM: number;
|
||
hairpin: number;
|
||
} | null {
|
||
const limit = alignment?.policy.min_plan_radius_m ?? 0;
|
||
const sections = detail?.cross_sections ?? [];
|
||
if (!limit || !sections.length) return null;
|
||
const hairpinLimit = alignment?.policy.hairpin_min_radius_m ?? 0;
|
||
let count = 0;
|
||
let hairpin = 0;
|
||
let worst = Number.POSITIVE_INFINITY;
|
||
sections.forEach((section) => {
|
||
const radius = section.plan_radius_m;
|
||
if (typeof radius !== "number" || !Number.isFinite(radius) || radius >= limit) return;
|
||
count += 1;
|
||
worst = Math.min(worst, radius);
|
||
if (hairpinLimit > 0 && radius < hairpinLimit) hairpin += 1;
|
||
});
|
||
return count ? { count, worstRadiusM: worst, limitM: limit, hairpin } : null;
|
||
}
|