Files
Aislo/resources/tester/test_140_b07_cross_grid.py
T
eomsangdonandClaude Opus 5.5 a504f561ad feat(B07): 횡단면도 장 배치 장마다 열 × 행 칸 가운데 · 도각 고정 그림칸(140-3)
- 쌓기 · 최소 장수 함수 지움 · grid_slots(n, 열, 행) 칸 가운데(좌하단 → 위 → 오른쪽) · 1/100 그대로
- 저장 B07_DesignDetail/cross_layout.json {default [4,4], sheets {장 번호: [열, 행]}} · GET · PUT cross-layout
- 배치 바꾸면 담긴 측점이 바뀐 확정 장 풂 · 그 측점 설계 잠정 · CROSS_SHEET_LAYOUT = 3
- 장 나눔이 설계 · 덧그림과 무관 — 목록 · 확정 · 수정에서 덧그림 재기 빠짐
- 좌측 정보 칸 「배치 열 × 행 [적용] · [모든 장 기본]」 → 다시 세워 같은 장 번호 엶

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TNMXVCZXMpHpVvqmFkeChL
2026-10-09 14:56:34 +09:00

149 lines
6.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""140-3 B07 횡단면도 장 배치 — 장마다 열 × 행 칸(기본 4 × 4).
못박는 것
- 칸 = 그림칸(usable_area)을 똑같이 나눈 자리 · 차례 = 좌하단 → 위 → 오른쪽 열.
- 측점 73개 · 4 × 4 → 16 · 16 · 16 · 16 · 9 · 17번째 측점 = 2장 첫 칸.
- 장마다 다른 배치(sheets {장 번호: [열, 행]}) · 기본 바꾸기.
- 단면 블록 가운데 = 칸 가운데 · 블록 크기 = 1/100 그대로(section_block_size).
- 도각 = 고정 그림칸 — 내용이 작아도 커도 같은 자리.
- 배치 저장 · 읽기(깨진 값 거절) · 담긴 측점이 바뀐 확정 장만 풂.
"""
import json
from pathlib import Path
import pytest
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import FRAME_LAYER_ID
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Sheet import (
build_cross_sheet,
grid_slots,
plan_cross_sheets,
section_block_size,
)
from B07_DesignDetail.B07_DesignDetail_Engine_Template import entities_bbox, usable_area
from B07_DesignDetail.B07_DesignDetail_Router_Support import (
_cross_sheet_plan,
read_cross_layout,
write_cross_layout,
)
from B07_DesignDetail.B07_DesignDetail_Router_Support_Io import _read_manifest, _write_manifest
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 test_grid_slots_bottom_left_up_then_right() -> None:
w, h = usable_area()
slots = grid_slots(6, 4, 4)
cw, ch = w / 4, h / 4
assert slots[0] == pytest.approx([-w / 2 + cw / 2, -h / 2 + ch / 2]) # 좌하단
assert slots[1] == pytest.approx([-w / 2 + cw / 2, -h / 2 + 1.5 * ch]) # 그 위
assert slots[4] == pytest.approx([-w / 2 + 1.5 * cw, -h / 2 + ch / 2]) # 다음 열 맨 아래
def test_73_stations_default_grid_17th_is_sheet_two() -> None:
chainages = [i * 20.0 for i in range(73)]
sheets = plan_cross_sheets(chainages)
assert [len(s["chainages"]) for s in sheets] == [16, 16, 16, 16, 9]
assert sheets[1]["chainages"][0] == chainages[16] # 17번째 측점 = 2장 첫 칸
assert sheets[1]["number"] == 2 and sheets[1]["grid"] == [4, 4]
assert sheets[1]["id"] == "cross_s00320m" # 장 id = 첫 측점
def test_per_sheet_and_default_layout() -> None:
chainages = [float(i) for i in range(40)]
layout = {"default": [3, 3], "sheets": {"1": [2, 2], "3": [5, 4]}}
sheets = plan_cross_sheets(chainages, layout)
assert [s["grid"] for s in sheets] == [[2, 2], [3, 3], [5, 4], [3, 3]]
assert [len(s["chainages"]) for s in sheets] == [4, 9, 20, 7]
def _frame_bbox(drawing: dict) -> tuple:
return entities_bbox([e for e in drawing["entities"] if e.get("layerId") == FRAME_LAYER_ID])
def test_block_centered_in_cell_at_scale_and_frame_fixed() -> None:
table = {"ground": 100.0, "planned": 100.0}
small = [_source(0.0, 0.05, 6)]
big = [_source(0.0, 0.6, 15), _source(20.0, 0.2, 8)]
drawings = []
for sources in (small, big):
sheet = plan_cross_sheets([s["chainage_m"] for s in sources])[0]
sections = [
{"chainage": s["chainage_m"], "source": s, "quantity_table": table, "title": ""}
for s in sources
]
drawing = build_cross_sheet(sheet, sections)
for source, slot, placement in zip(
sources, sheet["slots"], drawing["cross_placements"], strict=True
):
left, bottom, right, top = placement["frame"]
# 블록 가운데 = 칸 가운데
assert (left + right) / 2 == pytest.approx(slot[0])
assert (bottom + top) / 2 == pytest.approx(slot[1])
# 1/100 그대로 — 블록 크기가 자기 치수 그대로(칸 맞춤으로 줄이지 않음)
width, height = section_block_size(source, None, design=None)
assert right - left == pytest.approx(width, abs=0.5)
drawings.append(drawing)
# 도각 = 고정 그림칸 — 내용 크기와 무관하게 같은 자리
assert _frame_bbox(drawings[0]) == pytest.approx(_frame_bbox(drawings[1]))
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), 0.1)), "utf-8"
)
longitudinal_path.write_text(json.dumps({"stations": stations}), "utf-8")
return root, longitudinal_path
def test_layout_store_and_release_changed_confirmed(tmp_path: Path) -> None:
root, longitudinal_path = _project(tmp_path, 20)
assert read_cross_layout(root) == {"default": [4, 4], "sheets": {}} # 파일 없으면 기본
ids = [s["id"] for s in _cross_sheet_plan(root, longitudinal_path)]
assert ids == ["cross_s00000m", "cross_s00320m"] # 16 + 4
manifest = _read_manifest(root)
manifest["drawings"] = {i: {"confirmed": True} for i in ids}
_write_manifest(root, manifest)
# 2장만 3 × 3 — 1장 측점 그대로(확정 유지) · 2장 측점 그대로(4개 < 9칸) → 풀 장 없음
assert (
write_cross_layout(root, longitudinal_path, {"default": [4, 4], "sheets": {"2": [3, 3]}})
== []
)
assert read_cross_layout(root)["sheets"] == {"2": [3, 3]}
# 1장 3 × 3 — 1장 9개로 줄고 2장은 다른 측점에서 시작 → 1장 풂(옛 측점 16개 돌려줌)
stale = write_cross_layout(
root, longitudinal_path, {"default": [4, 4], "sheets": {"1": [3, 3]}}
)
assert stale[:16] == [i * 20 for i in range(16)]
drawings = _read_manifest(root)["drawings"]
assert drawings["cross_s00000m"]["confirmed"] is False
assert drawings["cross_s00320m"]["confirmed"] is False # 사라진 장 id 도 풂
new_ids = [s["id"] for s in _cross_sheet_plan(root, longitudinal_path)]
assert new_ids == ["cross_s00000m", "cross_s00180m"]
with pytest.raises(ValueError):
write_cross_layout(root, longitudinal_path, {"default": [0, 4], "sheets": {}})
with pytest.raises(ValueError):
write_cross_layout(root, longitudinal_path, {"default": [4, 4], "sheets": {"x": [2, 2]}})