feat(B05/B06): 옛 계획고 유토곡선 노출 차단 + 사면 미교차 측점 경고

① 저장된 횡단이 지금 계획선과 어긋나면(재계산 예약 상태) 유토곡선을 그리지 않고
「횡단을 지금 계획선에 맞춰 다시 계산하는 중입니다」만 띄움. 옛 계획고로 만든 면적이
2초간 보였다가 정본으로 갈아 끼워지던 것을 없앰(2026-09-03 사용자 확정 「새 값만
보여주기」). 용화 실측 — 65측점 중 56개가 낡음, 계획고 차 −9.36~+5.83m,
저장분 성토 42,050㎥ ↔ 정본 14,881㎥.

② 사면이 계산 반폭 끝까지 원지반과 만나지 않아 면적이 잘린 측점에 카드 경고 표기.
판정은 면적을 내는 엔진 한 곳(B06_Section_Engine_Design)에서 `slope_unclosed` 로
내려 화면·수량·도면이 같은 기준을 봄. 계산은 손대지 않음(2026-09-03 사용자 확정
「영원히 못 만나는 지형이 있을 수 있으니 경고로 대체」).

검증 — 공용 브라우저 B06 실측: 2.6초 재계산 안내만 표시(옛 값 미노출), 4.4초 정본
표시, 사면 미교차 경고 13/65측점. pytest 366 passed·17 skipped, typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-03 09:10:46 +09:00
co-authored by Claude Opus 5
parent 698f8f0505
commit 23b1e7e5b0
9 changed files with 81 additions and 10 deletions
+17 -9
View File
@@ -84,6 +84,7 @@ import {
L,
longitudinalMaxChainage,
LONG_PAD,
staleDesignChainages,
type YScaleOptions,
} from "./B06_Section_UI_Section_Common";
@@ -516,14 +517,19 @@ export function createSectionView(
),
];
const series: MassHaulSeries[] = currentConversion
? computeMassHaulSeries(
detail.longitudinal,
detail.cross_sections,
currentConversion,
currentNaturalSpoilSlope,
)
: [];
// 저장된 횡단이 지금 계획선과 어긋나면 그 면적은 **옛 계획고로 만든 값**이다. 진입
// 직후 `reconcileStaleDesigns()`가 다시 계산해 갈아 끼우므로, 그 사이 옛 값으로 곡선을
// 그리면 사용자가 옛 그림을 본다(2026-09-03 사용자 확정: 「새 값만 보여주기」).
const pendingRecalc = staleDesignChainages(detail).length > 0;
const series: MassHaulSeries[] =
currentConversion && !pendingRecalc
? computeMassHaulSeries(
detail.longitudinal,
detail.cross_sections,
currentConversion,
currentNaturalSpoilSlope,
)
: [];
// 토량 분배는 **면을 깐 곡선 하나**(= 켜 둔 첫 곡선)에만 얹는다 — 곡선마다 평형선을
// 그리면 계단이 서로 엇갈려 어느 쪽 배분인지 읽히지 않는다.
const bandedSeries = series.find((entry) => visibleSeries.has(entry.key));
@@ -588,7 +594,9 @@ export function createSectionView(
panelCount.textContent = "";
panelBody.append(createMassHaulSummary(summarySeries, haulPlan));
} else {
panelCount.textContent = L(series.length ? "B06_MassHaul_AllHidden" : "B06_MassHaul_Empty");
panelCount.textContent = pendingRecalc
? L("B06_MassHaul_Recalculating")
: L(series.length ? "B06_MassHaul_AllHidden" : "B06_MassHaul_Empty");
}
chartWrap.scrollLeft = keepScrollLeft;
// 상단 패널이 sticky라 선택 카드가 그 아래로 숨는다 — 패널 높이만큼 스크롤 여백을 잡아 준다.