feat(b05,b06): 사면안정 흙막이 · 옹벽 추가 단 상자 · 단마다 형태 · 높이 횡단 형상 (178-3)

- 추가 단 상자 = 유출 흙막이 상자 양식 · 공용 [추가] · [빼기] · 흙막이 형태 · 높이 · 옹벽 높이(콘크리트) · 키 tier_{n}_*
- 정의 그룹 종류 덧붙임 명세(ADDON_SPECS) — 좌측 폼만 · 그룹 상자 · 목록 · M02 그대로
- 벽 제원 짝(TS tierExtras · 파이썬 tier_extras) · 단 수 = 1 + 상자 수 · 횡단 단 조정 m · h
- 새 시험 test_178_3_slope_tiers · walls_mirror extras 대조

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sDZSjFvoiDjdGpgj3muc3
This commit is contained in:
2026-10-10 12:02:54 +09:00
co-authored by Claude Opus 5.5
parent f60657c8b9
commit a8a1471d71
8 changed files with 390 additions and 8 deletions
@@ -17,6 +17,8 @@
"""
import logging
import math
import re
from pathlib import Path
from typing import Any
@@ -39,6 +41,31 @@ _FORM_BY_TYPE = {
}
# 좌측 폼 「추가 단」 상자 저장 키 머리(178-3) — `tier_{n}_form` · `tier_{n}_height`.
_TIER_KEY = re.compile(r"^tier_(\d+)_")
def _num(value: Any) -> float | int | None:
"""유한한 수만(TS `num` 짝 — 글 · 참거짓은 None)."""
if isinstance(value, bool) or not isinstance(value, (int, float)):
return None
return value if math.isfinite(value) else None
def tier_extras(type_id: str, options: dict[str, Any]) -> list[dict[str, Any]]:
"""「추가 단」 상자 → 추가 단 [{형태 · 높이}](n 차례) — 옹벽 = 콘크리트 · TS `tierExtras` 짝."""
ns = sorted({int(m.group(1)) for key in options if (m := _TIER_KEY.match(key))})
return [
{
"form": "콘크리트"
if type_id == "retaining_wall"
else (options.get(f"tier_{n}_form") or None),
"height_m": _num(options.get(f"tier_{n}_height")),
}
for n in ns
]
# 소단 타입 id — 레지스트리와 한 벌이다(C군이지만 벽이 아니다).
BERM_TYPE_ID = "berm"
@@ -60,6 +87,7 @@ def load_wall_structures(project_root: Path) -> list[dict[str, Any]]:
if start is None or end is None:
continue
options = structure.options or {}
extras = tier_extras(structure.type_id, options)
found.append(
{
"structure_id": structure.structure_id,
@@ -76,7 +104,9 @@ def load_wall_structures(project_root: Path) -> list[dict[str, Any]]:
# 기초 축(기초유/기초버림) — 터파기 그림·물량이 같이 쓴다.
# 비어 있으면 화면이 터파기를 안 그린다(근거 없음).
"foundation": options.get("foundation"),
"tiers": options.get("tiers"),
# 단 수 = 1 + 추가 단 상자 수 · 상자 없으면 옛 `tiers`(178-3)
"tiers": len(extras) + 1 if extras else options.get("tiers"),
"extras": extras,
"lift_m": options.get("lift_m"),
"shift_m": options.get("shift_m"),
# 전면 기울기를 B08 과 같은 칸으로 가르는 값 — 저장 원본(2026-09-14 B5 · TS 짝 같은 키).