Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1

This commit is contained in:
2026-09-04 19:14:11 +09:00
13 changed files with 430 additions and 112 deletions
+20 -3
View File
@@ -30,13 +30,21 @@ DESIGNING_LOCK_NAME = "initial_design.lock"
DESIGN_FAILED_NAME = "initial_design.failed"
# 프로젝트 루트 기준 상대 경로 — 정본 파일이 사는 자리 전부.
#
# 배수유역은 `edits/`(관 지점 편집분)만 뜨다가 **폴더 통째**로 넓혔다(2026-09-04 사용자
# 확정: 「초기값 = 파일 입력 직후 결과 전부」). 관을 옮기면 세부유역(`04_detailed_basins`)이
# 다시 나뉘는데 그 산출물이 스냅샷 밖이라 [초기화]가 옛 유역도를 그대로 남겼다.
# 용량은 실측 8.6MB(스냅샷 전체 3.9MB → 약 12MB)로 감당할 만하다.
_FILE_TREES = (
"B05_Profile/route",
"B06_Section/longitudinal",
"B06_Section/cross_sections",
"B04_PreProcess/drainage/edits",
"B04_PreProcess/drainage",
)
# 배수유역을 폴더 통째로 넓히기 전(2026-09-04)에 찍힌 스냅샷이 갖고 있는 자리.
_LEGACY_DRAINAGE_TREE = "B04_PreProcess__drainage__edits"
# `routes.id`를 참조하는 표는 스키마상 이 넷이 전부다(001_create_schema.sql:539~556).
_CHILD_TABLES = ("route_points", "route_statistics", "longitudinal_sections", "cross_sections")
@@ -238,11 +246,20 @@ def wipe_edited_masters(project_root: Path) -> list[str]:
def restore_snapshot_files(project_root: Path) -> None:
"""스냅샷 파일을 작업본 자리에 되돌린다. 스냅샷에 없던 트리는 비워 둔다."""
"""스냅샷 파일을 작업본 자리에 되돌린다. 스냅샷에 없던 트리는 비워 둔다.
배수유역 범위를 넓히기 전(2026-09-04)에 찍힌 스냅샷은 `edits/`만 갖고 있다 —
그런 프로젝트는 예전처럼 그 자리만 되돌린다. 넓힌 트리를 못 찾았다고 그냥 넘어가면
관 지점 편집분이 초기화 뒤에도 남는다.
"""
root = Path(project_root)
source = snapshot_dir(root)
for tree in _FILE_TREES:
_copy_tree(source / tree.replace("/", "__"), root / tree)
stored = source / tree.replace("/", "__")
if not stored.is_dir() and tree == "B04_PreProcess/drainage":
_copy_tree(source / _LEGACY_DRAINAGE_TREE, root / "B04_PreProcess/drainage/edits")
continue
_copy_tree(stored, root / tree)
async def restore_initial_snapshot(
+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;