diff --git a/B05_Profile/B05_Profile_UI_Profile_Panel.ts b/B05_Profile/B05_Profile_UI_Profile_Panel.ts index 798025b4..5e25ee90 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Panel.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Panel.ts @@ -20,7 +20,7 @@ import { createProfileTableOverlay, TABLE_OVERLAY_MIN_HEIGHT, } from "./B05_Profile_UI_Profile_TableOverlay"; -import { hasLegacyAlignment, readAlignment } from "./B05_Profile_UI_Profile_Data"; +import { hasLegacyAlignment, readAlignment, toDesignProfile } from "./B05_Profile_UI_Profile_Data"; import { createProgressCircle } from "@ui/ui_template_progress"; import { showToast } from "@ui/ui_template_elements"; import { saveProfileAlignment } from "./B05_Profile_Api_Fetch"; @@ -584,7 +584,23 @@ export function createRouteProfilePanel( // 저장된 횡단 설계가 옛 계획고로 굳어 있으면 진입 즉시 한 번 맞춘다 — // 지금까지는 B06에 들어가야만 고쳐져, B05의 횡단 기준 유토곡선과 3D // 예상형상이 옛 설계선을 그대로 썼다(2026-08-23 사용자 보고 · 실측 확인). - if (hasStaleDesigns(nextDetail)) scheduleCrossPreview(); + // + // 비교 대상은 **편집이 반영된 계획선**이다. 저장분끼리 견주면, 세션에 미저장 편집이 + // 남은 채로 새로고침했을 때 「어긋남 없음」으로 나와 재계산이 예약되지 않고, 그리기 + // 쪽은 편집분 기준으로 어긋났다고 보아 유토곡선이 영영 빈 화면이 됐다 + // (2026-09-03 사용자 보고 — 편집 측점 2개가 세션에 남은 상태에서 재현). + if ( + hasStaleDesigns({ + longitudinal: { + design_profiles: alignment + ? [toDesignProfile(alignment, nextDetail.longitudinal.design_profiles?.[0])] + : nextDetail.longitudinal.design_profiles, + }, + cross_sections: nextDetail.cross_sections, + }) + ) { + scheduleCrossPreview(); + } }, /** 라이브 계획선 샘플(편집 반영분) — 3D 측점 바·코리도가 이걸 본다(2026-08-23). * 편집 전·base 없음이면 null — 호출부는 정본 design_profiles로 폴백한다. */ diff --git a/B05_Profile/B05_Profile_UI_Profile_Render.ts b/B05_Profile/B05_Profile_UI_Profile_Render.ts index 9221ddb0..13a0992d 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Render.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Render.ts @@ -326,9 +326,15 @@ export function renderProfile(ctx: ProfileRenderContext): void { // 측점별 횡단 설계(기본 프리뷰 포함)를 입력으로 쓴다 — B06과 같은 재료다. longitudinal: { length_m: longitudinal.length_m, design_profiles: designProfiles }, crossSections: detail.cross_sections, - // 저장된 횡단이 지금 계획선과 어긋나면 그 면적은 옛 계획고로 만든 값이다 — - // Panel이 재계산을 예약해 두므로 유토곡선은 그 결과만 그린다(2026-09-03 사용자 확정). - pendingRecalc: hasStaleDesigns(detail), + // 횡단이 지금 계획선과 어긋나면 그 면적은 옛 계획고로 만든 값이다 — Panel이 + // 재계산을 예약해 두므로 유토곡선은 그 결과만 그린다(2026-09-03 사용자 확정). + // 비교 대상은 **편집이 반영된 계획선**(`designProfiles`)이다. 저장분 + // (`detail.longitudinal.design_profiles`)과 견주면 편집 중에는 영원히 어긋난 것으로 + // 나와 곡선이 계속 빈 화면이 된다(2026-09-03 사용자 보고). + pendingRecalc: hasStaleDesigns({ + longitudinal: { design_profiles: designProfiles }, + cross_sections: detail.cross_sections, + }), axis: { maxChainageM: maxChainageOf(longitudinal), // 종단 그래프의 `chainageMapper`와 정확히 같은 매핑이 되도록 반 칸 들여쓰기를 diff --git a/B06_Section/B06_Section_UI_Section_Common.ts b/B06_Section/B06_Section_UI_Section_Common.ts index ad93fadb..ffa54a7a 100644 --- a/B06_Section/B06_Section_UI_Section_Common.ts +++ b/B06_Section/B06_Section_UI_Section_Common.ts @@ -107,6 +107,7 @@ interface StaleCandidate { design_elevation_m: number; geometry_preset?: string; two_stage_slope?: boolean; + surface_drop_m?: number; } | null; } @@ -123,7 +124,11 @@ function isStaleSection( if (design.geometry_preset === "rock" && design.two_stage_slope === undefined) return true; if (!profiles?.length) return false; const planned = designElevationAt(profiles, section.chainage_m); - return planned !== undefined && Math.abs(planned - design.design_elevation_m) > toleranceM; + if (planned === undefined) return false; + // 세월교가 앉은 측점의 노면은 월류 높이만큼 **일부러** 내려 앉는다 — 그 차이를 빼지 + // 않으면 계획선을 안 건드려도 늘 어긋난 것으로 나온다(2026-09-03 사용자 보고). + const designed = design.design_elevation_m + (design.surface_drop_m ?? 0); + return Math.abs(planned - designed) > toleranceM; } /**