feat(b08): 구조물도 그림 — 뒷길이 미정 장도 표가 선 기본값으로 그림 · 표 사유와 같은 말
- 뒷길이를 안 고른 돌쌓기 장: 표가 쓴 그 뒷길이(양식 장은 양식이 푼 값, 전개 장은 기본 45㎝)로 그림 - 「⚠ 뒷길이를 안 골라 기본 N㎝ 로 섰습니다」 한 줄을 한 곳에 두고 표 사유·그림이 같이 씀(골막이 문구도 같은 함수) - 돌쌓기 표 사유에 그 한 줄이 새로 붙음 — 전에는 기본값인 줄 표에 안 드러났음 - 그림 까닭은 정말 못 그리는 장(큰돌쌓기 직경 축·옹벽)만 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
This commit is contained in:
@@ -15,7 +15,8 @@
|
||||
터파기 폭 = 평균두께 + 0.2 기초 0.5×0.9 (기초유) · 0.1×0.7 (기초버림)
|
||||
|
||||
⚠ 터파기 치수는 **`common_util_excavation` 한 벌**을 읽는다 — 횡단도가 쓰는 그 상수다.
|
||||
⚠ 뒷길이를 안 정한 장은 **그리지 않고 까닭을 적는다**(브레인 판정 — 빈칸 대신 까닭).
|
||||
⚠ 뒷길이를 안 정한 장은 **표가 선 그 기본값으로 그리고 표 사유와 같은 말을 적는다**
|
||||
(2026-09-14 브레인 판정 — 표가 서면 그림도 섬). 까닭은 정말 못 그리는 장(큰돌쌓기 등)만.
|
||||
|
||||
⚠ **치수가 없는 것은 그리되 치수를 적지 않는다** — 막자갈(뒷채움)은 우리 식이 「입적에서
|
||||
몸통·고임돌을 뺀 것」이라 폭·높이로 정의된 도형이 아니다. 자리만 보이고 값은 표를 가리킨다.
|
||||
@@ -27,7 +28,8 @@ from typing import Any
|
||||
|
||||
from B08_Quantity.B08_Quantity_Engine_StructureSheet import slope_of
|
||||
from B08_Quantity.B08_Quantity_Engine_UnitQuantity import (
|
||||
BACK_LENGTH_KEYS,
|
||||
_back_length,
|
||||
back_length_default_note,
|
||||
wall_thickness,
|
||||
weep_hole_spec,
|
||||
)
|
||||
@@ -64,16 +66,15 @@ _LABEL_SIZE = 4.0 / SCALE_MM_PER_M
|
||||
_DIM_SIZE = 3.4 / SCALE_MM_PER_M
|
||||
|
||||
|
||||
def back_length_cm(sheet: dict[str, Any]) -> float | None:
|
||||
"""장의 뒷길이(㎝). 안 정했으면 `None` — 그때는 그리지 않는다."""
|
||||
def back_length_cm(sheet: dict[str, Any]) -> tuple[int, str | None]:
|
||||
"""(표가 쓴 뒷길이 ㎝, 기본으로 섰으면 표 사유와 같은 한 줄).
|
||||
|
||||
양식 장은 양식이 푼 값(`formula_sheet.vars.L3`), 전개 장은 `_back_length` — 표와 한 벌.
|
||||
"""
|
||||
options = sheet.get("options") or {}
|
||||
raw = next(
|
||||
(options[key] for key in BACK_LENGTH_KEYS if options.get(key) not in (None, "")), None
|
||||
)
|
||||
try:
|
||||
return float(raw) if raw is not None else None
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
solved = ((sheet.get("formula_sheet") or {}).get("vars") or {}).get("L3")
|
||||
back = int(float(solved)) if isinstance(solved, (int, float)) else _back_length(options)
|
||||
return back, back_length_default_note(options, back)
|
||||
|
||||
|
||||
def figure_reason(sheet: dict[str, Any]) -> str | None:
|
||||
@@ -83,8 +84,6 @@ def figure_reason(sheet: dict[str, Any]) -> str | None:
|
||||
return _NO_FIGURE_REASONS.get(type_id, _NO_FIGURE_DEFAULT)
|
||||
if float(sheet.get("height_m") or 0.0) <= 0:
|
||||
return "그림 없음 — 높이가 없습니다(제원에서 높이를 넣으면 그림이 섭니다)"
|
||||
if back_length_cm(sheet) is None:
|
||||
return "그림 없음 — **뒷길이를 고르면 그림이 섭니다**(두께가 뒷길이에서 나옵니다)"
|
||||
return None
|
||||
|
||||
|
||||
@@ -104,7 +103,7 @@ def build_figure(sheet: dict[str, Any]) -> list[dict[str, Any]] | None:
|
||||
return None
|
||||
options = sheet.get("options") or {}
|
||||
height_m = float(sheet.get("height_m") or 0.0)
|
||||
back_cm = back_length_cm(sheet) or 0.0
|
||||
back_cm, default_note = back_length_cm(sheet)
|
||||
slope, slope_note = slope_of(sheet)
|
||||
top_t, bottom_t, _basis = wall_thickness(options, back_cm=back_cm, height_m=height_m)
|
||||
lean = slope * height_m
|
||||
@@ -156,7 +155,11 @@ def build_figure(sheet: dict[str, Any]) -> list[dict[str, Any]] | None:
|
||||
(right, height_m * 0.92),
|
||||
"left",
|
||||
),
|
||||
_text(f"뒷길이 ℓ₃ = {back_cm:g}㎝", (right, height_m * 0.72), "left"),
|
||||
_text(
|
||||
f"뒷길이 ℓ₃ = {back_cm}㎝{' (기본)' if default_note else ''}",
|
||||
(right, height_m * 0.72),
|
||||
"left",
|
||||
),
|
||||
_text("막자갈(뒷채움) — 수량은 아래 표", (right, height_m * 0.55), "left"),
|
||||
_text(
|
||||
f"물구멍 Ø{weep_diameter_mm} — {weep_area_m2:g}㎡당 1개소",
|
||||
@@ -179,6 +182,9 @@ def build_figure(sheet: dict[str, Any]) -> list[dict[str, Any]] | None:
|
||||
_LABEL_SIZE,
|
||||
),
|
||||
]
|
||||
if default_note:
|
||||
# 「이 값이 어디서 왔나」 — 표 사유 줄과 **글자까지 같게**(브레인 판정).
|
||||
shapes.append(_text(default_note, (0.0, -0.80 - base_d), "left"))
|
||||
return shapes
|
||||
|
||||
|
||||
|
||||
@@ -627,6 +627,16 @@ def _back_length(options: dict[str, Any]) -> int:
|
||||
return DEFAULT_BACK_LENGTH_CM
|
||||
|
||||
|
||||
def back_length_default_note(options: dict[str, Any], back_cm: int) -> str | None:
|
||||
"""뒷길이를 안 골라 기본으로 섰을 때의 한 줄 — 표 사유·구조물도 그림이 **같은 말**을 씀."""
|
||||
if any(options.get(key) not in (None, "") for key in BACK_LENGTH_KEYS):
|
||||
return None
|
||||
return (
|
||||
f"⚠ 뒷길이를 안 골라 기본 {back_cm}㎝ 로 섰습니다 "
|
||||
"— 그 값이 두께식·고임돌·돌 무게·채움콘크리트 계수를 모두 가릅니다"
|
||||
)
|
||||
|
||||
|
||||
def _num(value: Any, fallback: float = 0.0) -> float:
|
||||
return float(value) if isinstance(value, (int, float)) else fallback
|
||||
|
||||
@@ -877,6 +887,9 @@ def stone_masonry(
|
||||
f"뒷길이 {back_cm}㎝ 는 품셈 표(25·30·35·45·55·60·75㎝)에 없어 물량이 서지 않습니다"
|
||||
]
|
||||
table = dict(STONE_BACK_LENGTH_TABLE[back_cm])
|
||||
default_note = back_length_default_note(options, back_cm)
|
||||
if default_note:
|
||||
notes.append(default_note)
|
||||
# ⚠ **돌 종류로 계수가 갈린다** (품셈 13-4-3 · 13-4-4 [주]① · 교본 7-3).
|
||||
# 안 고르면 종전 값(건설품셈 참고자료)으로 돌되 그 사실을 알린다 — 값을 못 낸다고
|
||||
# 멈추면 이미 저장된 프로젝트가 통째로 빈다.
|
||||
|
||||
@@ -210,6 +210,7 @@ def erosion_check_dam(
|
||||
STONE_BACK_LENGTH_TABLE,
|
||||
STONE_MASONRY,
|
||||
_back_length,
|
||||
back_length_default_note,
|
||||
fill_concrete_mpa,
|
||||
stone_weight_per_m2,
|
||||
weep_hole_spec,
|
||||
@@ -240,11 +241,9 @@ def erosion_check_dam(
|
||||
coeff = STONE_BACK_LENGTH_TABLE.get(back_cm)
|
||||
if coeff is None:
|
||||
return [], [f"뒷길이 {back_cm}㎝ 는 품셈 표(25·30·35·45·55·60·75㎝)에 없어 계수가 없습니다"]
|
||||
if not any(options.get(key) is not None for key in ("back_len_cm", "stone_back_length_cm")):
|
||||
notes.append(
|
||||
f"⚠ 뒷길이를 안 골라 기본 {back_cm}㎝ 로 섰습니다 "
|
||||
"— 그 값이 두께식·고임돌·돌 무게·채움콘크리트 계수를 모두 가릅니다"
|
||||
)
|
||||
default_note = back_length_default_note(options, back_cm)
|
||||
if default_note:
|
||||
notes.append(default_note)
|
||||
back_m = back_cm / 100.0
|
||||
|
||||
given = _num(options.get("face_slope_ratio"), 0.0)
|
||||
|
||||
@@ -45,9 +45,9 @@ def _sheet(type_id: str, **options) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def test_뒷길이가_없으면_무엇을_고르면_되는지_적는다() -> None:
|
||||
reason = figure_reason(_sheet("masonry_wet"))
|
||||
assert reason is not None and "뒷길이" in reason and "섭니다" in reason
|
||||
def test_뒷길이가_없어도_표처럼_기본값으로_서니_까닭이_없다() -> None:
|
||||
"""2026-09-14 브레인 판정 — 표가 기본 45㎝ 로 서면 그림도 섬(까닭은 정말 못 그리는 장만)."""
|
||||
assert figure_reason(_sheet("masonry_wet")) is None
|
||||
|
||||
|
||||
def test_뒷길이가_있으면_까닭이_없다() -> None:
|
||||
|
||||
@@ -16,6 +16,10 @@ from B08_Quantity.B08_Quantity_Engine_StructureFigure import ( # noqa: E402
|
||||
attach_figures,
|
||||
build_figure,
|
||||
)
|
||||
from B08_Quantity.B08_Quantity_Engine_UnitQuantity import ( # noqa: E402
|
||||
back_length_default_note,
|
||||
stone_masonry,
|
||||
)
|
||||
|
||||
|
||||
def _sheet(**options) -> dict:
|
||||
@@ -65,6 +69,34 @@ def test_못_그리는_장은_그림_대신_까닭이_실린다() -> None:
|
||||
}
|
||||
attach_figures(payload)
|
||||
no_back, boulder, drawn = payload["sheets"]
|
||||
assert no_back["figure"] is None and "뒷길이" in no_back["figure_reason"]
|
||||
# 뒷길이 미정은 기본값이 있어 **그림이 섬**(2026-09-14 브레인 판정) — 까닭은 직경 축만.
|
||||
assert no_back["figure_reason"] is None and no_back["figure"]
|
||||
assert boulder["figure"] is None and "직경" in boulder["figure_reason"]
|
||||
assert drawn["figure_reason"] is None and drawn["figure"]
|
||||
|
||||
|
||||
def _texts(shapes: list[dict]) -> list[str]:
|
||||
return [shape["text"] for shape in shapes if shape["kind"] == "text"]
|
||||
|
||||
|
||||
def test_기본값으로_그린_장은_표_사유와_같은_말을_적는다() -> None:
|
||||
options = {"height_m": 2.0}
|
||||
_rows, table_notes = stone_masonry(2.0, 1.0, options, wet=False)
|
||||
table_default = [note for note in table_notes if "뒷길이를 안 골라" in note]
|
||||
assert table_default == [back_length_default_note(options, 45)]
|
||||
|
||||
texts = _texts(build_figure(_sheet()))
|
||||
assert table_default[0] in texts
|
||||
assert "뒷길이 ℓ₃ = 45㎝ (기본)" in texts
|
||||
# 두께도 기본 45 로 — 상부 0.45+0.30
|
||||
assert "상부 0.75 m" in texts
|
||||
|
||||
chosen = _texts(build_figure(_sheet(back_len_cm=45)))
|
||||
assert "뒷길이 ℓ₃ = 45㎝" in chosen and not any("안 골라" in text for text in chosen)
|
||||
assert back_length_default_note({"back_len_cm": 45}, 45) is None
|
||||
|
||||
|
||||
def test_양식_장은_양식이_푼_뒷길이로_그린다() -> None:
|
||||
sheet = {**_sheet(), "formula_sheet": {"vars": {"L3": 55}}}
|
||||
texts = _texts(build_figure(sheet))
|
||||
assert "뒷길이 ℓ₃ = 55㎝ (기본)" in texts and "상부 0.85 m" in texts
|
||||
|
||||
Reference in New Issue
Block a user