- 새 Engine_Cad_PlanLabels — 시종점 > 측점 NO > IP > R > 배수 > 대피소 차례로 후보 중 첫 빈 자리 · 장애물 IP 표 · 필지 상자 · 먼 자리엔 지시선 - 조각 글은 후보째 모아 build_plan_kind_drawing 이 자리 정한 뒤 그림 · R 실거리 4 m 대신 종이 mm 후보 - 비탈 · 노선 따라가기 기하 = 새 Engine_Cad_PlanSlope(PlanKinds 700줄 넘어 분리 · import 길 그대로) - sub6 초안 반영 · 검증_sub8 실측 겹친 글 쌍 지형 12 → 0 · 새 test_141 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
549 lines
20 KiB
Python
549 lines
20 KiB
Python
"""B07 계획평면도 종류 — 정보 조각을 골라 한 장으로 얹는다(PLAN 60장).
|
|
|
|
계획평면도는 여러 종류(지형 · 노선배치도 · 대피소 · 라이다 · 사용자 추가)가 **같은 양식**이다.
|
|
정보 조각(등고선 · 중심선 · 노견선 · 성토 · 절토 외곽선 · 비탈 눈금 · IP · R · 측점 NO ·
|
|
시종점 · 대피소/사토장 · 배수시설 기호 · 라이다 음영)마다 프로젝트 저장소에 파일이 하나씩
|
|
있고, 종류는 그중 무엇을 얹을지 고른 목록이다. 이 모듈은 조각 기하 계산과 도면 조립만
|
|
한다(파일 입출력 없음).
|
|
|
|
좌표 규약: 종이 mm = (사업지 좌표 m - 그 장 도곽 최소점) x (1000 / 종류 축척 분모) (기본 1/1,200).
|
|
"""
|
|
|
|
import math
|
|
from typing import Any
|
|
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import (
|
|
DRAWING_FORMAT,
|
|
FRAME_LAYER_ID,
|
|
TABLE_LABEL_COLOR,
|
|
_layer,
|
|
_text_entity,
|
|
polyline_entity,
|
|
)
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_IpTable import ip_table_entities
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_MassHaul import _balloon_entities, balloon_size
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Plan import (
|
|
_COMPASS_MARGIN,
|
|
_COMPASS_SIZE,
|
|
_FONT_SIZE,
|
|
_ROUTE_WIDTH,
|
|
_STRUCTURE_FONT_SIZE,
|
|
_STRUCTURE_SIZE_MM,
|
|
_TITLE_FONT_SIZE,
|
|
CONTOUR_COLOR,
|
|
MM,
|
|
STREAM_COLOR,
|
|
STRUCTURE_COLOR,
|
|
)
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_PlanLabels import (
|
|
label_entities,
|
|
place_labels,
|
|
text_width,
|
|
)
|
|
|
|
# 비탈 · 노선 따라가기 기하는 700줄을 넘어 떼어냄 — 호출부 import 경로는 그대로.
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_PlanSlope import ( # noqa: F401
|
|
TICK_STEP_M,
|
|
route_frame,
|
|
side_is_cut,
|
|
slope_end,
|
|
slope_pieces,
|
|
)
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Template import (
|
|
compass_entities,
|
|
entities_bbox,
|
|
frame_entities,
|
|
scale_fields,
|
|
)
|
|
from config.config_system import DRAWING_SCALE_PLAN
|
|
|
|
# 조각 (id, 이름, 도면층 잠금) — 그리는 차례(배경이 먼저).
|
|
PIECES: tuple[tuple[str, str, bool], ...] = (
|
|
("lidar", "라이다 음영", True),
|
|
("contour", "등고선", True),
|
|
("stream", "계류", True),
|
|
("slope_ticks", "비탈 눈금", False),
|
|
("road_edge", "노견선", False),
|
|
("fill_outline", "성토 외곽선", False),
|
|
("cut_outline", "절토 외곽선", False),
|
|
("centerline", "중심선", False),
|
|
("ip", "IP", False),
|
|
("radius", "R", False),
|
|
("ip_table", "IP 표", False),
|
|
("station", "측점 NO", False),
|
|
("endpoints", "시종점", False),
|
|
("refuge", "대피소 · 사토장", False),
|
|
("drainage", "배수시설 기호", False),
|
|
# 용지도(93장) — 연속지적도 · 행정구역
|
|
("parcel", "필지 경계", False),
|
|
("parcel_info", "필지 정보", False),
|
|
("emd", "읍면동 경계", False),
|
|
("sgg", "시군구 경계", False),
|
|
)
|
|
PIECE_IDS: tuple[str, ...] = tuple(piece for piece, _name, _locked in PIECES)
|
|
# 옛 조각 id → 나눈 조각들 — 옛 종류 파일을 읽을 때 펼친다.
|
|
LEGACY_PIECES: dict[str, tuple[str, ...]] = {
|
|
"slope_outline": ("road_edge", "fill_outline", "cut_outline"),
|
|
}
|
|
_OUTLINE = LEGACY_PIECES["slope_outline"]
|
|
|
|
_TERRAIN = ("contour", "stream", "centerline", *_OUTLINE, "slope_ticks", "ip", "radius")
|
|
# 기본 종류 — 지울 수 있다. id 는 옛 도면 id(plan_terrain …)와 같아 확정본이 이어진다.
|
|
DEFAULT_KINDS: tuple[dict[str, Any], ...] = (
|
|
{"id": "terrain", "name": "지형", "pieces": [*_TERRAIN, "station"]},
|
|
{
|
|
"id": "route",
|
|
"name": "노선배치도",
|
|
"pieces": [p for p in _TERRAIN if p != "radius"] + ["station", "endpoints", "ip_table"],
|
|
},
|
|
{
|
|
"id": "layout",
|
|
"name": "대피소",
|
|
"pieces": [p for p in _TERRAIN if p != "radius"] + ["station", "refuge"],
|
|
},
|
|
# 라이다 = 지형과 같은 정보 · 배경만 라이다(등고선 · 계류 대신).
|
|
{
|
|
"id": "lidar",
|
|
"name": "라이다",
|
|
"pieces": ["lidar", *[p for p in _TERRAIN if p not in ("contour", "stream")], "station"],
|
|
},
|
|
# 용지도 = 옛 따로 도면과 같은 정보(등고선 · 노선 · 지적 · 행정 경계).
|
|
{
|
|
"id": "landuse",
|
|
"name": "용지도",
|
|
"pieces": ["contour", "centerline", "parcel", "parcel_info", "emd", "sgg"],
|
|
},
|
|
# 배수유역도 = 양식이 다른 종류(1/6,000 한 장 · 정보표) — 조각을 고르지 않는다(109장).
|
|
{"id": "watershed", "name": "배수유역도", "pieces": [], "layout": "watershed"},
|
|
)
|
|
|
|
_COLORS = {
|
|
"contour": CONTOUR_COLOR,
|
|
"stream": STREAM_COLOR,
|
|
"centerline": "#ff4d4d",
|
|
"road_edge": "#4dd8e6",
|
|
"fill_outline": "#4dd8e6",
|
|
"cut_outline": "#4dd8e6",
|
|
"slope_ticks": "#ff4d4d",
|
|
"ip": "#4d7dff",
|
|
"radius": "#4d7dff",
|
|
"station": "#ffe066",
|
|
"endpoints": "#ff9d4d",
|
|
"refuge": "#7ddc7d",
|
|
"drainage": STRUCTURE_COLOR,
|
|
"parcel": "#8c6b4f",
|
|
"parcel_info": "#a8703f", # 밝음 · 어두움 바탕 둘 다 읽히는 중간 밤색
|
|
"emd": "#2f7d4f",
|
|
"sgg": "#a63d3d",
|
|
}
|
|
# 행정 · 지적 경계 (선굵기, 파선)
|
|
_BOUNDARY_STYLE: dict[str, tuple[int, list[int] | None]] = {
|
|
"parcel": (1, None),
|
|
"emd": (2, [8, 4]),
|
|
"sgg": (3, [14, 5, 3, 5]),
|
|
}
|
|
PARCEL_FONT = 3.6 # 필지 정보 글 크기(종이 mm) — 용지도 자리 잡기도 이 값
|
|
_MAJOR_EVERY = 5 # 측점 NO 큰 글자 · 동그라미 간격(측점 수)
|
|
_LABEL_FONT = 2.5
|
|
_MAJOR_FONT = 4.5
|
|
_TICK_MM = 1.5
|
|
_CIRCLE_MM = 1.2
|
|
|
|
|
|
def layer_id(piece: str) -> str:
|
|
return f"b07-plan-{piece}"
|
|
|
|
|
|
def curve_pieces(
|
|
curves: list[dict[str, Any]],
|
|
route: list[tuple[float, float, float]],
|
|
box: tuple[float, float, float, float],
|
|
) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
"""곡선표에서 IP(교각점 · 접선)와 R 라벨을 만든다. 번호는 노선 전체 차례(IP1 …)."""
|
|
ips: list[dict[str, Any]] = []
|
|
radii: list[dict[str, Any]] = []
|
|
min_x, min_y, max_x, max_y = box
|
|
for number, curve in enumerate(curves, start=1):
|
|
apex, start, end = curve.get("apex"), curve.get("start"), curve.get("end")
|
|
radius = curve.get("radius_m")
|
|
if not (apex and start and end) or not isinstance(radius, (int, float)):
|
|
continue
|
|
if not (min_x <= apex[0] <= max_x and min_y <= apex[1] <= max_y):
|
|
continue
|
|
ips.append(
|
|
{
|
|
"name": f"IP{number}",
|
|
"apex": list(apex[:2]),
|
|
"start": list(start[:2]),
|
|
"end": list(end[:2]),
|
|
}
|
|
)
|
|
# R 라벨 후보는 곡선 한가운데에서 교각점 반대쪽(곡선 안쪽) · 바깥으로(종이 mm · 141-1).
|
|
middle = min(
|
|
((x, y) for _c, x, y in route),
|
|
key=lambda point: math.hypot(point[0] - apex[0], point[1] - apex[1]),
|
|
default=(apex[0], apex[1]),
|
|
)
|
|
dx, dy = middle[0] - apex[0], middle[1] - apex[1]
|
|
length = math.hypot(dx, dy) or 1.0
|
|
radii.append(
|
|
{
|
|
"label": f"R={round(float(radius), 1):g}",
|
|
"middle": list(middle),
|
|
"dir": [dx / length, dy / length],
|
|
}
|
|
)
|
|
return {"items": ips}, {"items": radii}
|
|
|
|
|
|
# ── 도면 조립 ─────────────────────────────────────────
|
|
|
|
|
|
def _circle(center: tuple[float, float], radius: float) -> list[tuple[float, float]]:
|
|
return [
|
|
(center[0] + radius * math.cos(a), center[1] + radius * math.sin(a))
|
|
for a in (2 * math.pi * i / 24 for i in range(25))
|
|
]
|
|
|
|
|
|
def _reading(nx: float, ny: float) -> tuple[float, float]:
|
|
"""글자 방향 — 노선 방향(법선을 돌린 것)으로 쓰되 거꾸로 서지 않게."""
|
|
tx, ty = ny, -nx
|
|
return (tx, ty) if tx > 0 or (abs(tx) < 1e-9 and ty > 0) else (-tx, -ty)
|
|
|
|
|
|
def _side_spots(cx: float, cy: float, size: float) -> list[dict[str, Any]]:
|
|
"""기호 곁 글 후보 — 오른쪽(옛 자리) → 왼 · 위 · 아래 → 멀리 오른 · 왼(지시선)."""
|
|
return [
|
|
{"x": cx + size + 1.0, "y": cy, "align": "left"},
|
|
{"x": cx - size - 1.0, "y": cy, "align": "right"},
|
|
{"x": cx, "y": cy + size + 2.5},
|
|
{"x": cx, "y": cy - size - 2.5},
|
|
{"x": cx + size + 8.0, "y": cy + 4.0, "align": "left", "far": True},
|
|
{"x": cx - size - 8.0, "y": cy - 4.0, "align": "right", "far": True},
|
|
]
|
|
|
|
|
|
def _piece_entities(
|
|
drawing_id: str,
|
|
piece: str,
|
|
data: dict[str, Any],
|
|
paper: Any,
|
|
mm: float = MM,
|
|
labels: list[dict[str, Any]] | None = None,
|
|
) -> list[dict[str, Any]]:
|
|
"""조각의 선 · 기호 엔티티. 글은 후보 자리째 `labels` 에 모음(자리는 `place_labels` · 141-1)."""
|
|
layer, color = layer_id(piece), _COLORS.get(piece, TABLE_LABEL_COLOR)
|
|
out: list[dict[str, Any]] = []
|
|
labels = labels if labels is not None else []
|
|
|
|
def line(points: Any, suffix: str, width: int = 1, dash: list[int] | None = None) -> None:
|
|
entity = polyline_entity(
|
|
drawing_id,
|
|
[paper(p) for p in points],
|
|
layer,
|
|
color,
|
|
suffix=suffix,
|
|
width=width,
|
|
dash=dash,
|
|
)
|
|
if entity:
|
|
out.append(entity)
|
|
|
|
def text(
|
|
seed: str,
|
|
label: str,
|
|
size: float,
|
|
spots: list[dict[str, Any]],
|
|
anchor: tuple[float, float] | None = None,
|
|
) -> None:
|
|
labels.append(
|
|
{
|
|
"piece": piece,
|
|
"seed": f"{drawing_id}:{piece}:{seed}",
|
|
"label": label,
|
|
"size": size,
|
|
"layer": layer,
|
|
"color": color,
|
|
"anchor": anchor,
|
|
"candidates": spots,
|
|
}
|
|
)
|
|
|
|
if piece == "lidar":
|
|
image, box = data.get("image"), data.get("box")
|
|
if image and box:
|
|
left, bottom = paper((box[0], box[1]))
|
|
right, top = paper((box[2], box[3]))
|
|
corners = [(left, bottom), (right, bottom), (right, top), (left, top)]
|
|
out.append(
|
|
{
|
|
"id": f"{drawing_id}:lidar",
|
|
"type": "Image",
|
|
"lineColor": "#ffffff",
|
|
"lineWidth": 1,
|
|
"layerId": layer,
|
|
"shapeData": {
|
|
"points": [{"x": x, "y": y} for x, y in corners],
|
|
"imageData": image,
|
|
},
|
|
}
|
|
)
|
|
elif piece in ("contour", "stream", *_OUTLINE):
|
|
for index, points in enumerate(data.get("lines") or []):
|
|
line(points, f":{index}")
|
|
elif piece == "centerline":
|
|
for index, points in enumerate(data.get("lines") or []):
|
|
line(points, f":{index}", width=_ROUTE_WIDTH)
|
|
elif piece == "slope_ticks":
|
|
for index, points in enumerate(data.get("ticks") or []):
|
|
line(points, f":{index}")
|
|
elif piece == "ip":
|
|
for index, item in enumerate(data.get("items") or []):
|
|
line([item["start"], item["apex"], item["end"]], f":{index}", dash=[4, 2])
|
|
apex = paper(item["apex"])
|
|
out_circle = polyline_entity(
|
|
drawing_id, _circle(apex, _CIRCLE_MM), layer, color, suffix=f":c:{index}"
|
|
)
|
|
if out_circle:
|
|
out.append(out_circle)
|
|
ax, ay, reach = apex[0], apex[1], _CIRCLE_MM + 3.0
|
|
text(
|
|
f"{index}",
|
|
item["name"],
|
|
_LABEL_FONT,
|
|
[
|
|
{"x": ax, "y": ay - reach},
|
|
{"x": ax, "y": ay + reach},
|
|
{"x": ax - _CIRCLE_MM - 1.0, "y": ay, "align": "right"},
|
|
{"x": ax + _CIRCLE_MM + 1.0, "y": ay, "align": "left"},
|
|
{"x": ax, "y": ay - reach - 5.0, "far": True},
|
|
{"x": ax, "y": ay + reach + 5.0, "far": True},
|
|
],
|
|
apex,
|
|
)
|
|
elif piece == "radius":
|
|
for index, item in enumerate(data.get("items") or []):
|
|
if "middle" not in item: # 옛 조각 파일 — 자리 하나
|
|
at = paper(item["at"])
|
|
text(f"{index}", item["label"], _LABEL_FONT, [{"x": at[0], "y": at[1]}])
|
|
continue
|
|
mx, my = paper(item["middle"])
|
|
dx, dy = item["dir"]
|
|
text(
|
|
f"{index}",
|
|
item["label"],
|
|
_LABEL_FONT,
|
|
[
|
|
{"x": mx + dx * d, "y": my + dy * d, "far": abs(d) > 6.0}
|
|
for d in (3.0, 6.0, -3.0, -6.0, 10.0, -10.0)
|
|
],
|
|
(mx, my),
|
|
)
|
|
elif piece == "ip_table":
|
|
out.extend(ip_table_entities(drawing_id, data))
|
|
elif piece == "station":
|
|
for index, item in enumerate(data.get("items") or []):
|
|
cx, cy = paper((item["x"], item["y"]))
|
|
nx, ny = item["nx"], item["ny"]
|
|
line(
|
|
[
|
|
(item["x"] - nx * _TICK_MM / mm, item["y"] - ny * _TICK_MM / mm),
|
|
(item["x"] + nx * _TICK_MM / mm, item["y"] + ny * _TICK_MM / mm),
|
|
],
|
|
f":{index}",
|
|
)
|
|
if item.get("major"):
|
|
circle = polyline_entity(
|
|
drawing_id, _circle((cx, cy), _CIRCLE_MM), layer, color, suffix=f":c:{index}"
|
|
)
|
|
if circle:
|
|
out.append(circle)
|
|
gap, (tx, ty) = _MAJOR_FONT * 1.8, _reading(nx, ny)
|
|
shift = text_width(item["label"], _MAJOR_FONT) / 2.0 + 2.0
|
|
text(
|
|
f"{index}",
|
|
item["label"],
|
|
_MAJOR_FONT,
|
|
[
|
|
{
|
|
"x": cx + nx * g + tx * t,
|
|
"y": cy + ny * g + ty * t,
|
|
"direction": (tx, ty),
|
|
}
|
|
for g, t in ((gap, 0.0), (-gap, 0.0), (gap, shift), (gap, -shift))
|
|
]
|
|
+ [
|
|
{"x": cx + nx * g, "y": cy + ny * g, "direction": (tx, ty), "far": True}
|
|
for g in (gap * 1.8, -gap * 1.8)
|
|
],
|
|
(cx, cy),
|
|
)
|
|
elif piece == "endpoints":
|
|
for index, item in enumerate(data.get("items") or []):
|
|
nx, ny = item["nx"], item["ny"]
|
|
reach = 6.0 / mm # 종이 6mm 막대
|
|
line(
|
|
[
|
|
(item["x"] - nx * reach, item["y"] - ny * reach),
|
|
(item["x"] + nx * reach, item["y"] + ny * reach),
|
|
],
|
|
f":{index}",
|
|
width=2,
|
|
)
|
|
cx, cy = paper((item["x"], item["y"]))
|
|
text(
|
|
f"{index}",
|
|
item["label"],
|
|
_LABEL_FONT + 0.5,
|
|
[
|
|
{"x": cx + nx * g, "y": cy + ny * g, "far": abs(g) > 9.0}
|
|
for g in (9.0, -9.0, 15.0, -15.0)
|
|
],
|
|
(cx, cy),
|
|
)
|
|
elif piece == "refuge":
|
|
size = 2.0 # 네모 기호 반크기(종이 mm)
|
|
for index, item in enumerate(data.get("items") or []):
|
|
x, y, half = item["x"], item["y"], size / mm
|
|
corners = [(-1, -1), (1, -1), (1, 1), (-1, 1), (-1, -1)]
|
|
line([(x + sx * half, y + sy * half) for sx, sy in corners], f":{index}")
|
|
cx, cy = paper((x, y))
|
|
text(f"{index}", item["label"], _LABEL_FONT, _side_spots(cx, cy, size), (cx, cy))
|
|
elif piece == "drainage":
|
|
size = _STRUCTURE_SIZE_MM # 마름모 기호 반크기(종이 mm)
|
|
for index, item in enumerate(data.get("items") or []):
|
|
cx, cy = paper((item["x"], item["y"]))
|
|
corners = [(0, 1), (1, 0), (0, -1), (-1, 0), (0, 1)]
|
|
diamond = polyline_entity(
|
|
drawing_id,
|
|
[(cx + sx * size, cy + sy * size) for sx, sy in corners],
|
|
layer,
|
|
color,
|
|
suffix=f":{index}",
|
|
)
|
|
if diamond:
|
|
out.append(diamond)
|
|
text(
|
|
f"{index}", item["label"], _STRUCTURE_FONT_SIZE, _side_spots(cx, cy, size), (cx, cy)
|
|
)
|
|
elif piece in _BOUNDARY_STYLE:
|
|
width, dash = _BOUNDARY_STYLE[piece]
|
|
for index, points in enumerate(data.get("lines") or []):
|
|
line(points, f":{index}", width=width, dash=dash)
|
|
elif piece == "parcel_info":
|
|
for index, item in enumerate(data.get("items") or []):
|
|
out.extend(
|
|
_balloon_entities(
|
|
f"{drawing_id}:{piece}",
|
|
str(index),
|
|
item["lines"],
|
|
paper(item["anchor"]),
|
|
paper(item["center"]),
|
|
"rect",
|
|
layer=layer,
|
|
color=color,
|
|
font=PARCEL_FONT,
|
|
)
|
|
)
|
|
return out
|
|
|
|
|
|
def _table_rect(entity: dict[str, Any]) -> tuple[float, float, float, float]:
|
|
shape = entity["shapeData"]
|
|
x, y = shape["origin"]["x"], shape["origin"]["y"]
|
|
return (x, y - sum(shape["rowHeights"]), x + sum(shape["columnWidths"]), y)
|
|
|
|
|
|
def build_plan_kind_drawing(
|
|
drawing_id: str,
|
|
label: str,
|
|
box: tuple[float, float, float, float],
|
|
pieces: dict[str, dict[str, Any]],
|
|
scale: int = DRAWING_SCALE_PLAN,
|
|
) -> dict[str, Any]:
|
|
"""고른 조각만 얹어 계획평면도 한 장을 만든다. `box` = 그 장 도곽 범위(실좌표 m)."""
|
|
min_x, min_y = box[0], box[1]
|
|
mm = 1000.0 / scale
|
|
|
|
def paper(point: Any) -> tuple[float, float]:
|
|
return ((point[0] - min_x) * mm, (point[1] - min_y) * mm)
|
|
|
|
entities: list[dict[str, Any]] = []
|
|
labels: list[dict[str, Any]] = []
|
|
obstacles: list[tuple[float, float, float, float]] = []
|
|
for piece in PIECE_IDS:
|
|
if piece in pieces:
|
|
made = _piece_entities(drawing_id, piece, pieces[piece], paper, mm, labels)
|
|
entities.extend(made)
|
|
# 글이 비켜 갈 자리 — IP 표 · 필지 상자(141-1). 제목은 내용 위에 따로 서서 안 겹침.
|
|
if piece == "ip_table":
|
|
obstacles += [_table_rect(e) for e in made if e.get("type") == "Table"]
|
|
elif piece == "parcel_info":
|
|
for item in pieces[piece].get("items") or []:
|
|
width, height = balloon_size(item["lines"], PARCEL_FONT)
|
|
cx, cy = paper(item["center"])
|
|
obstacles.append(
|
|
(cx - width / 2, cy - height / 2, cx + width / 2, cy + height / 2)
|
|
)
|
|
entities.extend(label_entities(drawing_id, place_labels(labels, obstacles)))
|
|
# 도곽 · 방위표 자리는 조각과 무관하게 같은 자리 — 그 장 범위를 기준으로 잡는다.
|
|
frame_box = (0.0, 0.0, (box[2] - min_x) * mm, (box[3] - min_y) * mm)
|
|
entities.extend(
|
|
compass_entities(
|
|
drawing_id,
|
|
(
|
|
frame_box[2] + _COMPASS_MARGIN + _COMPASS_SIZE / 2.0,
|
|
frame_box[3] - _COMPASS_SIZE / 2.0,
|
|
),
|
|
_COMPASS_SIZE,
|
|
)
|
|
)
|
|
bbox = entities_bbox(entities) or frame_box
|
|
min_bx, _min_by, max_bx, max_by = bbox
|
|
entities.append(
|
|
_text_entity(
|
|
f"{drawing_id}:title",
|
|
label,
|
|
(min_bx + max_bx) / 2.0,
|
|
max_by + 12.0,
|
|
"b07-plan-title",
|
|
_TITLE_FONT_SIZE,
|
|
TABLE_LABEL_COLOR,
|
|
)
|
|
)
|
|
entities.append(
|
|
_text_entity(
|
|
f"{drawing_id}:scale",
|
|
f"S = 1/{scale:,}",
|
|
max_bx,
|
|
max_by + 5.0,
|
|
"b07-plan-title",
|
|
_FONT_SIZE,
|
|
TABLE_LABEL_COLOR,
|
|
align="right",
|
|
)
|
|
)
|
|
entities.extend(
|
|
frame_entities(
|
|
drawing_id,
|
|
entities_bbox(entities) or bbox,
|
|
fit=False,
|
|
fields={"도면명": label, **scale_fields(("", scale))},
|
|
)
|
|
)
|
|
layers = [
|
|
_layer(layer_id(piece), name, locked=locked)
|
|
for piece, name, locked in PIECES
|
|
if piece in pieces
|
|
]
|
|
return {
|
|
"format": DRAWING_FORMAT,
|
|
"entities": entities,
|
|
"layers": [
|
|
*layers,
|
|
_layer("b07-plan-title", "표제"),
|
|
_layer(FRAME_LAYER_ID, "도각", locked=True),
|
|
],
|
|
}
|