- 좌측 패널 · 아래 단추 넷 · 종단도 · 구조물 배치 폼을 페이지에 한 번만 만듦(design_shared) — 두 보기가 같은 노드 - 보기는 가운데 위 칸만: 종단 보기 = 3D(design_view_profile) · 횡단 보기 = 횡단 카드(design_view_cross · 처음 보일 때 그림) - 옛 B05_Profile_UI_Page · B06_Section_UI_Page 삭제 · 한 벌이 받은 조작은 지금 보기로, 관 목록 · 횡단 재계산 알림은 두 보기 모두로 - 진행 단계 종단설계 · 횡단설계 두 칸이 보기를 따라 켜짐 · 누르면 주소만 바꿈 · 제목도 따라감(setTitle · setActiveStep) - [저장] · [확정] 한 벌 — 두 보기 몫을 모아 saveDesign 한 번 · 종단 보기 [확정] 켬(횡단 보기를 숨긴 채 세워 횡단 자료를 얻음) - [초기화] · 계획노선 편집은 같은 페이지라도 다시 세움(remountRoute) - 덧붙임: 방향키는 초점 있는 쪽만(조정창 ↔ 종단도 계획고) · 횡단 보기 계획고 편집에도 [3D 업데이트] 대기 표시 · 종단 보기 폼 관 옵션을 횡단 캐시에도 얹음 · 종단 보기에서 횡단 조정 칸 가림 - 나눔: Cross_View 756→658(_Cross_View_Frame) · Page_Structures_Panel 700→547(_Structures_Spec) · 옛 B05 Page 786 → design_view_profile 638(_Page_Latest · _Page_Stations) - 시험: test_design_view_parity 한 벌 기준(한 패널 · 보기 오가도 같은 노드) · page_single · profile_single · save_channels · save_recompute 등 옮긴 자리로 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KmELpdXFDRZHhfbGuRhSmL
105 lines
4.4 KiB
Python
105 lines
4.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""종단 계획고 변경이 횡단 설계에 반영되는지(2026-08-23 사용자 지시).
|
|
|
|
횡단 설계는 계산 당시 계획고(design.design_elevation_m)를 기준으로 설계선 좌표를
|
|
굳혀 둔다. 계획선이 뒤에 바뀌면 설계선은 옛 자리에 남는데 화면의 계획고 십자선·
|
|
3D 예상형상은 최신 계획선을 쓴다 — 두 기준이 어긋난다(실측: 한 노선 최대 2.21m).
|
|
어긋남 판정을 B05·B06 공용 규칙(hasStaleDesigns)으로 두고, B05 진입 때도
|
|
재계산이 돌게 한 것을 소스로 잠근다.
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
if str(PROJECT_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
COMMON = (PROJECT_ROOT / "B06_Section" / "B06_Section_UI_Section_Common.ts").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
# 종단 패널 본체는 종단도 한 벌로 옮겼다(계획서 24-2c).
|
|
B05_PANEL = (PROJECT_ROOT / "A00_Common" / "design" / "design_profile.ts").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
# 설계 재계산 자리는 700줄 제한으로 `_UI_Page_Design_Sync` 로 떨어져 나갔다(2026-09-13).
|
|
# 횡단 보기 본체는 한 페이지 보기 파일로 옮겼다(계획서 24-3b).
|
|
B06_PAGE = (PROJECT_ROOT / "A00_Common" / "design" / "design_view_cross.ts").read_text(
|
|
encoding="utf-8"
|
|
) + (PROJECT_ROOT / "B06_Section" / "B06_Section_UI_Page_Design_Sync.ts").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
|
|
|
|
def _stale(profile_samples, sections, tol=1e-3):
|
|
"""TS hasStaleDesigns와 같은 규칙 — 계획선 보간값과 계산 기준의 차이."""
|
|
|
|
def plan_at(ch):
|
|
if not profile_samples:
|
|
return None
|
|
if ch <= profile_samples[0][0]:
|
|
return profile_samples[0][1]
|
|
if ch >= profile_samples[-1][0]:
|
|
return profile_samples[-1][1]
|
|
for i in range(1, len(profile_samples)):
|
|
c0, z0 = profile_samples[i - 1]
|
|
c1, z1 = profile_samples[i]
|
|
if ch <= c1:
|
|
span = c1 - c0
|
|
t = (ch - c0) / span if span > 0 else 0
|
|
return z0 + (z1 - z0) * t
|
|
return profile_samples[-1][1]
|
|
|
|
out = []
|
|
for ch, used in sections:
|
|
if used is None:
|
|
continue
|
|
planned = plan_at(ch)
|
|
if planned is not None and abs(planned - used) > tol:
|
|
out.append(ch)
|
|
return out
|
|
|
|
|
|
PROFILE = [(0.0, 100.0), (20.0, 102.0), (40.0, 104.0)]
|
|
|
|
|
|
def test_detects_design_computed_from_old_plan():
|
|
"""계획선이 바뀌어 계산 기준과 어긋난 측점을 잡는다."""
|
|
assert _stale(PROFILE, [(20.0, 101.0)]) == [20.0]
|
|
|
|
|
|
def test_clean_when_design_matches_plan():
|
|
"""계획선과 계산 기준이 같으면 재계산 대상이 아니다."""
|
|
assert _stale(PROFILE, [(0.0, 100.0), (20.0, 102.0), (40.0, 104.0)]) == []
|
|
|
|
|
|
def test_interpolates_between_profile_samples():
|
|
"""측점이 계획선 샘플 사이에 있으면 보간값과 비교한다(비정규 측점·배수관 자리)."""
|
|
assert _stale(PROFILE, [(10.0, 101.0)]) == []
|
|
assert _stale(PROFILE, [(10.0, 100.5)]) == [10.0]
|
|
|
|
|
|
def test_sections_without_design_are_skipped():
|
|
"""설계가 아직 없는 측점은 판정 대상이 아니다(최초 생성 전)."""
|
|
assert _stale(PROFILE, [(20.0, None)]) == []
|
|
|
|
|
|
def test_tolerance_ignores_rounding_noise():
|
|
"""저장 반올림 수준(1e-3 이하)은 어긋남으로 보지 않는다 — 무한 재계산 방지."""
|
|
assert _stale(PROFILE, [(20.0, 102.0005)]) == []
|
|
|
|
|
|
def test_source_shared_rule_and_b05_entry_recompute():
|
|
"""공용 판정 유틸을 B05 진입·B06 재계산이 함께 쓰는지 소스 검사."""
|
|
assert "export function hasStaleDesigns" in COMMON
|
|
# B05: 진입 렌더에서 어긋남이 있으면 프리뷰 1회로 맞춘다.
|
|
assert "hasStaleDesigns" in B05_PANEL
|
|
# 진입 즉시 한 번 맞춘다 — 편집 반영 계획선을 넣어 부르고, 어긋나면 예약한다.
|
|
assert "hasStaleDesigns({" in B05_PANEL
|
|
assert "scheduleCrossPreview();" in B05_PANEL
|
|
# B06: 같은 규칙 하나만 쓴다 — 옛 암 2단계 필드 누락 조건도 공용 유틸로 옮겼다
|
|
# (2026-09-03 일원화: 조건이 갈리면 같은 데이터가 두 화면에서 다른 값이 된다).
|
|
assert "hasStaleDesigns(sectionDetail)" in B06_PAGE
|
|
assert "two_stage_slope === undefined" in COMMON
|
|
assert "two_stage_slope === undefined" not in B06_PAGE
|