From 91a01573249886f3c64a172a92f76f8f574a3b60 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 3 Sep 2026 20:12:34 +0900 Subject: [PATCH] =?UTF-8?q?fix(B05):=20=EC=9C=A0=ED=86=A0=EA=B3=A1?= =?UTF-8?q?=EC=84=A0=EC=9D=B4=20=ED=8E=B8=EC=A7=91=20=EC=A4=91=20=EA=B3=84?= =?UTF-8?q?=EC=86=8D=20=E3=80=8C=EB=8B=A4=EC=8B=9C=20=EA=B3=84=EC=82=B0=20?= =?UTF-8?q?=EC=A4=91=E3=80=8D=EC=97=90=20=EB=A8=B8=EB=AC=BC=EB=8D=98=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 낡음 판정이 **편집 전 계획선**과 견주고 있었음. 로컬 재계산은 편집된 계획고로 횡단 설계를 갱신하는데 비교 대상은 저장분이라, 편집이 있는 동안 판정이 영원히 참으로 남아 유토곡선이 빈 화면에 문구만 남았음(사용자 보고). - 그리기(`_Profile_Render`)와 진입 판정(`_Profile_Panel.render`) 모두 편집이 반영된 계획선(`toDesignProfile(alignment, …)`)과 견주도록 바꿈. 특히 진입 판정은 세션에 미저장 편집이 남은 채 새로고침하면 「어긋남 없음」으로 나와 재계산을 예약조차 하지 않았음 — 그래서 새로고침해도 문구가 그대로였음. - 세월교 측점은 노면이 월류 높이만큼 일부러 내려 앉으므로, 판정에서 `surface_drop_m` 을 더해 비교. 안 그러면 계획선을 안 건드려도 늘 어긋난 것으로 나옴. 화면 검증(공용 브라우저 5174, route 132): [초기화] 직후 문구 없음·유토곡선 정상 (절토 1,113.5㎥ / 성토 1,424.1㎥). 계획고 ▲ 1클릭 시 문구 없이 즉시 갱신 (절토 1,108.3㎥ / 성토 1,428.6㎥), ↶ 로 되돌리면 원값 복귀. 임시 진단 코드는 제거함. 전체 테스트 183건 통과. Co-Authored-By: Claude Opus 5 (1M context) --- B05_Profile/B05_Profile_UI_Profile_Panel.ts | 20 ++++++++++++++++++-- B05_Profile/B05_Profile_UI_Profile_Render.ts | 12 +++++++++--- B06_Section/B06_Section_UI_Section_Common.ts | 7 ++++++- 3 files changed, 33 insertions(+), 6 deletions(-) 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; } /**