fix(B05): 종단 기준 절·성토 불균형 표시 삭제 + 유토곡선 깜빡임 제거 + 쉬프트 안내
사용자 지적 3건. 1. 상단 표시줄의 「절·성토 불균형」은 **종단 기준** 값이었음 — 계획선과 지반선 사이 세로 면적(㎡)의 비라 실제 토량이 아니고 판단 근거가 안 됨. 삭제함. 의미 있는 균형은 횡단 단면적을 쌓아 부피(㎥)로 내는 하단 유토곡선 요약이고 그쪽은 그대로 둠. 2. 계획고를 연속으로 누르면 유토곡선이 매번 사라졌다 다시 서서 깜빡였음. 재계산 대기 중에 곡선을 **지우지 않고 직전 것을 그대로 두며**, 요약줄 끝에 「다시 계산 중…」 칩만 붙임. 재계산이 브라우저 안에서 수십 ms에 끝나므로 화면이 비는 구간이 없어짐. 아직 한 번도 못 그렸을 때만 예전처럼 문구로 자리를 채움. 3. [쉬프트]에서 아무것도 안 골라지던 것은 **직선화된 구간이 없어서**였음(초기 계획선은 마디마다 기울기가 달라 직선 구간이 0개). 안내를 갈라 적음 — 「직선화된 구간이 없습니다 — [직선화]로 먼저 만드세요」 / 「…고르세요 (여러 개 가능) — 끝내려면 [쉬프트]를 다시 누르세요」. 직선화 안내에도 취소 방법을 덧붙임. 전체 테스트 183건 통과, `tsc --noEmit` 통과. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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 사용자 지시로 삭제했다. 편집 차단(강제)은
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user