From b8aa5ce01471268d42fb9359ad78c260781b0fd5 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 3 Sep 2026 20:32:46 +0900 Subject: [PATCH] =?UTF-8?q?fix(B05):=20=EC=A2=85=EB=8B=A8=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=20=EC=A0=88=C2=B7=EC=84=B1=ED=86=A0=20=EB=B6=88?= =?UTF-8?q?=EA=B7=A0=ED=98=95=20=ED=91=9C=EC=8B=9C=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?+=20=EC=9C=A0=ED=86=A0=EA=B3=A1=EC=84=A0=20=EA=B9=9C=EB=B9=A1?= =?UTF-8?q?=EC=9E=84=20=EC=A0=9C=EA=B1=B0=20+=20=EC=89=AC=ED=94=84?= =?UTF-8?q?=ED=8A=B8=20=EC=95=88=EB=82=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용자 지적 3건. 1. 상단 표시줄의 「절·성토 불균형」은 **종단 기준** 값이었음 — 계획선과 지반선 사이 세로 면적(㎡)의 비라 실제 토량이 아니고 판단 근거가 안 됨. 삭제함. 의미 있는 균형은 횡단 단면적을 쌓아 부피(㎥)로 내는 하단 유토곡선 요약이고 그쪽은 그대로 둠. 2. 계획고를 연속으로 누르면 유토곡선이 매번 사라졌다 다시 서서 깜빡였음. 재계산 대기 중에 곡선을 **지우지 않고 직전 것을 그대로 두며**, 요약줄 끝에 「다시 계산 중…」 칩만 붙임. 재계산이 브라우저 안에서 수십 ms에 끝나므로 화면이 비는 구간이 없어짐. 아직 한 번도 못 그렸을 때만 예전처럼 문구로 자리를 채움. 3. [쉬프트]에서 아무것도 안 골라지던 것은 **직선화된 구간이 없어서**였음(초기 계획선은 마디마다 기울기가 달라 직선 구간이 0개). 안내를 갈라 적음 — 「직선화된 구간이 없습니다 — [직선화]로 먼저 만드세요」 / 「…고르세요 (여러 개 가능) — 끝내려면 [쉬프트]를 다시 누르세요」. 직선화 안내에도 취소 방법을 덧붙임. 전체 테스트 183건 통과, `tsc --noEmit` 통과. Co-Authored-By: Claude Opus 5 (1M context) --- B05_Profile/B05_Profile_UI_Profile_Balance.ts | 14 +++++----- .../B05_Profile_UI_Profile_MassHaul.ts | 27 ++++++++++++++++--- .../B05_Profile_UI_Profile_Panel_Tools.ts | 5 ++++ .../B05_Profile_UI_Profile_Straighten.ts | 16 +++++++++++ B05_Profile/B05_Profile_UI_Profile_Tools.ts | 10 +++++-- B05_Profile/B05_Profile_UI_Style_MassHaul.css | 8 ++++++ 6 files changed, 66 insertions(+), 14 deletions(-) diff --git a/B05_Profile/B05_Profile_UI_Profile_Balance.ts b/B05_Profile/B05_Profile_UI_Profile_Balance.ts index 5f7f7c68..f6166abe 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Balance.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Balance.ts @@ -4,9 +4,9 @@ * * 표시 항목(별표2 근거): * 최대 기울기 x.x% (상한 y%) — Ⅰ.2.라: 설계속도·지형별 상한 준수 판정 - * 불균형 z% / 허용 w% — Ⅰ.1.나.(4)(다): 시공계획고 절·성토 균형 * 곡선 필요 n곳 — Ⅰ.2.마: 대수차 5% 초과 변화점의 종단곡선 삽입 - * 절·성토 면적은 하단 유토곡선이 부피로 더 정확히 보여 주고, 변화점·종단곡선 개수와 + * 절·성토 균형은 **하단 유토곡선**이 횡단 기준 부피(㎥)로 보여 준다 — 여기 있던 종단 기준 + * 불균형(㎡ 비율)은 실제 토량이 아니라 의미가 없어 뺐다(2026-09-03). 변화점·종단곡선 개수와 * 기본 곡선길이 L은 판단 기준이 없어 뺐다(2026-08-19 사용자 지적 + 지식DB 대조). * [저장선 복원]은 상단에서 내려 그래프 조작부로 옮겼다. * @@ -53,7 +53,7 @@ export function renderBalanceBar(params: BalanceBarParams): void { return; } const { alignment } = params; - const { balance, policy, violations } = alignment; + const { policy, violations } = alignment; // 최대 종단기울기 — 법정 상한 대비가 이 화면의 첫 판정 항목이다(별표2 Ⅰ.2.라). const steepest = alignment.segments.reduce( (worst, segment) => Math.max(worst, Math.abs(segment.grade_percent)), @@ -70,12 +70,10 @@ export function renderBalanceBar(params: BalanceBarParams): void { `${steepest.toFixed(1)} % / 상한 ${policy.max_grade_pct.toFixed(1)} %`, violations.length ? "over" : undefined, ], - [ - "절·성토 불균형", - `${balance.imbalance_percent.toFixed(1)} % / 허용 ${balance.tolerance_percent.toFixed(0)} %`, - balance.within_tolerance ? undefined : "over", - ], ]; + // 절·성토 불균형은 2026-09-03 사용자 지시로 뺐다. 이 값은 **종단 기준**이었다 — + // 계획선과 지반선 사이 세로 면적(㎡)의 절·성토 비이지 실제 토량이 아니다. 의미 있는 + // 균형은 횡단 단면적을 쌓아 부피로 내는 하단 유토곡선 요약(㎥)이고, 그쪽이 이미 있다. // 필요한 곳이 없으면 적지 않는다 — "0곳"은 화면 폭만 먹는다. if (curvesNeeded) entries.push(["종단곡선 필요", `${curvesNeeded} 곳`, "over"]); // 횡단배수 최소고 표시는 2026-09-02 사용자 지시로 삭제했다. 편집 차단(강제)은 diff --git a/B05_Profile/B05_Profile_UI_Profile_MassHaul.ts b/B05_Profile/B05_Profile_UI_Profile_MassHaul.ts index 840737d7..42c0cdfc 100644 --- a/B05_Profile/B05_Profile_UI_Profile_MassHaul.ts +++ b/B05_Profile/B05_Profile_UI_Profile_MassHaul.ts @@ -293,7 +293,11 @@ export function createRouteMassHaulPanel(onChanged: () => void): RouteMassHaulPa mirror(scroll, main); } + /** 곡선을 한 번이라도 그렸는지 — 재계산 대기 중 화면을 비우지 않기 위한 표시. */ + let drawnOnce = false; + function note(message: string): void { + drawnOnce = false; bar.replaceChildren(); scroll.replaceChildren(); legendLayer.replaceChildren(); @@ -312,11 +316,25 @@ export function createRouteMassHaulPanel(onChanged: () => void): RouteMassHaulPa note("토량환산계수를 불러오지 못해 유토곡선을 계산할 수 없습니다."); return; } - // 저장된 횡단이 지금 계획선과 어긋나 있으면 그 면적은 **옛 계획고로 만든 값**이다. - // 그걸 잠깐 그렸다가 재계산 결과로 갈아 끼우면 사용자가 옛 그림을 본다(2026-09-03 - // 사용자 확정: 「새 값만 보여주기」). Panel이 이미 재계산을 예약해 두므로 기다린다. + // 저장된 횡단이 지금 계획선과 어긋나 있으면 그 면적은 옛 계획고로 만든 값이다 — + // 새 값이 올 때까지 기다린다(2026-09-03 사용자 확정: 「새 값만 보여주기」). + // + // 다만 **곡선을 지우지는 않는다**. 계획고를 연속으로 누르면(초당 10회) 매번 곡선이 + // 사라졌다 다시 서서 화면이 깜빡였다(2026-09-03 사용자 보고). 재계산은 브라우저 + // 안에서 끝나 수십 ms면 끝나므로, 그동안 **직전 곡선을 그대로 두고** 상태줄로만 + // 알린다. 아직 한 번도 못 그렸을 때만 문구로 자리를 채운다. if (params.pendingRecalc) { - note("횡단을 지금 계획선에 맞춰 다시 계산하는 중입니다."); + if (drawnOnce) { + const pending = bar.querySelector(".b05-profile__masshaul-pending"); + if (!pending) { + const chip = document.createElement("span"); + chip.className = "b05-profile__masshaul-pending"; + chip.textContent = "다시 계산 중…"; + bar.append(chip); + } + } else { + note("횡단을 지금 계획선에 맞춰 다시 계산하는 중입니다."); + } return; } // B06과 같은 계산 — 횡단 기준(정식)과 종단 기준(개략 비교)을 함께 낸다. @@ -386,6 +404,7 @@ export function createRouteMassHaulPanel(onChanged: () => void): RouteMassHaulPa onChanged(); }), ); + drawnOnce = true; } return { diff --git a/B05_Profile/B05_Profile_UI_Profile_Panel_Tools.ts b/B05_Profile/B05_Profile_UI_Profile_Panel_Tools.ts index a22a7aaa..06ce78de 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Panel_Tools.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Panel_Tools.ts @@ -19,6 +19,7 @@ import { createProfileHistory, type ProfileHistory } from "./B05_Profile_UI_Prof import { createProfileTools, type ProfileTools } from "./B05_Profile_UI_Profile_Tools"; import { detectStraightRun, + hasStraightRuns, shiftStraightRuns, straightenBetween, tiltStraightRun, @@ -118,6 +119,10 @@ export function createPanelTools(ctx: PanelToolsContext): PanelTools { onRedo: () => inner.redo(), canUndo: () => inner.canUndo(), canRedo: () => inner.canRedo(), + hasStraightRuns: () => { + const alignment = ctx.alignment(); + return alignment ? hasStraightRuns(alignment) : false; + }, onChanged: ctx.refresh, }); diff --git a/B05_Profile/B05_Profile_UI_Profile_Straighten.ts b/B05_Profile/B05_Profile_UI_Profile_Straighten.ts index 1bcc587e..5b9279de 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Straighten.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Straighten.ts @@ -114,6 +114,22 @@ export function detectStraightRun( }; } +/** + * 지금 계획선에 **직선화된 구간이 하나라도 있는가** — [쉬프트] 안내 문구용. + * + * 초기 계획선은 모든 측점이 변화점이라 마디마다 기울기가 달라 직선 구간이 없다. 그때 + * [쉬프트]로 아무 데나 눌러도 잡히는 것이 없어 「선택이 안 된다」로 보였다 + * (2026-09-03 사용자 보고). 안내를 나누려면 이 판정이 필요하다. + */ +export function hasStraightRuns(alignment: ProfileAlignment): boolean { + for (let index = 1; index < alignment.segments.length; index += 1) { + if (Math.abs(gradeAt(alignment, index) - gradeAt(alignment, index - 1)) < COLLINEAR_EPSILON) { + return true; + } + } + return false; +} + /** 두 측점 사이(끝 제외)의 변화점 chainage. */ function interiorNodes(alignment: ProfileAlignment, fromM: number, toM: number): number[] { return pviChainages(alignment).filter( diff --git a/B05_Profile/B05_Profile_UI_Profile_Tools.ts b/B05_Profile/B05_Profile_UI_Profile_Tools.ts index e42c66a6..758e2e55 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Tools.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Tools.ts @@ -28,6 +28,8 @@ export interface ProfileToolsCallbacks { onRedo: () => void; canUndo: () => boolean; canRedo: () => boolean; + /** 지금 계획선에 직선화된 구간이 있는가 — [쉬프트] 안내 문구를 가른다. */ + hasStraightRuns: () => boolean; /** 선택 표시를 갱신해야 할 때(모드·선택 변화) 호출된다. */ onChanged: () => void; } @@ -166,12 +168,16 @@ export function createProfileTools(callbacks: ProfileToolsCallbacks): ProfileToo if (mode === "straighten" && pending !== null) { const hint = document.createElement("em"); hint.className = "b05-route-profile__tool-hint"; - hint.textContent = `${pending.toFixed(1)}m 선택 — 두 번째 측점을 고르세요`; + hint.textContent = `${pending.toFixed(1)}m 선택 — 두 번째 측점을 고르세요 (취소는 [직선화] 다시 누르기)`; wrap.append(hint); } else if (mode === "shift" && !runs.length) { const hint = document.createElement("em"); hint.className = "b05-route-profile__tool-hint"; - hint.textContent = "직선화된 라인을 고르세요"; + // 고를 것이 아예 없는 경우와 있는데 안 고른 경우를 갈라 적는다 — 초기 계획선은 + // 마디마다 기울기가 달라 직선 구간이 없어, 눌러도 안 잡히는 것이 정상이다. + hint.textContent = callbacks.hasStraightRuns() + ? "직선화된 라인을 고르세요 (여러 개 가능) — 끝내려면 [쉬프트]를 다시 누르세요" + : "직선화된 구간이 없습니다 — [직선화]로 먼저 만드세요"; wrap.append(hint); } return wrap; diff --git a/B05_Profile/B05_Profile_UI_Style_MassHaul.css b/B05_Profile/B05_Profile_UI_Style_MassHaul.css index 5ee9930a..c482c4ca 100644 --- a/B05_Profile/B05_Profile_UI_Style_MassHaul.css +++ b/B05_Profile/B05_Profile_UI_Style_MassHaul.css @@ -112,6 +112,14 @@ padding-top: 0; } +/* 재계산 대기 표시 — 곡선을 지우지 않고 요약줄 끝에만 붙인다(2026-09-03 깜빡임 제거). */ +.b05-profile__masshaul-pending { + margin-left: auto; + color: var(--color-text-secondary); + font-size: var(--text-caption); + opacity: 0.8; +} + .b05-profile__masshaul-empty { padding: var(--spacing-8) var(--spacing-16); color: var(--color-text-secondary);