feat(B05/B06): 종단 그래프 줌 버튼 단순화 + 세로 자동 맞춤

- 줌 버튼을 셋(줌인·줌아웃·초기화)으로 줄임. 세로 배율·창 이동 버튼 제거
- 배율 1 = 기본값이자 축소 한계 — 한계에 닿은 버튼은 흐리게 죽임
- 세로는 보이는 누가거리 구간의 지반·계획선 범위로 자동(공통 함수
  windowElevationRange, B05·B06 종단이 함께 씀). 스크롤이 멈춘 뒤 0.16초에 갱신
- 계획고 편집 버튼을 누르고 있는 동안 Y 축 고정, 손을 떼면 다시 맞춤
- 유토곡선 Y 도 같은 창 기준(전 구간 ±200㎥ 고정 해제)
- B06 종단이 쓰던 공통 Y 스케일(calculateYScale) 제거 — 횡단 카드는 원래 안 쓰던 값

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-04 18:14:59 +09:00
co-authored by Claude Opus 5
parent 92441eb8c9
commit b51c887549
9 changed files with 249 additions and 54 deletions
+43 -2
View File
@@ -43,6 +43,12 @@ export interface MassHaulAxis {
axisX?: number;
/** 그래프 위 여백(px). 생략하면 기본(10). B05는 범례 오버레이만큼 크게 준다. */
padTop?: number;
/**
* **화면에 보이는 누가거리 구간**(m). 넘기면 Y 범위를 이 구간의 누계 토량으로 잡는다
* (2026-09-04 사용자 지시 — 종단 그래프의 세로 자동 맞춤과 같은 창). 생략하면 예전처럼
* 전 구간 기준 ±200㎥ 고정이다.
*/
viewRange?: { fromM: number; toM: number };
}
/** 축 눈금이 읽히는 최소 높이. 이보다 낮아지면 그래프가 뭉개진다. */
@@ -128,9 +134,44 @@ const VOLUME_RANGE_BASE_M3 = 200;
* Y축 상·하한을 잡는다. 기본 −200~+200㎥ 고정(0선 항상 포함), 곡선이 넘치는 쪽만
* 데이터에 5% 여유를 더해 확장한다. B05·B06이 같은 함수를 쓰므로 두 화면이 함께 고정된다.
*/
function volumeRange(series: MassHaulSeries[]): { min: number; max: number } {
function volumeRange(
series: MassHaulSeries[],
viewRange?: { fromM: number; toM: number },
): { min: number; max: number } {
let rawMin = 0;
let rawMax = 0;
if (viewRange) {
// 보이는 구간만 훑는다. 창 경계를 걸친 선분이 안에서 솟구치므로 바깥 이웃 한 점도 본다.
let found = false;
for (const entry of series) {
const points = entry.result.points;
let first = -1;
let last = -1;
for (let index = 0; index < points.length; index += 1) {
const chainage = points[index].chainage_m;
if (chainage < viewRange.fromM || chainage > viewRange.toM) continue;
if (first < 0) first = index;
last = index;
}
if (first < 0) continue;
for (
let index = Math.max(0, first - 1);
index <= Math.min(points.length - 1, last + 1);
index += 1
) {
const volume = points[index].cumulative_volume_m3;
if (!Number.isFinite(volume)) continue;
rawMin = found ? Math.min(rawMin, volume) : volume;
rawMax = found ? Math.max(rawMax, volume) : volume;
found = true;
}
}
if (found) {
// 창 안이 거의 평평하면(구간 토량 변화가 없으면) 최소 폭을 줘 선이 축에 붙지 않게 한다.
const padding = Math.max((rawMax - rawMin) * 0.05, 1);
return { min: rawMin - padding, max: rawMax + padding };
}
}
for (const entry of series) {
rawMin = Math.min(rawMin, entry.result.min_cumulative_m3);
rawMax = Math.max(rawMax, entry.result.max_cumulative_m3);
@@ -323,7 +364,7 @@ export function createMassHaulChart(
// 기준 버튼이 라디오가 되면서(택1 표시) Y 범위도 **표시 중인 곡선**으로 잡는다 —
// 숨은 기준까지 합쳐 잡으면 선택한 그래프가 눌려 보인다. 아무것도 안 켰으면 전체로 폴백.
const rangeSource = series.filter((entry) => visibleKeys.has(entry.key));
const { min, max } = volumeRange(rangeSource.length ? rangeSource : series);
const { min, max } = volumeRange(rangeSource.length ? rangeSource : series, axis.viewRange);
const span = Math.max(max - min, 1e-6);
const y = (volume: number) => padTop + ((max - volume) / span) * plotHeight;