"""764-1 추천 측점 맞춤 — 덜 나쁜 자리 고를 때 원지반 기울기 늘어남 · 옮긴 거리를 따짐(요구 606-2). ./venv/Scripts/python.exe -m pytest resources/tester/test_764_1_snap_grade.py -q · 가까운 측점 자리가 비탈(기울기 늘어남)이면 다음 측점 자리로 — 옛 순위는 가까운 측점 · 토공을 앞세워 비탈 자리를 골랐음. · 모든 자리가 크게 망치면 그중 가장 덜 망치는 자리(곡선 가운데 = 측점은 지킴). · 판(`ALGO_VERSION`)이 다른 저장본은 꺼내지 않음. """ import json import math import pytest from B05_Profile.B05_Profile_Engine_RouteInitial_Station import ( STATION_M, curve_mc_chainages, off_station_count, snap_curves_to_stations, ) from B05_Profile.B05_Profile_Engine_RouteInitial_Store import ( ALGO_VERSION, read_stored_any, store_dir, ) from common_util.common_util_route_polyline import build_planned_polyline RADIUS = 12.0 # MC ≈ 109.5 m — 가까운 측점 100 · 다음 120 POINTS = [(0.0, 0.0), (110.0, 0.0), (230.0, 90.0)] class _Slope: """직접 잼 흉내 — 늘 못 지남(덜 나쁜 자리만 남음) · 곡선 가운데가 x < 112 면 비탈 쪽. 토공은 비탈 쪽이 더 작게(옛 순위가 비탈을 고르도록 유혹).""" def __init__(self, steep: float, flat: float) -> None: self.steep, self.flat = steep, flat def rise(self, local, reference) -> float: arc = local[1:-1] return self.steep if sum(x for x, _ in arc) / len(arc) < 112.0 else self.flat def judge(self, local, reference): return False, (False, 0.0, -1000.0 * self.rise(local, reference)) def ok(self, local, reference) -> bool: return False def _snap(guard) -> float: outline = build_planned_polyline( POINTS, min_radius_m=RADIUS, simplify=False, curve_flags=[True] * 3, radii=[None, 40.0, None], ) (mc,) = curve_mc_chainages(outline) assert abs(mc - 100.0) < abs(mc - 120.0) # 가까운 측점 = 비탈 쪽 snapped = snap_curves_to_stations(outline, RADIUS, guard=guard) assert snapped is not None and off_station_count(snapped) == 0 (placed,) = curve_mc_chainages(snapped) return round(placed / STATION_M) * STATION_M def test_less_bad_spot_avoids_slope() -> None: """가까운 측점 자리가 기울기를 크게 늘리면 다음 측점 자리.""" assert _snap(_Slope(steep=0.30, flat=0.0)) == 120.0 def test_all_ruined_takes_least_ruin() -> None: """모든 자리가 크게 망치면 그중 가장 덜 망치는 자리 · MC 는 측점에.""" assert _snap(_Slope(steep=0.20, flat=0.40)) == 100.0 assert _snap(_Slope(steep=0.40, flat=0.20)) == 120.0 @pytest.mark.parametrize("version, found", [(ALGO_VERSION - 1, False), (ALGO_VERSION, True)]) def test_other_version_not_read(tmp_path, version: int, found: bool) -> None: folder = store_dir(tmp_path) folder.mkdir(parents=True) data = {"version": version, "source": "s", "pool": "p", "request": {}, "result": {}} (folder / "k.json").write_text(json.dumps(data), encoding="utf-8") got = read_stored_any(tmp_path, "p", lambda source: "k") assert (got is not None) is found def test_ruin_threshold_constants() -> None: from B05_Profile.B05_Profile_Engine_RouteInitial_Station import RUIN_GAP_M, RUIN_RISE assert RUIN_RISE == pytest.approx(0.05) and math.isclose(RUIN_GAP_M, STATION_M / 2)