Files
Aislo/resources/tester/test_b06_wall_shape_unified.py
T
eomsangdonandClaude Opus 5.5 6ba26ee61c fix(B06 벽): 371-1 성토 위 이음 = 윗면 사면 쪽 끝에서 0.1 m · 다단 사이 시작 = 윗단 발끝 · 기본 자리 새 이음 기준
- 공용 벽 fillTop 칸 · 성토 사면선 · 트림 · 3D 실루엣 목표 topJoint → fillTop(무늬 이음선 그대로)
- 0점 · 다단 자동 자리 · 최소 노견 자리 = 배면에서 1.5t − 0.1(노폭 연장 0 유지 · 관 길이 7.9 → 7.5 m)
- 다단 사이 = 늘 윗단 발끝(원지반 무관) · 끝 구간 · 관 유출 벽 = 발끝이 지반 위에 뜬 벽만 발끝
- 시험 186_1 · 139_6 · 225_1 · 139_4 새 수치 · wall_shape_unified 이음 0.1 m 확인

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

107 lines
5.3 KiB
Python

"""횡단 벽 형상 한 벌 — 관 유입·유출 흙막이 · 추가 흙막이 · 사면안정 흙막이 · 옹벽.
규칙(10-6 사용자):
① 네 벽 모두 공용 `buildRevetWallGeometry` 로 선다 — 형태는 안 무늬만 바꿈(외곽 같음).
② 형태별 높이 한도(메 2.0 · 찰 3.0 · 콘크리트 5.0) · 원지반 맞춤 자동 자르기 없음.
③ 벽 윗면은 노면 위로 올라오지 않음(넘으면 노면에서 멈춤).
서버 면적은 같은 TS 를 Node 로 돌리므로(`B06_Section_Server_Calc_Node.ts`) 이 함수가 곧 서버 기하.
"""
import json
import re
import subprocess
from pathlib import Path
import pytest
PROJECT_ROOT = Path(__file__).resolve().parents[2]
B06 = PROJECT_ROOT / "B06_Section"
TSC = PROJECT_ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
_RUNNER = """
import { writeFileSync } from "node:fs";
import { buildRevetWallGeometry } from "./B06_Section_UI_Cross_Wall.js";
import { revetWallSpec, REVET_FORMS } from "./logic/B06_Section_UI_Cross_Culvert_Const.js";
const anchor = { offset: 5, elevation: 100 };
const outline = (form, role, extra = {}) =>
buildRevetWallGeometry({ anchor, outward: 1, height: 2.5, material: "dry", role, form, ...extra })
.points.map((p) => [p.offset, p.elevation]);
writeFileSync(process.argv[2], JSON.stringify({
forms: REVET_FORMS.map((form) => outline(form, "outlet")),
roles: ["inlet", "outlet", "extra"].map((role) => outline("돌쌓기(메)", role)),
capped: buildRevetWallGeometry({
anchor, outward: 1, height: 5, material: "dry", role: "outlet", roadElevation: 103,
}).topJoint.elevation,
under: buildRevetWallGeometry({
anchor, outward: 1, height: 2, material: "dry", role: "outlet", roadElevation: 103,
}).topJoint.elevation,
fill: (({ fillTop, topJoint, points }) => [fillTop.offset, fillTop.elevation, topJoint.offset, points[2].offset, points[2].elevation])(
buildRevetWallGeometry({ anchor, outward: 1, height: 2.5, material: "dry", role: "outlet" }),
),
dry: revetWallSpec({ revet_form: "돌쌓기(메)" }, { h: 3.0 }, true, 0.3).pureHeight,
concrete: revetWallSpec({ revet_form: "콘크리트" }, { h: 7.5 }, true, 0.3).pureHeight,
}));
"""
@pytest.mark.skipif(not TSC.is_file(), reason="프론트엔드 의존성 미설치")
def test_벽_외곽_한_벌_높이_입력_그대로_노면_멈춤(tmp_path: Path) -> None:
out = tmp_path / "js"
# 형(type) import 가 끌어오는 페이지 파일은 별칭을 못 풀어 오류를 내지만 산출물은 나옴.
subprocess.run( # noqa: S603 — 고정 실행 파일
[
"node",
str(TSC),
str(B06 / "B06_Section_UI_Cross_Wall.ts"),
"--outDir",
str(out),
"--module",
"esnext",
"--target",
"es2022",
"--moduleResolution",
"bundler",
"--skipLibCheck",
"--ignoreConfig",
],
cwd=str(PROJECT_ROOT),
capture_output=True,
)
for emitted in out.rglob("*.js"):
emitted.write_text(
re.sub(r'(from\s+"\./[^"]+)"', r'\1.js"', emitted.read_text(encoding="utf-8")),
encoding="utf-8",
)
base = next(path.parent for path in out.rglob("B06_Section_UI_Cross_Wall.js"))
(base / "runner.mjs").write_text(_RUNNER, encoding="utf-8")
result = tmp_path / "out.json"
subprocess.run( # noqa: S603
["node", str(base / "runner.mjs"), str(result)], check=True, capture_output=True
)
got = json.loads(result.read_text(encoding="utf-8"))
assert all(outline == got["forms"][0] for outline in got["forms"]), "형태마다 외곽이 다름"
assert all(outline == got["roles"][0] for outline in got["roles"]), "벽 갈래마다 외곽이 다름"
assert got["capped"] == pytest.approx(103.0), "노면 위로 올라옴"
assert got["under"] == pytest.approx(102.0), "노면 아래 벽이 잘림"
assert got["dry"] == pytest.approx(3.0), "돌쌓기(메) 3.0 이 잘림"
assert got["concrete"] == pytest.approx(7.5), "콘크리트 7.5 가 잘림"
# 371-1 — 성토 위 이음 = 윗면 사면 쪽 끝에서 0.1 m 안 · 무늬 이음선(topJoint)은 그대로
fill_offset, fill_elevation, joint_offset, front_offset, front_elevation = got["fill"]
assert fill_offset == pytest.approx(front_offset - 0.1), "성토 위 이음이 끝에서 0.1 m 아님"
assert fill_elevation == pytest.approx(front_elevation), "성토 위 이음이 윗면 표고 아님"
assert joint_offset == pytest.approx(front_offset - 0.45), "무늬 이음선이 옮겨짐"
def test_네_벽이_공용_함수로_서고_형태_한도가_없다() -> None:
for name in ("Culvert_Geom", "Culvert_Extra", "Revetment"):
source = (B06 / f"B06_Section_UI_Cross_{name}.ts").read_text(encoding="utf-8")
assert "buildRevetWallGeometry(" in source, f"{name} 가 공용 벽 함수를 안 씀"
for path in B06.rglob("*.ts"): # logic/ 까지(348-1)
source = path.read_text(encoding="utf-8")
assert "revetHeightLimit" not in source, f"{path.name} 에 형태 높이 한도가 남음"
assert "materialLimit" not in source, f"{path.name} 에 재질 높이 한도가 남음"
extra = (B06 / "B06_Section_UI_Cross_Culvert_Extra.ts").read_text(encoding="utf-8")
assert "fitted" not in extra, "추가 흙막이가 원지반에 맞춰 늘어남"