feat(B06): 소단 = 좌측 깊이 칸 숨김 · 횡단도 소단 고름 ▲▼ 만 · 측점 · 쪽마다 깊이 patch · 고름 조각 키 · 좌측 소단 상자 켬(194-1 · 194-2)

- 정의 「상세 내용」 = 폭만 · depth_m 표시 끔(옛 구간 값 = 기본값으로 읽음 · 새 기본 1 m)
- 짝: BermSpec 쪽마다 깊이 · side_berm ↔ sideBerm · 설계 결과 berm_depth_{side}_m 되싣기(파이썬 ↔ TS)
- 저장: patch berm_depth_{side}_m(≥ 0) · 재계산 살림 목록 짝(RD ↔ CR) · 세션 bermdepth
- 서버 [저장] 다시 셈 = 구조물 정본 소단 구간(load_berm_spans) + 측점 깊이 · 구간 지우면 소단 없음
- 고름: 소단 수평 단 = 높이만(▲ 0 에서 멈춤 · ▼ 사면 끝 앞 멈춤 · ◀▶ 막힘) · 사면 = 가로 · 노면 = 종단
- 고름을 조각 키(갈래 + 쪽 + 차례)로 기억 — 옮겨 다시 그려도 같은 조각
- 새 시험 test_194_1_berm_depth + helper · test_178_2 · test_54_26 고침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3mdb91125mPoC6tpsQx4P
This commit is contained in:
2026-10-10 13:59:43 +09:00
co-authored by Claude Opus 5.5
parent e442777cc8
commit cb7a140c10
24 changed files with 1090 additions and 69 deletions
+28 -10
View File
@@ -160,6 +160,9 @@ USER_TOUCHED_KEYS = (
# 설계선 쉬프트 = 쪽마다 노견 확장(139-6) — 기하 입력(`stored_shoulder_extend`)
"shoulder_extend_left_m",
"shoulder_extend_right_m",
# 소단 쪽마다 옮긴 깊이(194-1 · 횡단도 ▲▼) — 기하 입력(`stored_berm`)
"berm_depth_left_m",
"berm_depth_right_m",
)
# 노선 [확인] 체인이 옛 노선에서 누가거리로 넘기는 **사용자 입력**(PLAN 74-3) — 계획고 · 면적 ·
@@ -256,14 +259,20 @@ def enforce_design_lines(
받아 적었다(CLAUDE.md 5장 「[저장]은 서버가 정본으로 다시 계산」 어긋남 · B07 이 저장된 선을
그림). 입력 = 저장분(지반유형 · 단면유형 · 측구 · 포장 · 암 경계 · 암 절토 경사 · 소단 · 노견
확장) + 지금 종단 계획고 · 세월교 월류 하강 · 곡선부 확폭. 사용자 조작값은 승계.
소단 제원은 구조물 정본(`structures.json`)의 구간이 원천 — 쪽마다 옮긴 깊이는 측점 patch(194-1).
"""
from B06_Section.B06_Section_Engine_Structures_Wall import berm_span_at, load_berm_spans
ford_drops = ford_surface_drops(project_root)
spans = load_berm_spans(project_root)
changed = 0
for section in cross_sections:
design = section.get("design")
if not isinstance(design, dict):
continue
chainage = float(section.get("chainage_m", 0.0))
if spans is not None: # 구간을 지웠으면 소단 없음 · 못 읽으면 저장분 그대로
design = {**design, "berm": berm_span_at(spans, chainage)}
try:
recomputed = compute_cross_design(
section.get("samples", []),
@@ -449,22 +458,34 @@ def stored_cut_slope(stored: dict[str, Any]) -> float | None:
return None
def stored_berm(stored: dict[str, Any]) -> BermSpec | None:
"""저장분에 남은 소단 제원 — 세션값이 없을 때 쓴다(확정 뒤·다른 PC)."""
spec = stored.get("berm")
def berm_from(spec: Any, sides: dict[str, Any]) -> BermSpec | None:
"""소단 제원 dict + 쪽마다 옮긴 깊이(`berm_depth_{side}_m` · 194-1) → BermSpec."""
if not isinstance(spec, dict):
return None
def own(side: str) -> float | None:
value = sides.get(f"berm_depth_{side}_m")
ok = isinstance(value, (int, float)) and not isinstance(value, bool)
return max(float(value), 0.0) if ok else None
try:
return BermSpec(
width_m=float(spec.get("width_m", BERM_DEFAULT_WIDTH_M)),
interval_m=float(spec.get("interval_m", BERM_DEFAULT_INTERVAL_M)),
slope_deg=float(spec.get("slope_deg", BERM_DEFAULT_SLOPE_DEG)),
depth_m=max(float(spec.get("depth_m", BERM_DEFAULT_DEPTH_M)), 0.0),
depth_left_m=own("left"),
depth_right_m=own("right"),
)
except (TypeError, ValueError):
return None
def stored_berm(stored: dict[str, Any]) -> BermSpec | None:
"""저장분에 남은 소단 제원 + 쪽마다 옮긴 깊이 — 세션값이 없을 때 쓴다(확정 뒤·다른 PC)."""
return berm_from(stored.get("berm"), stored)
def recompute_designs_for_alignment(
longitudinal: dict[str, Any],
cross_sections: list[dict[str, Any]],
@@ -500,13 +521,10 @@ def recompute_designs_for_alignment(
session_berms: dict[float, BermSpec] = {}
for raw_key, spec in (berms or {}).items():
try:
session_berms[round(float(raw_key), 3)] = BermSpec(
width_m=float(spec.get("width_m", BERM_DEFAULT_WIDTH_M)),
interval_m=float(spec.get("interval_m", BERM_DEFAULT_INTERVAL_M)),
slope_deg=float(spec.get("slope_deg", BERM_DEFAULT_SLOPE_DEG)),
depth_m=max(float(spec.get("depth_m", BERM_DEFAULT_DEPTH_M)), 0.0),
)
except (TypeError, ValueError, AttributeError):
parsed = berm_from(spec, spec if isinstance(spec, dict) else {}) # 쪽 깊이도(194-1)
if parsed is not None:
session_berms[round(float(raw_key), 3)] = parsed
except (TypeError, ValueError):
continue
for section in cross_sections:
chainage = float(section.get("chainage_m", 0.0))