From 79dc0cc82d38002bea286afcfd3a2daa44e8df6d Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 4 Sep 2026 23:10:22 +0900 Subject: [PATCH] =?UTF-8?q?fix(B05,B06):=20=EA=B3=84=ED=9A=8D=EA=B3=A0?= =?UTF-8?q?=EB=A5=BC=20=EC=A1=B0=EC=A0=88=ED=95=B4=EB=8F=84=20=ED=9A=A1?= =?UTF-8?q?=EB=8B=A8=20=EA=B8=B0=EC=A4=80=20=EC=9C=A0=ED=86=A0=EA=B3=A1?= =?UTF-8?q?=EC=84=A0=EC=9D=B4=20=EC=95=88=20=EB=94=B0=EB=9D=BC=EC=98=A4?= =?UTF-8?q?=EB=8D=98=20=EA=B2=83=20=E2=80=94=20=EA=B3=84=EC=82=B0=20?= =?UTF-8?q?=EC=BA=90=EC=8B=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용자 보고(2026-09-04) — 「종단 곡선 조절에 따라 횡단 기준 유토곡선 변경 안 됨」. 오늘 프레임을 아끼려고 넣은 곡선 계산 캐시가 원인. 횡단 설계는 `refreshCrossDesigns` 가 **같은 객체를 제자리에서 고치므로**(배열·요소 동일성 유지) 「입력 객체가 같으면 안 바뀐 것」 이라는 캐시 판정이 성립하지 않음. B06 은 `detail` 동일성으로 캐시해 **한 번 계산한 뒤로는 영원히 낡은 곡선**을 보였음. - 두 곳(B05 패널·B06 조립기)의 캐시를 걷어내고 그릴 때마다 계산하게 되돌림. - 왜 캐시하면 안 되는지 주석으로 못 박음. 자체검증(공용 브라우저 임시 탭): ① B06 — 횡단 지반 종류를 바꾸자 절토 4,500.3 → 4,500.7㎥, 토취 10,211.5 → 10,211.1㎥, 곡선 경로도 바뀜(고치기 전에는 완전 동일) ② B05 — 계획고 6칸 올리자 계획선·유토곡선 둘 다 바뀜(성토 14,729 → 17,513㎥) ③ 휠 20칸 기준 최대 프레임 27.0ms, 33ms 초과 **0건**(캐시 없어도 예산 안) ④ 전체 시험 381 통과·17 건너뜀. Co-Authored-By: Claude Opus 5 --- .../B05_Profile_UI_Profile_MassHaul.ts | 46 ++++--------------- .../B06_Section_UI_Section_View_MassHaul.ts | 28 +++-------- 2 files changed, 14 insertions(+), 60 deletions(-) diff --git a/B05_Profile/B05_Profile_UI_Profile_MassHaul.ts b/B05_Profile/B05_Profile_UI_Profile_MassHaul.ts index 8ab1394a..bc6979ad 100644 --- a/B05_Profile/B05_Profile_UI_Profile_MassHaul.ts +++ b/B05_Profile/B05_Profile_UI_Profile_MassHaul.ts @@ -308,41 +308,6 @@ export function createRouteMassHaulPanel(onChanged: () => void): RouteMassHaulPa bar.append(empty); } - /** 곡선 계산 아낌 — 입력 객체가 그대로면 다시 계산하지 않는다. */ - let seriesCache: { - longitudinal: RouteMassHaulDrawParams["longitudinal"]; - crossSections: RouteMassHaulDrawParams["crossSections"]; - context: RouteMassHaulContext; - series: MassHaulSeries[]; - } | null = null; - - function cachedSeries( - params: RouteMassHaulDrawParams, - current: RouteMassHaulContext, - ): MassHaulSeries[] { - if ( - seriesCache && - seriesCache.longitudinal === params.longitudinal && - seriesCache.crossSections === params.crossSections && - seriesCache.context === current - ) { - return seriesCache.series; - } - const series = computeMassHaulSeries( - params.longitudinal, - params.crossSections, - current.conversion, - current.naturalSpoilMinSlope, - ); - seriesCache = { - longitudinal: params.longitudinal, - crossSections: params.crossSections, - context: current, - series, - }; - return series; - } - // 세로창 버티기·부드러운 이동 상태 — 이 패널이 사는 동안 하나만 둔다(2026-09-04). const windowState = createMassHaulWindowState(); /** 마지막으로 그린 입력 — 이동이 아직 안 끝났으면 다음 프레임에 이걸로 한 번 더 그린다. */ @@ -372,9 +337,14 @@ export function createRouteMassHaulPanel(onChanged: () => void): RouteMassHaulPa } pendingChip.hidden = true; // B06과 같은 계산 — 횡단 기준(정식)과 종단 기준(개략 비교)을 함께 낸다. - // 가로로 스크롤할 때는 **입력이 그대로**인데 그릴 때마다 다시 계산하면 프레임을 잡아먹는다 - // (2026-09-04 실측: 세로창 이동 중 33ms 넘는 프레임 다수). 같은 입력이면 아껴 둔 것을 쓴다. - const series: MassHaulSeries[] = cachedSeries(params, context); + // 결과를 아껴 두지 **않는다** — 횡단 설계가 제자리에서 갈리므로 객체 동일성으로는 + // 바뀐 것을 못 잰다(2026-09-04 캐시를 넣었다가 곡선이 안 따라와 되돌림). + const series: MassHaulSeries[] = computeMassHaulSeries( + params.longitudinal, + params.crossSections, + context.conversion, + context.naturalSpoilMinSlope, + ); if (!series.length) { note("횡단 설계가 아직 없어 유토곡선을 그릴 수 없습니다."); return; diff --git a/B06_Section/B06_Section_UI_Section_View_MassHaul.ts b/B06_Section/B06_Section_UI_Section_View_MassHaul.ts index 5a72fa2c..b28167fe 100644 --- a/B06_Section/B06_Section_UI_Section_View_MassHaul.ts +++ b/B06_Section/B06_Section_UI_Section_View_MassHaul.ts @@ -82,37 +82,21 @@ export interface MassHaulPanelResult { * 컨테이너 폭 계산이 어긋나 측점 세로선이 밀린다) 범례는 스크롤 컨테이너 **밖**이라 * 붙일 자리가 서로 다르기 때문이다. */ -/** 곡선 계산 아낌 — 스크롤 중에는 입력이 그대로다. 그릴 때마다 다시 계산하면 프레임을 - * 잡아먹는다(2026-09-04 실측). */ -let seriesCache: { - detail: SectionDetailResponse; - conversion: EarthworkConversion | null; - slope: number | undefined; - series: MassHaulSeries[]; -} | null = null; - export function buildMassHaulPanel(input: MassHaulPanelInput): MassHaulPanelResult { const { detail, visibleSeries } = input; const pendingRecalc = hasStaleDesigns(detail); - const conversion = input.conversion ?? null; - const cached = - seriesCache && - seriesCache.detail === detail && - seriesCache.conversion === conversion && - seriesCache.slope === input.naturalSpoilSlope - ? seriesCache.series - : null; + // 계산 결과를 아껴 두지 **않는다**. 횡단 설계는 같은 객체를 제자리에서 고치므로 + // (`refreshCrossDesigns`) 객체가 같은지로는 바뀐 것을 못 잰다 — 2026-09-04 에 캐시를 + // 넣었다가 계획고를 조절해도 횡단 기준 곡선이 그대로였다(사용자 보고). const series: MassHaulSeries[] = - cached ?? - (conversion && !pendingRecalc + input.conversion && !pendingRecalc ? computeMassHaulSeries( detail.longitudinal, detail.cross_sections, - conversion, + input.conversion, input.naturalSpoilSlope, ) - : []); - if (!cached) seriesCache = { detail, conversion, slope: input.naturalSpoilSlope, series }; + : []; // 토량 분배는 **면을 깐 곡선 하나**(= 켜 둔 첫 곡선)에만 얹는다 — 곡선마다 평형선을 // 그리면 계단이 서로 엇갈려 어느 쪽 배분인지 읽히지 않는다. const bandedSeries = series.find((entry) => visibleSeries.has(entry.key));