- TS `logic/B05_Profile_Profile_Straight.ts` ↔ 파이썬 `is_straight_bend` 짝 · 곡선 넣기 · ▲▼ 축 · 직선 찾기 - 적용 한 곳 `tidyEdits` — 직선 위 기억 R · 0 오프셋 지움(모양 그대로) - 직선화 · 자유지정 끝이 꼭짓점 아니면 지금 높이로 세움 · 고른 직선 기울이기 = 지금 경첩 높이 + 걸음 - 「편집 측점 N개」 = 꺾인 편집 꼭짓점 수 · 시험 test_426 + 헬퍼 스텁 넷 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWh5wNNycMkbwMpACz1rte
133 lines
4.9 KiB
Python
133 lines
4.9 KiB
Python
# -*- 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")
|