feat(B06): 세월교 · 박스 흙막이 횡단 그림 — 관보호공 흙막이와 같은 벽 · 자리 · 연동 · 경사(73-4)

- 유입 · 유출 구조 「흙막이」면 배관 흙막이와 같은 세트(관 숨김 · body)를 같은 측점 culvert 자리에 함께 얹음(파이썬 _guard_set ↔ TS guardSet 짝 · 거울 시험)
- 자동 자리 = 벽 전면이 관(세월교) · 본체(박스) 끝 · 좌우 x 는 그 자리 기준(본체를 늘리면 벽도 따라감) · 상하 d 는 배관과 같음
- 연동 옆 측점 · 3D 연동 해제 자리는 본체 기준 x 를 절대값으로 넘겨 받음 · 3D 만료 해시에 흙막이 값 포함
- 설계선 트림 = 벽 선 쪽은 흙막이 · 안 선 쪽은 본체 · 벽 선 쪽 본체 성토선은 걷음(두 줄 방지) · 관 연장은 안 실음
- 좌측 폼 → 캐시: 세트 다시 짓기 · 높이 · 형태 · 추가 흙막이 상자 수 = 유출 다단 수
- [연동][경사] 회색 풀림(옆 측점에 벽이 서면 연동 · 구간 있으면 경사) — 배관과 같은 규칙
- 시험: test_73_4_body_guard(벽 전면 = 본체 끝 · 박스 +1 m 따라감 · 연동 옆 측점 벽 · 서버 Node 면적 = 화면) · 거울 시험 흙막이 칸

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYJJRCfHfdCGNhmXTjQKV7
This commit is contained in:
2026-10-07 08:23:48 +09:00
co-authored by Claude Opus 5.5
parent 5e3ad45248
commit ffbf772f3c
18 changed files with 619 additions and 52 deletions
+48 -2
View File
@@ -315,6 +315,36 @@ def _revet_set(options: dict[str, Any] | None) -> dict[str, Any]:
}
# 세월교 · 박스 유입 · 유출 구조 칸(73-4) — 「흙막이」면 배관 흙막이와 같은 세트를 함께 얹음.
_GUARD_KEYS = {"box": ("wing_in", "wing_out"), "ford": ("inlet_type", "outlet_type")}
def _guard_set(values: dict[str, Any], body: str, diameter_m: float) -> dict[str, Any] | None:
"""세월교 · 박스 흙막이 — 독립 기슭막이와 같은 세트 모양(관 숨김) · 자리 = 관 · 본체 끝(73-4).
같은 측점에 `culvert` 키로 함께 얹혀 배관 흙막이 길(그림 · 연동 · 경사 · 3D · 면적)을 그대로 탐.
흙막이를 안 고른 쪽 = 구조 빈 값(안 그림) · 둘 다 아니면 None. TS 짝 `guardSet`.
"""
sides: dict[str, Any] = {}
for role, key in zip(("inlet", "outlet"), _GUARD_KEYS[body]):
guarded = str(values.get(key) or "").strip() == "흙막이"
sides[role] = _revet_side(values, role) if guarded else {"role": role, "structure": ""}
if not any(side["structure"] for side in sides.values()):
return None
return {
"type": "pipe",
"hidden_pipe": True,
"body": body,
"side": "양쪽",
"tiers": 1,
"foundation": None,
"pipe_kind": None,
"diameter_m": diameter_m,
"min_cover_m": 0.0,
**sides,
}
def _wing_spec(values: dict[str, Any], defaults: dict[str, Any], side: str) -> dict[str, Any]:
"""날개벽 한쪽 제원 + 그 각도가 만드는 바닥판 연장량.
@@ -404,7 +434,7 @@ def _ford_set(options: dict[str, Any] | None) -> dict[str, Any]:
("ford_height_m", "월류 높이", 0.0),
],
)
return {
spec = {
"type": "ford",
"pipe_kind": str(kind) if kind else None,
"diameter_m": round((diameter_mm or 1000.0) / 1000.0, 3),
@@ -423,6 +453,10 @@ def _ford_set(options: dict[str, Any] | None) -> dict[str, Any]:
+ _wing_defaulted(values, wing_in, "in")
+ _wing_defaulted(values, wing_out, "out"),
}
guard = _guard_set(values, "ford", spec["diameter_m"])
if guard:
spec["guard"] = guard
return spec
def _ford_pavement_set(options: dict[str, Any] | None) -> dict[str, Any]:
@@ -474,7 +508,7 @@ def _box_set(options: dict[str, Any] | None) -> dict[str, Any]:
slab = slab if slab and slab > 0 else FORD_SLAB_THICKNESS_M
wing_in = _wing_spec(values, defaults, "in")
wing_out = _wing_spec(values, defaults, "out")
return {
spec = {
"type": "box",
"inner_width_m": inner_width or 2.0,
"inner_height_m": inner_height or 2.0,
@@ -496,6 +530,15 @@ def _box_set(options: dict[str, Any] | None) -> dict[str, Any]:
+ _wing_defaulted(values, wing_in, "in")
+ _wing_defaulted(values, wing_out, "out"),
}
guard = _guard_set(values, "box", box_body_height(spec))
if guard:
spec["guard"] = guard
return spec
def box_body_height(box: dict[str, Any]) -> float:
"""박스 본체 전체 높이(상판 + 내공 + 저판) — 흙막이 세트의 관경 자리(73-4)."""
return round(box["top_thickness_m"] + box["inner_height_m"] + box["slab_thickness_m"], 3)
def load_culvert_sets(project_root: Path) -> dict[float, dict[str, Any]]:
@@ -578,6 +621,9 @@ def attach_culvert_sets(project_root: Path, cross_sections: list[dict[str, Any]]
**spec,
"chainage_m": pipe_chainage,
}
# 세월교 · 박스 흙막이(73-4) — 배관 흙막이와 같은 자리(`culvert`)에 함께 얹음.
if spec.get("guard"):
section["culvert"] = {**spec["guard"], "chainage_m": pipe_chainage}
attached += 1
break
return attached