- 장 배치: 칸을 그 장 가장 큰 블록으로 통일하던 것을 없애고 블록을 자기 크기로 열마다 쌓음(좌하단부터 아래→위, 차면 오른쪽 열) - 그리는 가로 범위를 선이 있는 데까지로 — 원지반 자료가 짧으면 빈 여유가 박스를 밀던 것 - 블록 크기에 암 경계선 포함(실제 그림과 같은 규칙) - 옛 배치로 확정된 장은 저장본을 그대로 보이되 [수정] 안내 알림 - 검증_sub4: 박스 ↔ 그림 66개 모두 1.0 mm · 같은 조건 27장 → 20장 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013rJwJMVL4EE31y77TB11AP
152 lines
6.3 KiB
Python
152 lines
6.3 KiB
Python
"""B07 횡단면도 — 단면 블록 테두리 ↔ 안쪽 그림 간격 1 mm (59-1)."""
|
||
|
||
import pytest
|
||
|
||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import (
|
||
CROSS_BORDER_PAD_MM,
|
||
FRAME_LAYER_ID,
|
||
build_cross_drawing,
|
||
)
|
||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Sheet import section_block_size
|
||
from B07_DesignDetail.B07_DesignDetail_Engine_Template import entities_bbox
|
||
|
||
_SOURCE = {
|
||
"chainage_m": 20.0,
|
||
"samples": [
|
||
{"valid": True, "offset_m": float(x), "elevation_m": 100.0 + 0.3 * x}
|
||
for x in range(-10, 11)
|
||
],
|
||
}
|
||
|
||
|
||
def test_frame_hugs_section_by_one_mm() -> None:
|
||
drawing = build_cross_drawing(_SOURCE, "cross_00020m", None, None, None, "")
|
||
lines = [e for e in drawing["entities"] if e.get("layerId") != FRAME_LAYER_ID]
|
||
frame = [e for e in drawing["entities"] if e.get("layerId") == FRAME_LAYER_ID]
|
||
min_x, min_y, max_x, max_y = entities_bbox(lines)
|
||
f_min_x, f_min_y, f_max_x, f_max_y = entities_bbox(frame)
|
||
assert CROSS_BORDER_PAD_MM == 1.0
|
||
assert f_max_y - max_y == pytest.approx(1.0)
|
||
assert min_y - f_min_y == pytest.approx(1.0)
|
||
# 표 없는 블록 크기 = 테두리 크기
|
||
width, height = section_block_size(_SOURCE, None, with_table=False)
|
||
assert height == pytest.approx(f_max_y - f_min_y)
|
||
assert width == pytest.approx(f_max_x - f_min_x)
|
||
|
||
|
||
# ── 68-1 장 배치: 단면마다 자기 박스(칸 통일 없음) ──────────────────────────────
|
||
|
||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import CROSS_SECTION_MARGIN_M # noqa: E402
|
||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Sheet import ( # noqa: E402
|
||
build_cross_sheet,
|
||
plan_cross_sheets,
|
||
)
|
||
|
||
|
||
def _source(chainage: float, slope: float, half: int = 10) -> dict:
|
||
return {
|
||
"chainage_m": chainage,
|
||
"samples": [
|
||
{"valid": True, "offset_m": float(x), "elevation_m": 100.0 + slope * x}
|
||
for x in range(-half, half + 1)
|
||
],
|
||
}
|
||
|
||
|
||
def _box(entity: dict) -> tuple[float, float, float, float] | None:
|
||
if entity.get("type") == "Table":
|
||
shape = entity["shapeData"]
|
||
origin = shape["origin"]
|
||
return (
|
||
origin["x"],
|
||
origin["y"] - sum(shape["rowHeights"]),
|
||
origin["x"] + sum(shape["columnWidths"]),
|
||
origin["y"],
|
||
)
|
||
return entities_bbox([entity])
|
||
|
||
|
||
_TABLE = {"ground": 100.0, "planned": 100.0}
|
||
|
||
|
||
def test_each_block_hugs_its_own_drawing_by_one_mm() -> None:
|
||
"""크기 다른 단면 셋을 한 장에 — 박스마다 자기 그림(표 포함)에서 사방 1.0 mm."""
|
||
sources = [_source(0.0, 0.05), _source(20.0, 0.6, 15), _source(40.0, 0.2, 6)]
|
||
blocks = [(s["chainage_m"], *section_block_size(s, None, design=None)) for s in sources]
|
||
sheets = plan_cross_sheets(blocks)
|
||
assert len(sheets) == 1
|
||
sections = [
|
||
{"chainage": s["chainage_m"], "source": s, "quantity_table": _TABLE, "title": ""}
|
||
for s in sources
|
||
]
|
||
drawing = build_cross_sheet(sheets[0], sections)
|
||
inner = [_box(e) for e in drawing["entities"] if e.get("layerId") != FRAME_LAYER_ID]
|
||
frames = [p["frame"] for p in drawing["cross_placements"]]
|
||
sizes = {(round(f[2] - f[0], 1), round(f[3] - f[1], 1)) for f in frames}
|
||
assert len(sizes) == 3 # 칸 통일 없음 — 박스 크기가 단면마다 다르다
|
||
for left, bottom, right, top in frames:
|
||
mine = [
|
||
b
|
||
for b in inner
|
||
if b
|
||
and b[0] >= left - 1e-6
|
||
and b[2] <= right + 1e-6
|
||
and b[1] >= bottom - 1e-6
|
||
and b[3] <= top + 1e-6
|
||
]
|
||
assert min(b[0] for b in mine) - left == pytest.approx(1.0)
|
||
assert right - max(b[2] for b in mine) == pytest.approx(1.0)
|
||
assert min(b[1] for b in mine) - bottom == pytest.approx(1.0)
|
||
assert top - max(b[3] for b in mine) == pytest.approx(1.0)
|
||
|
||
|
||
def test_small_blocks_are_not_widened_by_a_big_one() -> None:
|
||
"""큰 단면 하나 + 작은 단면 여럿 — 칸을 큰 블록으로 통일하던 때보다 한 장에 더 담긴다."""
|
||
blocks = [(0.0, 300.0, 300.0)] + [(float(i), 110.0, 60.0) for i in range(1, 30)]
|
||
sheets = plan_cross_sheets(blocks)
|
||
# 옛 배치(칸 300×300 → A1 739×499 에 2×1)는 첫 장에 2개였다.
|
||
assert len(sheets[0]["chainages"]) > 2
|
||
assert sum(len(s["chainages"]) for s in sheets) == 30
|
||
|
||
|
||
def test_window_stops_where_lines_end() -> None:
|
||
"""원지반 자료가 여유(1.5 m)보다 짧으면 범위를 선 끝에서 멈춘다 — 빈 여유가 박스를 밀지 않음."""
|
||
source = _source(0.0, 0.1, 6)
|
||
design_line = [
|
||
{"offset_m": -6.0, "elevation_m": 99.4},
|
||
{"offset_m": -5.5, "elevation_m": 98.0},
|
||
{"offset_m": 5.5, "elevation_m": 98.0},
|
||
{"offset_m": 6.0, "elevation_m": 100.6},
|
||
]
|
||
drawing = build_cross_drawing(source, "cross_00000m", design_line, None, None, "")
|
||
placement = drawing["cross_placements"][0]
|
||
assert placement["x0"] == pytest.approx(-6.0)
|
||
assert placement["x1"] == pytest.approx(6.0)
|
||
assert CROSS_SECTION_MARGIN_M == 1.5
|
||
|
||
|
||
def test_old_confirmed_cross_sheet_is_flagged(tmp_path) -> None:
|
||
"""68-1 전에 확정한 횡단 장은 저장본을 그대로 보이되 「옛 배치」 표시를 단다."""
|
||
import json
|
||
|
||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import DRAWING_FORMAT
|
||
from B07_DesignDetail.B07_DesignDetail_Router_Support import (
|
||
CROSS_SHEET_LAYOUT,
|
||
_read_drawing,
|
||
)
|
||
|
||
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}
|
||
manifest = {"drawings": {"cross_s00000m": entry}}
|
||
(stage / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8")
|
||
_k, _l, drawing, confirmed, _t = _read_drawing(tmp_path, tmp_path / "x.json", "cross_s00000m")
|
||
assert confirmed and drawing.get("stale_layout") is True
|
||
|
||
entry["layout"] = CROSS_SHEET_LAYOUT
|
||
(stage / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8")
|
||
_k, _l, drawing, confirmed, _t = _read_drawing(tmp_path, tmp_path / "x.json", "cross_s00000m")
|
||
assert confirmed and "stale_layout" not in drawing
|