- 돌 무늬: 켜마다 반 칸 엇갈림 · 이웃 · 위아래와 겹침 · 기울기 ±22° 번갈아 교차 · 맹암거 색(카드 · [상세] 모달 같은 그림) - 맹암거 무리 pointer-events none — 성토 띠 · 포장 · 넓은 관 판정면이 맹암거 안에서도 골라짐 - 차례: 보라 선 > 맹암거 테두리 > 구조물 판정면(작은 것 위) > 그림 · 띠 > 맹암거 안쪽 - 시험 test_306_underdrain_pick Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
107 lines
4.0 KiB
Python
107 lines
4.0 KiB
Python
"""PLAN 306 — B06 횡단 맹암거 안 돌 무늬 · 고르기 차례(요구 200-1).
|
|
|
|
- 306-1 돌 무늬 = 켜마다 반 칸 엇갈리고 이웃 · 위아래와 겹친 돌 · 기울기 번갈아 교차 · 그림 전용(누름 안 받음)
|
|
- 306-2 고르기 차례 = 맹암거 안쪽 판정면 맨 아래(겹친 띠 · 관이 위) · 테두리 판정 = 면 판정 맨 위 ·
|
|
보라 선 맨 위 그대로 · 다른 구조물 차례(작은 것 위) 그대로
|
|
|
|
실제 TS 를 `helper_306_underdrain_pick.cjs` 가 Node 로 돌린다.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import re
|
|
import subprocess
|
|
from collections import defaultdict
|
|
|
|
import pytest
|
|
|
|
HERE = os.path.dirname(__file__)
|
|
ROOT = os.path.join(HERE, "..", "..")
|
|
B06 = os.path.join(ROOT, "B06_Section")
|
|
|
|
pytestmark = pytest.mark.skipif(
|
|
not os.path.isdir(os.path.join(ROOT, "config", "node_modules")),
|
|
reason="프론트엔드 의존성 미설치",
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def out() -> dict:
|
|
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
["node", os.path.join(HERE, "helper_306_underdrain_pick.cjs")],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
)
|
|
assert proc.returncode == 0, proc.stderr
|
|
return json.loads(proc.stdout)
|
|
|
|
|
|
def _rows(out: dict) -> list[list[dict]]:
|
|
"""돌을 가로(따라) · 노면 아래 깊이(켜) 좌표로 — 깊이 같은 것끼리 한 켜."""
|
|
(ax, ay), (bx, by) = out["body"][0], out["body"][1]
|
|
rows: dict[float, list[dict]] = defaultdict(list)
|
|
for s in out["stones"]:
|
|
depth = s["cy"] - (ay + (s["cx"] - ax) * (by - ay) / (bx - ax))
|
|
rows[round(depth, 1)].append({**s, "along": s["cx"]})
|
|
return [sorted(rows[k], key=lambda s: s["along"]) for k in sorted(rows)]
|
|
|
|
|
|
def test_돌_무늬_엇갈려_겹침_306_1(out):
|
|
assert out["groupClass"] == "b06-chart__underdrain"
|
|
assert out["stones"] and all(s["cls"] == "b06-chart__culvert-stone" for s in out["stones"])
|
|
rows = _rows(out)
|
|
assert len(rows) == 4 # 높이 1 m ÷ 0.25
|
|
step = rows[0][1]["along"] - rows[0][0]["along"]
|
|
gap = abs(rows[1][0]["along"] - rows[0][0]["along"]) % step
|
|
assert abs(gap - step / 2) < 1e-6 # 이웃 켜는 반 칸 엇갈림
|
|
for row in rows:
|
|
# 돌 길이(따라) > 간격 · 돌 높이(가로질러) > 켜 간격 → 이웃 · 위아래와 겹침
|
|
assert all(s["h"] > step * 1.3 for s in row)
|
|
degs = [s["deg"] for s in row]
|
|
assert all(a != b for a, b in zip(degs, degs[1:])) # 기울기 번갈아 → 교차
|
|
row_gap = 20 / len(rows) # 암거 높이 1 m = 20 px
|
|
assert all(s["w"] > row_gap for s in out["stones"])
|
|
assert len({s["deg"] for s in out["stones"]}) == 2
|
|
|
|
|
|
def test_돌_무늬는_누름_안_받음_306_1():
|
|
with open(os.path.join(B06, "B06_Section_UI_Style_Cross_Areas.css"), encoding="utf-8") as f:
|
|
css = f.read()
|
|
assert re.search(r"\.b06-chart__underdrain \{\s*pointer-events: none;", css)
|
|
assert re.search(r"\.b06-chart__shape-hit--edge \{[^}]*pointer-events: stroke;", css)
|
|
|
|
|
|
def test_고르기_차례_306_2(out):
|
|
# 안쪽 판정면 맨 아래 · 구조물 판정면 넓이 차례(작은 관 위) 그대로 · 테두리 = 면 판정 맨 위 · 보라 선 맨 위
|
|
assert out["order"] == [
|
|
"underdrain",
|
|
"band",
|
|
"g",
|
|
"bigPipe",
|
|
"smallPipe",
|
|
"underdrain+edge",
|
|
"road",
|
|
]
|
|
assert out["edgeCount"] == 1
|
|
|
|
|
|
def test_누른_자리_받는_것_306_2(out):
|
|
old, new = out["probesOld"], out["probesNew"]
|
|
# 옛 차례 — 맹암거 안 성토 띠 · 맹암거보다 넓은 관이 막힘
|
|
assert old["bandInUnderdrain"] == "underdrain" and old["pipeInUnderdrain"] == "underdrain"
|
|
assert new == {
|
|
"bandInUnderdrain": "band",
|
|
"pipeInUnderdrain": "bigPipe",
|
|
"smallPipe": "smallPipe",
|
|
"underdrainOnly": "underdrain",
|
|
"border": "underdrain+edge",
|
|
"road": "road",
|
|
}
|
|
|
|
|
|
def test_테두리_누름은_맹암거_고름_306_2(out):
|
|
assert out["picked"] == ["ud-1"]
|