- B06 STRUCTURE_ROW_KEYS · 서버 _AREA_KEYS 에 slope_end_left_m · slope_end_right_m — 벽 트림 측점만 · 트림 사면 → 단마다 벽 앞 → 사이 성토부선 윤곽이 처음 땅에 닿는 오프셋 - 평면 발자국 finalSlopeEnd · B07 final_slope_end(짝) — 얹은 값 먼저 · 없으면 설계선 끝(구조물 없는 측점 그대로) - 시험 test_578_2_plan_slope_end 새로 · test_346_2 거울에 벽 측점 줄 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
101 lines
3.9 KiB
Python
101 lines
3.9 KiB
Python
"""578-2 평면 비탈 끝 = 횡단 최종 모양(구조물 처리 뒤)이 땅과 만나는 점.
|
|
|
|
- 벽(옹벽) 측점 — `structure.layouts`(서버 Node 묶음 · B06 `STRUCTURE_ROW_KEYS`)가
|
|
`slope_end_<쪽>_m` 을 얹음 · 벽 앞(성토 위 이음 → 전면 발끝)이 땅과 만나는 자리
|
|
· 설계선 끝보다 안쪽
|
|
- 구조물 없는 측점 · 벽 반대쪽 — 키 없음(평면 선 전후 0 차)
|
|
- 파이썬 짝 `final_slope_end` — 키가 있으면 그 값 · 없으면 설계선 끝
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_578_2_plan_slope_end.py -q
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_PlanSlope import ( # noqa: E402
|
|
final_slope_end,
|
|
slope_end,
|
|
)
|
|
from common_util.common_util_calc_runner import BUNDLE, run_calc # noqa: E402
|
|
|
|
|
|
def _ground(offset: float) -> float:
|
|
"""좌(+)로 내려가는 지반 — 좌 = 쌓기 · 우 = 깎기."""
|
|
return 100.0 - 0.5 * offset
|
|
|
|
|
|
def _section(chainage: float, wall: bool) -> dict:
|
|
offsets = [-20.0 + 0.5 * i for i in range(81)]
|
|
line = []
|
|
for o in offsets:
|
|
if -2.5 <= o <= 2.5:
|
|
z = 100.0
|
|
elif o > 2.5: # 쌓기 1:1.5 → 10 m 에서 땅
|
|
z = max(100.0 - (o - 2.5) / 1.5, _ground(o))
|
|
else: # 깎기 1:1 → -5 m 에서 땅
|
|
z = min(100.0 + (-2.5 - o), _ground(o))
|
|
line.append({"offset_m": o, "elevation_m": z})
|
|
section = {
|
|
"chainage_m": chainage,
|
|
"center_x": 0.0,
|
|
"center_y": chainage,
|
|
"samples": [{"offset_m": o, "elevation_m": _ground(o), "valid": True} for o in offsets],
|
|
"design": {
|
|
"design_elevation_m": 100.0,
|
|
"section_mode": "right_cut",
|
|
"road_edges": {
|
|
"left": {"offset_m": 2.5, "elevation_m": 100.0},
|
|
"right": {"offset_m": -2.5, "elevation_m": 100.0},
|
|
},
|
|
"design_line": line,
|
|
"cut_area_m2": 1.0,
|
|
"fill_area_m2": 1.0,
|
|
},
|
|
}
|
|
if wall:
|
|
section["revetment"] = {
|
|
"structure_id": "w1",
|
|
"type_id": "retaining_wall",
|
|
"name": "옹벽",
|
|
"start_m": 0.0,
|
|
"end_m": 20.0,
|
|
"anchor_m": 10.0,
|
|
"form": "콘크리트",
|
|
"height_m": 3.0,
|
|
"side": "좌",
|
|
}
|
|
return section
|
|
|
|
|
|
@pytest.mark.skipif(not BUNDLE.is_file(), reason="design_calc 묶음 없음(npm run build:design-calc)")
|
|
def test_벽_측점만_비탈_끝을_얹음() -> None:
|
|
sections = [_section(0.0, False), _section(10.0, True), _section(20.0, False)]
|
|
results = run_calc(["structure.layouts"], {"detail": {"cross_sections": sections}})
|
|
assert results is not None, "Node 묶음 실패"
|
|
rows = {row["chainage_m"]: row for row in results["structure.layouts"]["areas"]}
|
|
plain = slope_end(sections[1]["design"]["design_line"], sections[1]["samples"], 2.5, "left")
|
|
assert plain == pytest.approx(10.0)
|
|
end = rows[10.0]["slope_end_left_m"]
|
|
assert 2.5 < end < plain, f"벽이 자른 끝({end})이 설계선 끝({plain}) 안쪽이어야"
|
|
assert "slope_end_right_m" not in rows[10.0], "벽 반대쪽은 그대로"
|
|
for chainage in (0.0, 20.0):
|
|
assert not any(key.startswith("slope_end_") for key in rows.get(chainage, {}))
|
|
|
|
|
|
def test_파이썬_짝은_얹은_값을_먼저() -> None:
|
|
section = _section(10.0, False)
|
|
design, samples = section["design"], section["samples"]
|
|
assert final_slope_end(design, samples, 2.5, "left") == pytest.approx(10.0)
|
|
assert final_slope_end({**design, "slope_end_left_m": 4.25}, samples, 2.5, "left") == 4.25
|
|
assert final_slope_end({**design, "slope_end_left_m": None}, samples, 2.5, "left") == (
|
|
pytest.approx(10.0)
|
|
)
|