fix(526-1): B07 횡단 장 인쇄 칸선 지움 · 칸마다 안내선 · 처음 맞춤 작도 영역까지

- 횡단 장 칸 격자선(도각 층 인쇄 개체) 지움 — 칸은 화면 안내선 겹판(guide.cells · 단면마다 점선)
  · 안내선 숨기기에 함께 꺼짐 · 넘친 칸 붉은 틀은 그대로
- 옛 확정 · 저장본 장은 열 때 칸선만 걷고 칸 안내선은 지금 배치로(refresh_saved_sheet)
- CAD 처음 맞춤 · 전체 맞춤 = 내용 + 작도 영역(가이드) — 표준 횡단도가 내용에만 확대돼
  도면 한 장 안 자리가 안 보이던 흠
- 낡은 시험 고침: test_169 칸선 단언 → 칸선 없음 + 칸 안내선 · test_212_3 「남는 검은 선 =
  인쇄 칸선 지우지 않음」(212-3 결정) → 요구 343-4 로 칸선 지움 · guide-area 시험 칸 그리기

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 14:39:18 +09:00
co-authored by Claude Opus 5.5
parent 624a3e0aaa
commit a2c3c0fc1f
12 changed files with 140 additions and 80 deletions
@@ -3,19 +3,22 @@
채우는 순서는 **좌하단부터 아래에서 위로, 그다음 오른쪽 열**이다
(2026-08-29 사용자 확정). 칸 = 도각 그림칸(usable_area)을 똑같이 나눈 자리 · 단면은
칸 가운데 · 작성 척도 1/100 그대로(큰 단면이 칸을 넘으면 겹침 — 사용자 배치로 고침).
칸 = 인쇄되는 표 격자선 · 블록 테두리 없음 · 칸보다 큰 단면은 `overflow_cells`(169-2).
칸 = 화면 안내선(`guide_cells` · 인쇄 · 저장 안 됨) · 칸선 · 블록 테두리 없음 · 칸보다 큰 단면은
`overflow_cells`(169-2).
칸 안 = 표를 칸 아래 가운데에 먼저 · 그 위 글 · 그림(도로 중심 = 칸 가운데)(174-1).
측점마다 도면층 하나(「횡단 No.3」) = 원지반 · 계획선 · 구조물 · 암 그림 · 종류는 색 · 선 꼴
· 표 · 글은 수량표 층(212-2).
"""
import re
from collections.abc import Callable
from typing import Any
from uuid import uuid5
from B05_Profile.B05_Profile_Engine_Sections import cross_key
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import (
_ENTITY_NS,
_POLY_NS,
CROSS_BORDER_PAD_MM,
CROSS_TABLE_LAYER_ID,
DESIGN_LAYER_ID,
@@ -24,14 +27,12 @@ from B07_DesignDetail.B07_DesignDetail_Engine_Cad import (
GROUND_LAYER_ID,
ROCK_LAYER_ID,
STRUCTURE_LAYER_ID,
TABLE_LINE_COLOR,
_clip_polyline,
_layer,
_section_window,
build_cross_drawing,
cross_mm,
points_from_samples,
polyline_entity,
)
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Table import (
cross_note_block_height,
@@ -162,28 +163,41 @@ def plan_cross_sheets(
return sheets
def _grid_lines(sheet_id: str, cols: int, rows: int) -> list[dict[str, Any]]:
"""장 칸 격자선(169-2) — 세로 열 + 1 · 가로 행 + 1 · 도각 층(인쇄 · DXF 에 나감)."""
def strip_grid_lines(sheet_id: str, entities: list[Any]) -> list[Any]:
"""옛 저장본의 인쇄 칸선(169-2 · `:grid-v0` …) 걷음 — 칸은 이제 화면 안내선(`guide_cells`)."""
seeds = (f"{sheet_id}:{FRAME_LAYER_ID}:grid-{axis}{i}" for axis in "vh" for i in range(33))
ids = {str(uuid5(_POLY_NS, seed)) for seed in seeds}
return [
entity for entity in entities if not (isinstance(entity, dict) and entity.get("id") in ids)
]
def sheet_cells(sheet: dict[str, Any], count: int | None = None) -> list[list[float]]:
"""장 칸 [x0, y0, x1, y1] — 앞 `count` 개(없으면 측점 수) · 칸 가운데 = `slots`."""
cols, rows = sheet.get("grid") or (4, 4)
usable_w, usable_h = usable_area()
left, bottom = -usable_w / 2.0, -usable_h / 2.0
lines = [
((left + usable_w * i / cols, bottom), (left + usable_w * i / cols, -bottom), f"v{i}")
for i in range(cols + 1)
] + [
((left, bottom + usable_h * j / rows), (-left, bottom + usable_h * j / rows), f"h{j}")
for j in range(rows + 1)
cell_w, cell_h = usable_w / cols, usable_h / rows
slots = (sheet.get("slots") or [])[
: len(sheet.get("chainages") or []) if count is None else count
]
return [
entity
for start, end, name in lines
if (
entity := polyline_entity(
sheet_id, [start, end], FRAME_LAYER_ID, TABLE_LINE_COLOR, f":grid-{name}"
)
)
[x - cell_w / 2.0, y - cell_h / 2.0, x + cell_w / 2.0, y + cell_h / 2.0] for x, y in slots
]
def refresh_saved_sheet(
drawing_id: str, saved: dict[str, Any], plan: Callable[[], list[dict[str, Any]]]
) -> None:
"""확정 · 저장본 장 — 옛 인쇄 칸선 걷고 칸 안내선은 지금 배치로(526-1) · 원본 없으면 칸 없음."""
saved["entities"] = strip_grid_lines(drawing_id, saved.get("entities") or [])
try:
sheets = plan()
except FileNotFoundError:
sheets = []
sheet = next((item for item in sheets if item["id"] == drawing_id), None)
saved["guide_cells"] = sheet_cells(sheet) if sheet else []
def _move_to_layer(entity: dict[str, Any], layer_id: str) -> None:
"""그림 종류 층 개체(자식 선까지)를 측점 층으로 — id 는 그대로(212-2)."""
if entity.get("layerId") in _DRAWING_LAYER_IDS:
@@ -198,22 +212,12 @@ def build_cross_sheet(sheet: dict[str, Any], sections: list[dict[str, Any]]) ->
placements: list[dict[str, Any]] = []
overflow: list[list[float]] = []
station_layers: list[dict[str, Any]] = []
slots = sheet.get("slots") or []
cols, rows = sheet.get("grid") or (4, 4)
cells = sheet_cells(sheet, len(sections))
usable_w, usable_h = usable_area()
cell_w, cell_h = usable_w / cols, usable_h / rows
for index, section in enumerate(sections):
if index >= len(slots):
break
for section, cell in zip(sections, cells):
seed_id = f"{sheet['id']}:{section['chainage']}"
center_x, center_y = slots[index]
cell = [
center_x - cell_w / 2.0,
center_y - cell_h / 2.0,
center_x + cell_w / 2.0,
center_y + cell_h / 2.0,
]
center_x = (cell[0] + cell[2]) / 2.0
# 표 아래선 = 칸 아래 + 여백 · 도로 중심 = 칸 가운데 · 글 · 그림은 위로(174-1)
placed = build_cross_drawing(
section["source"],
@@ -254,7 +258,6 @@ def build_cross_sheet(sheet: dict[str, Any], sections: list[dict[str, Any]]) ->
placements.extend(placed.get("cross_placements") or [])
if entities:
entities.extend(_grid_lines(sheet["id"], cols, rows))
scale = drawing_scale("cross")
# 가로 · 세로가 같으면 분모 하나 · 다르면 이름표(H · V)(485)
scales = (
@@ -262,7 +265,7 @@ def build_cross_sheet(sheet: dict[str, Any], sections: list[dict[str, Any]]) ->
if scale["x"] == scale["y"]
else (("H", scale["x"]), ("V", scale["y"]))
)
# 도각 = 고정 그림칸 — 내용 크기와 무관하게 칸 격자와 같은 자리(140-3).
# 도각 = 고정 그림칸 — 내용 크기와 무관하게 칸과 같은 자리(140-3).
entities.extend(
frame_entities(
sheet["id"],
@@ -278,6 +281,8 @@ def build_cross_sheet(sheet: dict[str, Any], sections: list[dict[str, Any]]) ->
"cross_placements": placements,
# 칸보다 큰 단면의 칸 [x0, y0, x1, y1] — 라우터가 CAD 가이드 붉은 틀로 보냄(169-2)
"overflow_cells": overflow,
# 단면마다 칸 — 라우터가 CAD 가이드 칸 점선으로 보냄(인쇄 칸선 대신 · 526-1)
"guide_cells": cells,
"layers": [
*station_layers,
_layer(CROSS_TABLE_LAYER_ID, "수량 산출표"),