# -*- coding: utf-8 -*- """180-2 B07 횡단 장 — 표 · 표 위 글 배율(모든 장 한 값 · 0.5~2.0 · 기본 1). 못박는 것 - 배율 1.5 = 표 폭 162 · 높이 60 · 글 줄 높이 × 1.5 · 표 아래선은 칸 아래 + 여백 그대로. - 그림 아래 끝 = 글(없으면 표) 위 8 mm(간격은 배율 안 탐) · 넓은 표가 칸을 넘으면 overflow_cells. - 배율 1 = 배율 칸 없는 배치와 같은 엔티티. - cross_layout.json 이 배율을 지킴 · 범위 밖 = 거절 · 배율만 바꾸면 확정 장 안 풂. - 지금과 다른 배율로 확정된 장 = 그대로 + stale_scale 안내. """ import json from pathlib import Path import pytest from B07_DesignDetail.B07_DesignDetail_Engine_Cad import DRAWING_FORMAT, GROUND_COLOR from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Sheet import ( CELL_TABLE_PAD_MM, build_cross_sheet, plan_cross_sheets, ) from B07_DesignDetail.B07_DesignDetail_Engine_Template import ( entities_bbox, usable_area, use_project_templates, ) from B07_DesignDetail.B07_DesignDetail_Router_Support import ( CROSS_SHEET_LAYOUT, _cross_sheet_plan, _read_drawing, _read_manifest, _write_manifest, read_cross_layout, write_cross_layout, ) NOTE = {"structure_note": "L형 측구\n집수정 보강"} def _source(chainage: float, lo: int = -5, hi: int = 5) -> dict: return { "chainage_m": chainage, "samples": [ {"valid": True, "offset_m": float(x), "elevation_m": 100.0 + 0.05 * x} for x in range(lo, hi + 1) ], } def _drawing(layout: dict, designs: list[dict | None]) -> tuple[dict, dict]: use_project_templates(None) sources = [_source(i * 20.0) for i in range(len(designs))] sheet = plan_cross_sheets([s["chainage_m"] for s in sources], layout)[0] sections = [ { "chainage": s["chainage_m"], "source": s, "design": design, "quantity_table": {"ground": 1.0, "planned": 0.5}, "title": "No.0", } for s, design in zip(sources, designs, strict=True) ] return sheet, build_cross_sheet(sheet, sections) def _tables(drawing: dict) -> list[dict]: return [e for e in drawing["entities"] if e["type"] == "Table"] def test_배율_15_표_크기와_쌓기() -> None: sheet, drawing = _drawing({"default": [2, 2], "table_scale": 1.5}, [NOTE, None]) assert sheet["table_scale"] == 1.5 _w, h = usable_area() tables = _tables(drawing) for table, slot in zip(tables, sheet["slots"], strict=True): shape = table["shapeData"] assert sum(shape["columnWidths"]) == pytest.approx(162.0) assert sum(shape["rowHeights"]) == pytest.approx(60.0) bottom = shape["origin"]["y"] - sum(shape["rowHeights"]) assert bottom == pytest.approx(slot[1] - h / 4 + CELL_TABLE_PAD_MM) # 표 아래선 그대로 grounds = [ entities_bbox([e]) for e in drawing["entities"] if e.get("type") == "PolyLine" and e.get("lineColor") == GROUND_COLOR ] # 글 2 줄 × 3.0 × 1.5 = 9 mm · 간격 8 mm 는 배율 안 탐 tops = [t["shapeData"]["origin"]["y"] for t in tables] assert grounds[0][1] == pytest.approx(tops[0] + 9.0 + 8.0) assert grounds[1][1] == pytest.approx(tops[1] + 8.0) def test_배율_1_은_지금과_같음() -> None: _s, plain = _drawing({"default": [2, 2]}, [NOTE, None]) _s, one = _drawing({"default": [2, 2], "table_scale": 1.0}, [NOTE, None]) assert one["entities"] == plain["entities"] assert sum(_tables(one)[0]["shapeData"]["columnWidths"]) == pytest.approx(108.0) def test_넓은_표_넘침() -> None: w, _h = usable_area() # 4 × 4 칸 폭 192.5 — 표 108 × 2.0 = 216 이 칸을 넘음 _s, drawing = _drawing({"default": [4, 4], "table_scale": 2.0}, [None]) assert w / 4 < 216.0 assert len(drawing["overflow_cells"]) == 1 _s, drawing = _drawing({"default": [4, 4], "table_scale": 1.0}, [None]) assert drawing["overflow_cells"] == [] def _project(tmp_path: Path, count: int) -> tuple[Path, Path]: root = tmp_path / "project" cross_dir = root / "B06_Section" / "cross_sections" cross_dir.mkdir(parents=True) longitudinal_path = root / "B06_Section" / "longitudinal" / "longitudinal.json" longitudinal_path.parent.mkdir(parents=True) stations = [] for i in range(count): chainage = i * 20 stations.append({"chainage_m": float(chainage), "center_x": 0.0, "center_y": 0.0}) (cross_dir / f"cross_{chainage:05d}m.json").write_text( json.dumps(_source(float(chainage))), "utf-8" ) longitudinal_path.write_text(json.dumps({"stations": stations}), "utf-8") return root, longitudinal_path def test_배치_파일_배율_저장_범위(tmp_path: Path) -> None: root, longitudinal_path = _project(tmp_path, 5) ids = [s["id"] for s in _cross_sheet_plan(root, longitudinal_path)] manifest = _read_manifest(root) manifest["drawings"] = {i: {"confirmed": True} for i in ids} _write_manifest(root, manifest) # 배율만 바꿈 — 담긴 측점 그대로라 확정 장 안 풂 assert ( write_cross_layout(root, longitudinal_path, {"default": [4, 4], "table_scale": 1.3}) == [] ) assert read_cross_layout(root)["table_scale"] == 1.3 assert _read_manifest(root)["drawings"][ids[0]]["confirmed"] is True assert _cross_sheet_plan(root, longitudinal_path)[0]["table_scale"] == 1.3 for bad in (0.4, 2.1, "1.2", True): with pytest.raises(ValueError): write_cross_layout(root, longitudinal_path, {"default": [4, 4], "table_scale": bad}) assert read_cross_layout(root)["table_scale"] == 1.3 def test_옛_배율_확정_장_안내(tmp_path: Path) -> None: stage = tmp_path / "B07_DesignDetail" (stage / "drawings").mkdir(parents=True) saved = {"format": DRAWING_FORMAT, "entities": [], "layers": []} (stage / "drawings" / "cross_s00000m.json").write_text(json.dumps(saved), encoding="utf-8") entry = {"kind": "cross", "label": "1장", "confirmed": True, "layout": CROSS_SHEET_LAYOUT} manifest = {"drawings": {"cross_s00000m": entry}} (stage / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8") def read() -> dict: _k, _l, drawing, confirmed, _t = _read_drawing( tmp_path, tmp_path / "x.json", "cross_s00000m" ) assert confirmed return drawing assert "stale_scale" not in read() # 배율 적힘 없음 = 1 · 지금도 1 (stage / "cross_layout.json").write_text( json.dumps({"default": [4, 4], "sheets": {}, "table_scale": 1.5}), encoding="utf-8" ) assert read().get("stale_scale") is True entry["table_scale"] = 1.5 (stage / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8") assert "stale_scale" not in read()