Files
Aislo/resources/tester/test_288_underdrain_cross.py
T
eomsangdonandClaude Opus 5.5 486616aabd fix(B06 맹암거): 371-3 노면 = 표준 경사 · 암거 경사 = 형상 하단 경계(높은 쪽 노면 끝 − 높이에서 내림)
- 암거 경사가 노면으로 가던 길 끊음(tilt_ranges None ↔ facilityRanges null · Cross_Refresh · design_pipeline_cross) · 측구 없음 · 낮은 쪽 한 경사 그대로
- underdrainOutline 하단 = 높은 쪽 노면 끝 z − H + 암거 경사 · 절토 교점 · 성토 교점 · 돌 띠 따라감 · 경사 비면 옛 동작
- 저장분은 tilt_pct 달라 서버가 다시 셈 · B06 · B07 같은 함수(묶음 다시 지음)
- 시험 test_288 하단 경계 · 349_0n 맹암거 자리 경사 비움

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Se7ytqbohmBQnfYpwYFnYf
2026-10-17 12:21:00 +09:00

190 lines
7.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""288 맹암거 횡단 형상(요구 183-3 · 9).
못박는 것
- 288-1 맹암거 기준 측점 = 측구 없음 · 노면 = 표준 경사로 지대 낮은 쪽 한 경사
(물넘이 286 · 287 길 — 서버 `tilt_ranges` · `tilt_args` · TS 짝 `tilted` · 암거 경사는
노면에 안 감 371-3) · 제원 얹기 서버 `attach_underdrains` ↔ TS `attachUnderdrainSpecs`
같은 값 · 형상 = 노면을 암거 높이만큼 아래로 옵셋 → 성토 쪽은 성토 사면과 만남 ·
절토 쪽은 절토 사면을 늘여 만남 · 그 안 돌 무늬 · 높이 비면 안 그림.
- 371-3 암거 경사 = 형상 하단 경계 — 높은 쪽 노면 끝 − 높이에서 암거 경사로 낮은 쪽으로 내림.
- 288-2 맹암거 형상 누름 = 그 구조물 id 고름(구조물 목록 창구 · 알약 · 폼 · 측점선 따라옴) · 물넘이와 같은
판정면(보라 손잡이 아래 · 끌기 = 고름 아님 · 287-5) · 노면공 포장 범위 고름과 따로.
진짜 TS 는 `helper_288_underdrain.cjs` 가 Node 로 돌림. 화면은 사용자 확인.
"""
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
import pytest
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
import B06_Section.B06_Section_Engine_Tilt as tilt # noqa: E402
from B05_Profile.B05_Profile_Structures_Schema import StructureInstance # noqa: E402
from B06_Section.B06_Section_Engine_Design import compute_cross_design # noqa: E402
STRUCTURES = [
{
"structure_id": "ud-1",
"type_id": "cross_underdrain",
"placement": "point",
"chainage_m": 40.0,
"options": {"culvert_height_m": 0.8, "culvert_slope_pct": 4},
},
{ # 경사 · 높이 빈 맹암거 — 표준 횡단경사 · 형상 없음
"structure_id": "ud-2",
"type_id": "cross_underdrain",
"placement": "point",
"chainage_m": 80.0,
"options": {},
},
{
"structure_id": "x-1",
"type_id": "cross_drain_exposed",
"placement": "point",
"chainage_m": 60.0,
},
]
SECTIONS = [{"chainage_m": c} for c in (20.0, 40.0, 60.0, 80.0)]
pytestmark = pytest.mark.skipif(
not (ROOT / "config" / "node_modules" / "typescript").is_dir(),
reason="프론트엔드 의존성 미설치",
)
@pytest.fixture()
def project(monkeypatch, tmp_path) -> Path:
items = [StructureInstance(**s) for s in STRUCTURES]
monkeypatch.setattr(tilt, "load_structures", lambda _root: (1, items))
monkeypatch.setattr(tilt, "load_culvert_sets", lambda _root: {})
return tmp_path
@pytest.fixture(scope="module")
def ts(tmp_path_factory) -> dict:
source = tmp_path_factory.mktemp("s288") / "in.json"
source.write_text(
json.dumps({"structures": STRUCTURES, "sections": SECTIONS}), encoding="utf-8"
)
proc = subprocess.run(
["node", str(HERE / "helper_288_underdrain.cjs"), str(source)],
capture_output=True,
text=True,
encoding="utf-8",
timeout=60,
)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
def test_맹암거_자리_기울기_서버(project):
ranges = tilt.tilt_ranges(project)
# 371-3 — 암거 경사(4 %)는 노면에 안 감 · 노면 = 표준 경사
assert ranges == [(40.0, 40.0, None), (80.0, 80.0, None)]
assert tilt.tilt_args(40.0, ranges) == {"tilted": True, "tilt_pct": None}
assert tilt.tilt_args(80.01, ranges) == {"tilted": True, "tilt_pct": None}
assert tilt.tilt_args(60.0, ranges) == {"tilted": False, "tilt_pct": None}
def test_제원_얹기_서버_TS_같은_값(project, ts):
sections = [dict(s) for s in SECTIONS]
assert tilt.attach_underdrains(project, sections) == 2 == ts["attached"]
assert [s.get("underdrain") for s in sections] == ts["sections"]
assert ts["sections"][1] == {
"structure_id": "ud-1",
"chainage_m": 40.0,
"height_m": 0.8,
"slope_pct": 4.0,
}
def test_맹암거_측점_설계_측구_없음_낮은_쪽_기울기():
samples = [
{"offset_m": o / 2, "elevation_m": round(100.0 + 0.35 * o / 2, 4), "valid": True}
for o in range(-24, 25)
]
kwargs = {"ground_type": "soil", "section_mode": "left_cut", "ditch_side": "left"}
got = compute_cross_design(samples, 100.4, tilted=True, tilt_pct=None, **kwargs)
standard = compute_cross_design(samples, 100.4, **kwargs)["cross_slope_pct"]
assert got["ditch_enabled"] is False and got["cross_slope_pct"] == standard
left, right = (got["road_edges"][k]["elevation_m"] for k in ("left", "right"))
assert left > right # 지반이 좌로 오름 = 우가 낮음
def test_형상_옵셋선_성토_절토와_만남(ts):
outline = [(p["offset"], p["elevation"]) for p in ts["outline"]]
expected = [
(3, 100.15), # 좌 노면 끝
(-3, 99.85), # 우 노면 끝
(-4.632653, 98.768367), # 우 성토 사면 ∩ 옵셋선
(1.846154, 99.092308), # 좌 절토 사면을 늘인 선 ∩ 옵셋선
]
assert len(outline) == len(expected)
for got, want in zip(outline, expected):
assert got == pytest.approx(want, abs=1e-5)
assert ts["noHeight"] is None
assert ts["drew"] is True and ts["stones"] > 0
def test_하단_경계는_암거_경사(ts):
"""371-3 — 노면 5 % · 암거 3 % · 높이 1 m: 하단 = 좌(높은 쪽) 끝 99.15 에서 3 % 로 우로 내림."""
outline = [(p["offset"], p["elevation"]) for p in ts["sloped"]]
expected = [
(3, 100.15),
(-3, 99.85),
(-4.391304, 98.928261), # 우 성토 사면 ∩ 하단(99.15 − 0.03 × 7.391)
(1.872180, 99.116165), # 좌 절토 사면을 늘인 선 ∩ 하단
]
assert len(outline) == len(expected)
for got, want in zip(outline, expected):
assert got == pytest.approx(want, abs=1e-5)
def test_양측절토_늘인_노면_원지반_교점에서_끊음(ts):
"""356-1 — 낮은 쪽 노면 끝도 절토(tilt_reach) = reach 에서 수직으로 끊음 · 너머 원지반 따라가지 않음."""
outline = [(p["offset"], p["elevation"]) for p in ts["bothCut"]]
expected = [
(3, 100.15),
(-3, 99.85),
(-4, 99.8),
(-4.5, 99.775), # 늘인 노면 ∩ 원지반(tilt_reach)
(-4.5, 98.775), # reach 아래 옵셋선
(1.846154, 99.092308),
]
assert len(outline) == len(expected)
for got, want in zip(outline, expected):
assert got == pytest.approx(want, abs=1e-5)
def test_횡단도_B07_이_맹암거를_그림():
view = (ROOT / "B06_Section" / "B06_Section_UI_Cross_View.ts").read_text(encoding="utf-8")
assert (
"appendUnderdrainOverlay(plotLayer, section.underdrain, section.design, x, toDisplayY);"
in view
)
b07 = (ROOT / "B07_DesignDetail" / "B07_DesignDetail_Cross_Overlay.ts").read_text(
encoding="utf-8"
)
assert "appendUnderdrainOverlay(svg, section.underdrain, design, x, y);" in b07
refresh = (ROOT / "B06_Section" / "B06_Section_Cross_Refresh.ts").read_text(encoding="utf-8")
assert "tilted: Boolean(section.ford_pavement || section.underdrain)," in refresh
def test_맹암거_형상_누름은_그_구조물_고름(ts):
assert ts["bodyId"] == "ud-1" # 둘레 = 형상 고름 표식
assert ts["routed"] == [["pipe"], [ts["eventName"], "ud-1", True]]
view = (ROOT / "B06_Section" / "B06_Section_UI_Section_View.ts").read_text(encoding="utf-8")
assert "grid.addEventListener(SHAPE_PICK_EVENT, (event) => {" in view
assert "if (id) pavementSelectListener?.(id);" in view # = structuresPanel.selectStructure(id)
cross = (ROOT / "B06_Section" / "B06_Section_UI_Cross_View.ts").read_text(encoding="utf-8")
assert 'revetOffset && shapePickRoute(svg, (at) => toggleRevet("pipe", at)),' in cross