# -*- coding: utf-8 -*- """426 B05 종단 직선 판정 한 벌 — 높이 기준(1e-5 m) · 끝점 꼭짓점 · 누름 쌓임 · 편집 측점 수. 요구 259. 계산(`logic/B05_Profile_Profile_Straight.ts` · `_Alignment.ts` buildCurves · `_Pivot.ts` · `_Straighten.ts` · `design_profile_free.ts`)은 Node 헬퍼(`helper_426_profile_straight.cjs`)가 트랜스파일해 돌리고, 파이썬 짝(`B05_Profile_Engine_Grade_Alignment.py` `is_straight_bend`)은 같은 base · 편집으로 곡선 자리 · R 이 같은지 거울로 본다. ./venv/Scripts/python.exe -m pytest resources/tester/test_426_profile_straight.py -q """ import json import os import subprocess import sys from pathlib import Path import numpy as np import pytest HERE = Path(__file__).resolve().parent ROOT = HERE.parent.parent sys.path.insert(0, str(ROOT)) from B05_Profile.B05_Profile_Engine_Grade_Alignment import ( # noqa: E402 STRAIGHT_HEIGHT_M, AlignmentPolicy, build_alignment, is_straight_bend, ) def _read(relative: str) -> str: return (ROOT / relative).read_text(encoding="utf-8") @pytest.fixture(scope="module") def out(): proc = subprocess.run( ["node", os.path.join(HERE, "helper_426_profile_straight.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def test_붙은_측점_직선화_뒤_R_없음_한_직선(out): got = out["straightened"] # 옛 판정(대수차 1e-9)이면 꺾임으로 읽혔을 반올림 흠이 실제로 있음 — 시험이 그 길을 거침 assert got["noiseBends"] > 0 assert got["curvesInside"] == [] and got["radiiKeys"] == [] assert got["run"] == [40, 140] assert got["axes"] == [40, 140] def test_직선_안_누름_톱니_없음(out): got = out["straightened"] assert got["pressRise"] == pytest.approx(0.1, abs=1e-4) assert got["curvesAfterPress"] == [80] # 누른 점 하나만 꺾임 left, right = got["gradesUp"][:2], got["gradesUp"][2:] assert max(left) - min(left) < 1e-3 and max(right) - min(right) < 1e-3 # 0.4 m 조각 톱니 없음 def test_편집_측점_수는_꺾인_꼭짓점(out): assert out["straightened"]["offsetCount"] > 0 assert out["straightened"]["editedCount"] == 0 assert out["tilt"]["editedAfterOne"] == 1 def test_끝점_꼭짓점_아니면_지금_높이로_세움(out): got = out["endpoint"] assert got["endHeld"] == pytest.approx(0, abs=1e-6) assert got["endIsVertex"] assert got["curvesInside"] == [] assert all(abs(value) < 1e-5 for value in got["innerResidual"]) def test_자유지정_R_뒤_직선되면_R_지움(out): got = out["freeCase"] assert got["endHeld"] == pytest.approx(0, abs=1e-6) assert got["radiusAt140"] and got["curveAt140"] and got["curvesInsideF"] == [140] assert got["radiusKeptRaw"] # ▼ 만으로는 기억 R 이 남음 → 적용 한 곳의 정리가 지움 assert got["radiusAfterTidy"] is None and got["curvesAfterTidy"] == [] assert got["sameShape"] == pytest.approx(0, abs=1e-9) def test_고른_직선_기울이기_누름_쌓임(out): got = out["tilt"] assert got["rises"] == pytest.approx([0.1, 0.2, 0.1, 0.0], abs=1e-6) assert got["finalRadii"] == [] and got["finalCurves"] == [] def test_0_오프셋_정리(out): assert out["tidyZero"] == ["80.000"] def test_파이썬_짝_같은_기준(): assert STRAIGHT_HEIGHT_M == 1e-5 assert is_straight_bend(2.5e-6, 0.4, 20.0) # 반올림 흠(0.4 m 조각) = 직선 assert not is_straight_bend(1e-3, 20.0, 20.0) # 0.1 % 꺾임 = 꺾임 assert "STRAIGHT_HEIGHT_M = 1e-5" in _read("B05_Profile/logic/B05_Profile_Profile_Straight.ts") def test_파이썬_거울_곡선_자리_R(out): base = out["mirror"]["base"] policy = AlignmentPolicy.from_dict(base["policy"]) for case in out["mirror"]["cases"]: got = build_alignment( base_s=np.array([n["chainage_m"] for n in base["basePvi"]]), base_z=np.array([n["elevation_m"] for n in base["basePvi"]]), chainage=np.array(base["chainage"]), ground=np.array(base["ground"]), stations=base["stations"], policy=policy, edits=case["edits"], ) py = [[c["chainage_m"], c["r_m"]] for c in got["curves"]] assert [c[0] for c in py] == pytest.approx([c[0] for c in case["curves"]], abs=1e-6) assert [c[1] for c in py] == pytest.approx([c[1] for c in case["curves"]], rel=1e-6) def test_부르는_곳(): assert "tidyEdits(base, next)" in _read("A00_Common/design/design_profile.ts") assert "bentEditCount(alignment)" in _read("B05_Profile/B05_Profile_UI_Profile_Balance.ts") assert "isStraightBend(delta, spanLeft, spanRight)" in _read( "B05_Profile/logic/B05_Profile_UI_Profile_Alignment.ts" ) assert "is_straight_bend(delta," in _read("B05_Profile/B05_Profile_Engine_Grade_Alignment.py")