- 새 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
140 lines
4.6 KiB
Python
140 lines
4.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""141-1 B07 평면도 글자 겹침 피하기.
|
|
|
|
못박는 것
|
|
- 우선순위 = 시종점 > 측점 NO > IP > R > 배수 > 대피소 — 앞 글이 첫 후보(옛 자리)를 가짐.
|
|
- 후보 중 첫 빈 자리 · 모두 막히면 첫 후보 그대로(글은 남음).
|
|
- 장애물(IP 표 · 필지 상자) 비켜 감.
|
|
- 가까운 IP · R · 배수 둘 · NO.0 + 시점 한 장 → 글 사각 겹침 0 · 글 모두 남음.
|
|
- 멀리 놓인 글(후보 far)엔 지시선.
|
|
"""
|
|
|
|
from itertools import combinations
|
|
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_PlanKinds import build_plan_kind_drawing
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_PlanLabels import (
|
|
overlaps,
|
|
place_labels,
|
|
text_rect,
|
|
)
|
|
|
|
|
|
def _label(piece: str, label: str, spots: list[dict], anchor=None) -> dict:
|
|
return {
|
|
"piece": piece,
|
|
"seed": f"s:{piece}:{label}",
|
|
"label": label,
|
|
"size": 2.5,
|
|
"layer": "l",
|
|
"color": "#fff",
|
|
"anchor": anchor,
|
|
"candidates": spots,
|
|
}
|
|
|
|
|
|
def test_priority_keeps_first_spot_and_next_moves() -> None:
|
|
spot = [{"x": 0.0, "y": 0.0}, {"x": 0.0, "y": 10.0}]
|
|
placed = place_labels([_label("refuge", "대피소", spot), _label("endpoints", "시점", spot)], [])
|
|
by = {item["piece"]: item["spot"] for item in placed}
|
|
assert by["endpoints"] == spot[0] # 시종점이 먼저 — 옛 자리
|
|
assert by["refuge"] == spot[1]
|
|
|
|
|
|
def test_all_blocked_falls_back_to_first() -> None:
|
|
spot = [{"x": 0.0, "y": 0.0}]
|
|
placed = place_labels([_label("ip", "IP1", spot), _label("radius", "R=12", spot)], [])
|
|
assert [item["spot"] for item in placed] == [spot[0], spot[0]] # 글은 남음
|
|
|
|
|
|
def test_obstacle_is_avoided() -> None:
|
|
spots = [{"x": 0.0, "y": 0.0}, {"x": 30.0, "y": 0.0}]
|
|
placed = place_labels([_label("ip", "IP1", spots)], [(-5.0, -5.0, 5.0, 5.0)])
|
|
assert placed[0]["spot"] == spots[1]
|
|
|
|
|
|
def _texts(drawing: dict) -> list[dict]:
|
|
return [
|
|
e
|
|
for e in drawing["entities"]
|
|
if e["type"] == "Text"
|
|
and e["layerId"].startswith("b07-plan-")
|
|
and e["layerId"] != "b07-plan-title"
|
|
]
|
|
|
|
|
|
def _rect(entity: dict) -> tuple:
|
|
shape = entity["shapeData"]
|
|
opts = shape["options"]
|
|
direction = (opts["textDirection"]["x"], opts["textDirection"]["y"])
|
|
return text_rect(
|
|
shape["label"],
|
|
shape["basePoint"]["x"],
|
|
shape["basePoint"]["y"],
|
|
opts["fontSize"],
|
|
opts["textAlign"],
|
|
direction,
|
|
)
|
|
|
|
|
|
def test_crowded_sheet_has_no_overlapping_labels() -> None:
|
|
box = (0.0, 0.0, 300.0, 200.0)
|
|
pieces = {
|
|
"ip": {
|
|
"items": [
|
|
{
|
|
"name": "IP1",
|
|
"apex": [100.0, 100.0],
|
|
"start": [90.0, 100.0],
|
|
"end": [100.0, 110.0],
|
|
},
|
|
{
|
|
"name": "IP2",
|
|
"apex": [103.0, 101.0],
|
|
"start": [95.0, 101.0],
|
|
"end": [103.0, 111.0],
|
|
},
|
|
]
|
|
},
|
|
"radius": {
|
|
"items": [
|
|
{"label": "R=12", "middle": [100.0, 98.0], "dir": [0.0, -1.0]},
|
|
{"label": "R=15", "middle": [103.0, 99.0], "dir": [0.0, -1.0]},
|
|
]
|
|
},
|
|
"drainage": {
|
|
"items": [
|
|
{"x": 101.0, "y": 96.0, "label": "D800 L=8.0"},
|
|
{"x": 102.0, "y": 95.0, "label": "D600 L=6.0"},
|
|
]
|
|
},
|
|
"station": {
|
|
"items": [
|
|
{"x": 50.0, "y": 50.0, "nx": 0.0, "ny": 1.0, "label": "NO.0", "major": True},
|
|
]
|
|
},
|
|
"endpoints": {
|
|
"items": [{"label": "시점 NO.0", "x": 50.0, "y": 50.0, "nx": 0.0, "ny": 1.0}]
|
|
},
|
|
}
|
|
drawing = build_plan_kind_drawing("plan_x", "시험", box, pieces)
|
|
texts = _texts(drawing)
|
|
labels = sorted(e["shapeData"]["label"] for e in texts)
|
|
assert labels == sorted(
|
|
["IP1", "IP2", "R=12", "R=15", "D800 L=8.0", "D600 L=6.0", "NO.0", "시점 NO.0"]
|
|
)
|
|
pairs = [
|
|
(a["shapeData"]["label"], b["shapeData"]["label"])
|
|
for a, b in combinations(texts, 2)
|
|
if overlaps(_rect(a), _rect(b))
|
|
]
|
|
assert pairs == []
|
|
|
|
|
|
def test_far_spot_draws_leader() -> None:
|
|
near = [{"x": 0.0, "y": 0.0}, {"x": 40.0, "y": 0.0, "far": True}]
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_PlanLabels import label_entities
|
|
|
|
placed = place_labels([_label("ip", "IP1", near, (0.0, 0.0))], [(-3.0, -3.0, 3.0, 3.0)])
|
|
entities = label_entities("plan_x", placed)
|
|
assert [e["type"] for e in entities] == ["Text", "PolyLine"]
|