fix(계산묶음): 349-0n 노선 초안 미리보기 = [저장] 정본 — 초안 상세에 넓힌 지반 표본 · cross 단계에 서버 [확정] 보정(포장 구간 · 세월교 노면 하강 · 사토장)

- 남은 차이의 주인 = 초안 상세 지반 표본: 설계는 넓힌 표본으로 셈했는데 상세엔 20 m 표본을 실어 관 측점 구조물 면적이 잘린 지반으로 닫힘(검증_sub2 805 측점 성토 27.5 대 63.4 ㎡) — cross 단계 결과에 넓힌 표본을 싣고 초안 상세가 그 표본을 씀(서버도 넓힌 표본을 상세에 남김)
- 초안 상세 제원 누가거리 = 새 측점(서버 부착과 같음)
- 새 짝 logic/B06_Section_Cross_Enforce — 포장 구간 · 물넘이 기울임 · 세월교 하강 = 새 노선 관 · 구조물 자리로 판정(`pavement_ranges` · `tilt_ranges` · `ford_surface_drops` 짝) · 사토장 맨 뒤 기준 측점 하나 · 용량 이분법(`enforce_spoil_fills` 짝) · 파이썬 세 파일 머리에 짝 표시
- 검증_sub2 실측(서버 체인 실제로 돌리고 일부러 실패시켜 되살림): 25+0 부근 꺾임점 끌기 미리보기 -12,816.9 대 정본 -12,817.1 · 세월교 월류 0.22 · 0.29 넣고 -12,788.7 대 -12,789.0 · 끌기 한 번 103 ms
- 시험: test_349_0n_cross_enforce_mirror 5 · test_349_0j 표본 1 더함 · 관련 114 통과 · typecheck 0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QfHKQs7J3hQELKMWQMUJEy
This commit is contained in:
2026-10-16 16:36:09 +09:00
co-authored by Claude Opus 5.5
parent f38f6d7d70
commit ffbda88edf
10 changed files with 660 additions and 16 deletions
@@ -88,6 +88,19 @@ const kindOf = (station: PipelineStation): SectionStation["kind"] =>
? "regular"
: "irregular";
/** 제원의 누가거리 = 새 측점(서버 부착은 따라간 관 · 구조물 자리 = 그 측점) — 옛 값이면 구조물 면적이 관 자리를 잘못 잡음(349-0n). */
function atChainage(own: SectionSpecs, chainageM: number): SectionSpecs {
const out: Record<string, unknown> = { ...own };
for (const [name, spec] of Object.entries(own))
if (
spec &&
typeof spec === "object" &&
typeof (spec as { chainage_m?: unknown }).chainage_m === "number"
)
out[name] = { ...spec, chainage_m: chainageM };
return out as SectionSpecs;
}
/** 묶음 출력 → 상세. 저장 상세의 종단 머리(이름 · 계획선 묶음)는 그대로 · 측점 · 지반 · 횡단은 새 노선. */
export function draftSectionDetail(
saved: SectionDetailResponse,
@@ -98,7 +111,7 @@ export function draftSectionDetail(
intervalM: number,
): SectionDetailResponse {
const sections: CrossSection[] = ground.stations.map((station) => {
const own = specs.get(station.key) ?? {};
const own = atChainage(specs.get(station.key) ?? {}, station.chainageM);
const center = station.ground?.centerZ ?? null;
const head: SectionStation = {
station_id: stationIdOf(station.chainageM),
@@ -126,12 +139,15 @@ export function draftSectionDetail(
sample(0, center),
...(station.ground?.left ?? []).map((s) => sample(s.offsetM, s.z)),
];
const design = cross?.byKey[station.key]?.detail as CrossSection["design"] | undefined;
const entry = cross?.byKey[station.key] as
{ detail?: unknown; samples?: unknown[] } | undefined;
const design = entry?.detail as CrossSection["design"] | undefined;
// 설계는 사본 — 구조물 면적 얹기(`applyStructureAreaRows`)가 cross 단계 기억을 안 고침(349-0j)
return {
...head,
...own,
samples,
// 넓힌 표본(서버 [저장]도 넓힌 표본을 상세에 남김 — 구조물 면적이 그 지반으로 닫힘 · 349-0n)
samples: (entry?.samples as CrossSection["samples"] | undefined) ?? samples,
...(design ? { design: { ...design } } : {}),
preview: true,
};