fix(B07): 횡단면도 단면 블록 테두리 간격 1 mm — 한 장에 더 담음

- 테두리 여유 4 mm · 위아래 덧 여백 5 mm 제거 → 테두리 ↔ 그림 1 mm(CROSS_BORDER_PAD_MM)
- 장 배치(Sheet.py)와 단독 도면(Engine_Cad.py) 같은 상수
- 검증_sub4 첫 장 칸 168×172 → 162×156 mm · 담을 수 8 → 12

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013rJwJMVL4EE31y77TB11AP
This commit is contained in:
2026-10-06 17:38:14 +09:00
co-authored by Claude Opus 5.5
parent ffb851904c
commit 1eb43a790d
3 changed files with 44 additions and 6 deletions
@@ -386,6 +386,8 @@ def _cross_line_entities(
CROSS_MM = 1000.0 / DRAWING_SCALE_CROSS
# 단면을 그리는 가로 범위: 설계선(절·성토)·구조물이 원지반과 갈라지는 구간 + 여유(m).
CROSS_SECTION_MARGIN_M = 1.5
# 단면 블록 테두리 ↔ 안쪽 그림 간격(mm) — 한 장에 최대한 많이 담는다.
CROSS_BORDER_PAD_MM = 1.0
# 원지반과 "만난다"고 볼 표고 차이(m) — 이보다 작으면 붙어 있는 것으로 본다.
_TOUCH_TOLERANCE_M = 0.02
@@ -507,7 +509,7 @@ def build_cross_drawing(
# 표는 단면 중심 아래에 붙인다 (좌표는 mm). 그리는 창이 좌우 비대칭이면
# 단면 중심이 원점과 어긋나므로 표도 같은 중심을 쓴다.
center_x = ox + (x0 + x1) / 2.0 * CROSS_MM
half_height = own_half_height * CROSS_MM + 5.0
half_height = own_half_height * CROSS_MM
table_bottom = oy - half_height
if quantity_table is not None:
table_top = oy - half_height - 8.0
@@ -520,10 +522,11 @@ def build_cross_drawing(
if cell_frame is not None:
frame_left, frame_bottom, frame_right, frame_top = cell_frame
else:
frame_x = max((x1 - x0) / 2.0 * CROSS_MM + 4.0, cross_table_width() / 2.0 + 4.0)
pad = CROSS_BORDER_PAD_MM
frame_x = max((x1 - x0) / 2.0 * CROSS_MM + pad, cross_table_width() / 2.0 + pad)
frame_left, frame_right = center_x - frame_x, center_x + frame_x
frame_top = oy + half_height + 4.0
frame_bottom = table_bottom - 4.0
frame_top = oy + half_height + pad
frame_bottom = table_bottom - pad
corners = [
(frame_left, frame_bottom),
(frame_right, frame_bottom),
@@ -11,6 +11,7 @@ from typing import Any
from B05_Profile.B05_Profile_Engine_Sections import cross_key
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import (
CROSS_BORDER_PAD_MM,
CROSS_MM,
CROSS_TABLE_LAYER_ID,
DESIGN_LAYER_ID,
@@ -44,7 +45,7 @@ CROSS_SHEET_ID = re.compile(r"cross_s(\d{2,}(?:\.\d{2})?)m?")
# 블록 사이 여백(mm)과 테두리 여유 — build_cross_drawing의 테두리 값과 맞춘다.
# 여백 0 = 이웃 칸의 테두리가 맞닿는다(2026-09-04 사용자 지시 — 박스 사이 간격 제거).
_BLOCK_GAP_MM = 0.0
_BORDER_PAD_MM = 4.0
_BORDER_PAD_MM = CROSS_BORDER_PAD_MM
def _design_points(design_line: list[Any] | None) -> list[tuple[float, float]]:
@@ -72,7 +73,7 @@ def section_block_size(
x0, x1 = _section_window(ground, design)
ground_clipped = _clip_polyline(ground, x0, x1)
ys = [y for _x, y in ground_clipped] + [y for x, y in design if x0 <= x <= x1]
half_height = ((max(ys) - min(ys)) / 2.0 if ys else 5.0) * CROSS_MM + 5.0
half_height = ((max(ys) - min(ys)) / 2.0 if ys else 5.0) * CROSS_MM
width = 2.0 * max(
(x1 - x0) / 2.0 * CROSS_MM + _BORDER_PAD_MM,
@@ -0,0 +1,34 @@
"""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)