"""구조물도 상단 그림 — 그림 치수가 **수량표와 같은 한 벌**인가 (PLAN 12장). ⚠ 옛 B07 그림은 두께 식을 따로 적어, 상부·하부 두께를 넣어도 **그림만 옛 두께**였음. """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) 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: return { "title": "돌쌓기(메)", "type_id": "masonry_dry", "height_m": 2.0, "options": {"height_m": 2.0, **options}, } def _paths(shapes: list[dict]) -> list[dict]: return [shape for shape in shapes if shape["kind"] == "path"] def _area(points: list[list[float]]) -> float: return abs(sum(x0 * y1 - x1 * y0 for (x0, y0), (x1, y1) in zip(points, points[1:]))) / 2 def test_벽_두께는_사용자가_넣은_값을_따른다() -> None: wall = _paths(build_figure(_sheet(back_len_cm=35, face_slope_ratio=0.3)))[0]["points"] # 식: 상부 0.35+0.30=0.65 · 하부 0.65+0.30×(2−1)=0.95 · 기울기 0.3 → 앞면 위 끝 0.6 assert wall[:4] == [[0.0, 0.0], [0.6, 2.0], [1.25, 2.0], [0.95, 0.0]] given = _paths( build_figure( _sheet( back_len_cm=35, face_slope_ratio=0.3, thickness_top_m=0.5, thickness_bottom_m=1.2 ) ) )[0]["points"] assert given[2] == [1.1, 2.0] and given[3] == [1.2, 0.0] def test_터파기_넓이는_수량식과_같다() -> None: dig = _paths(build_figure(_sheet(back_len_cm=35, face_slope_ratio=0.3)))[1] assert dig["dash"] assert abs(_area(dig["points"]) - 2.0 * ((0.65 + 0.95) / 2 + 0.2)) < 1e-9 def test_못_그리는_장은_그림_대신_까닭이_실린다() -> None: payload = { "sheets": [ _sheet(), {**_sheet(stone_cm="60~80"), "type_id": "boulder_masonry"}, _sheet(back_len_cm=45), ] } attach_figures(payload) no_back, boulder, drawn = payload["sheets"] # 뒷길이 미정은 기본값이 있어 **그림이 섬**(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