Files
Aislo/common_util/common_util_berm_spans.ts
T
eomsangdonandClaude Opus 5.5 3a64f9a812 feat(472-4 ② 횡단 짝 모으기): [저장] · [확정] 횡단 설계선 · 면적 · 소단 구간을 Node 입구 한 갈래로(C1 · C2 · C4 · X3 풀림)
- Router_Design enforce 셋 · 넓힘 뒤 설계 — 파이썬은 입력 조립만 · 측점 묶음을 Node 한 번(B06_Section_Engine_Design_Node · cross.design) · Node 못 부르면 파이썬 짝
- 소단 구간 셈을 화면 파일에서 common_util_berm_spans.ts 로 · 서버도 같은 TS(cross.berm_spans) · Persist 가 다시 내보냄
- 값: 고정 자료 기준값 0 차(면적 · 계획고 · 누가토량 −17,570.7) · 설계선 점 13 곳 1e-4 m(반올림 동점 · 브레인 승인) — 기준값 시험에 판정 더함
- 시험: compute_cross_design 갈아 끼우던 시험 다섯을 묶음 자리로(까닭: [저장] 다시 셈이 묶음으로 부름) · 소단 글 시험 자리 옮김
- 등록부 · 위키 계산 목록 · B06 지침 줄

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 09:21:42 +09:00

108 lines
4.5 KiB
TypeScript

/* =============================================================================
* common_util_berm_spans.ts
* 소단 구간 — 구조물 목록(표 형상 berm)에서 구간 · 측점마다 제원(PLAN 472-4 C4 · 화면 파일
* `B06_Section_UI_Page_Persist.ts` 에서 옮김 · 그쪽이 다시 내보냄).
* 브라우저(시설 줄 · 다시 셈)와 서버 Node 입구(`design_calc_node` · 등록부 `cross.berm_spans`)가 같은 셈.
* 파이썬 짝 = `B06_Section_Engine_Structures_Wall.py` `load_berm_spans` · `berm_span_at`
* (Node 를 못 부를 때만 · 거울 시험 `test_272_berm_source.py`).
* ========================================================================== */
import {
BERM_DEFAULT_DEPTH_M,
BERM_DEFAULT_INTERVAL_M,
BERM_DEFAULT_SLOPE_DEG,
BERM_DEFAULT_WIDTH_M,
} from "./common_util_cross_berm";
import { catalogRow } from "./common_util_structure_catalog";
/** 측점 하나의 소단 제원 — 서버 payload 와 같은 이름을 쓴다(그대로 실어 보낸다). */
export interface BermSessionSpec {
width_m: number;
interval_m: number;
slope_deg: number;
/** 노면 끝 아래 깊이(m · ≥ 0 · 178-2). */
depth_m: number;
}
/**
* 사용자가 놓은 소단 **한 구간** — 종단 범위 + 제원.
*
* 사용자는 측점 하나가 아니라 **구간**에 놓는다(2026-09-07 확정: 「길이 + 기준측점 전·후」).
* 그래서 세션에는 구간 목록으로 두고, 측점별 제원은 읽는 자리에서 편다 — 구간을 측점으로
* 펴서 저장하면 나중에 「어디부터 어디까지 놓았나」를 되짚을 수 없다.
*/
export interface BermSpan extends BermSessionSpec {
start_m: number;
end_m: number;
}
/** 소단 = 표 형상 berm(323-4 · 파이썬 `load_berm_spans` 짝) — 종류 키로 안 가름. */
export const isBermType = (typeId: string): boolean => catalogRow(typeId)?.shape === "berm";
/**
* 구조물 목록에서 **소단 구간**을 뽑는다 — 사용자는 「구조물 배치」에서 놓는다
* (2026-09-07 사용자 확정: 별도 폼이 아니라 다른 옹벽·기슭막이와 같은 자리).
*
* 세션 열쇠 `berm` 은 그 결과를 담는 **사본**이다. 재계산(브라우저·서버)이 측점마다 값을
* 읽어야 하는데 구조물 목록은 비동기로 오므로, 목록이 바뀔 때마다 여기서 펴 두고 계산은
* 그 사본만 본다.
*/
export function bermSpansFromStructures(
structures: ReadonlyArray<{
type_id: string;
start_m?: number | null;
end_m?: number | null;
chainage_m?: number | null;
options?: Record<string, string | number>;
}>,
): BermSpan[] {
const spans: BermSpan[] = [];
for (const item of structures) {
if (!isBermType(item.type_id)) continue;
const anchor = item.chainage_m ?? item.start_m ?? null;
const start = item.start_m ?? anchor;
const end = item.end_m ?? anchor;
if (start === null || end === null) continue;
const options = item.options ?? {};
const number = (key: string, fallback: number): number => {
const parsed = Number(options[key]);
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
};
const width = number("width_m", BERM_DEFAULT_WIDTH_M);
const interval = number("interval_m", BERM_DEFAULT_INTERVAL_M);
if (width <= 0) continue; // 폭이 0이면 계단이 없다(간격은 성토 한 단에 안 씀 · 178-2)
// 깊이 = 노면 끝 아래 · 음수는 0(도로보다 높을 수 없음) · 빈 값은 기본 1.0
const rawDepth = Number(options.depth_m);
const depth =
options.depth_m === undefined || options.depth_m === "" || !Number.isFinite(rawDepth)
? BERM_DEFAULT_DEPTH_M
: Math.max(rawDepth, 0);
spans.push({
start_m: Math.min(start, end),
end_m: Math.max(start, end),
width_m: width,
interval_m: interval,
slope_deg: number("slope_deg", BERM_DEFAULT_SLOPE_DEG),
depth_m: depth,
});
}
return spans;
}
/** 그 측점을 덮는 소단 제원 — 없으면 null. 겹치면 먼저 놓은 것이 이긴다. */
export function bermSpecAt(spans: BermSpan[], chainageM: number): BermSessionSpec | null {
const found = spans.find(
(span) =>
chainageM >= Math.min(span.start_m, span.end_m) - 1e-6 &&
chainageM <= Math.max(span.start_m, span.end_m) + 1e-6,
);
return found
? {
width_m: found.width_m,
interval_m: found.interval_m,
slope_deg: found.slope_deg,
depth_m: found.depth_m ?? BERM_DEFAULT_DEPTH_M,
}
: null;
}