fix(B07): 유역 해칭·번호 배지를 넣고 토적도 balloon 숫자를 뺀다

화면 실측에서 나온 지적 세 가지를 반영했다.

- 세부유역에 격자 해칭(Hatch style=cross, 유역색)을 넣는다. openwebcad Hatch
  엔티티라 CAD에서 무늬·간격을 그대로 편집할 수 있다.
- 유역 번호를 동그라미 배지로 바꾼다 — 흰 원판(solid 해치) + 유역색 테두리 +
  검은 숫자. 해칭 위에서도 읽히고, 화면(어두운 배경)·인쇄(흰 배경) 양쪽에서 남는다.
  배지는 엔티티 목록 맨 뒤에 몰아 그려 뒤따르는 해칭·정보표에 가리지 않는다.
- 토적도 balloon의 장비명 뒤 숫자를 뺀다. 납품 도면에는 "도자 11"처럼 붙지만
  그 숫자의 정체가 확인되지 않았다(2026-08-30 사용자 확정 — 확인되면 붙인다).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-30 13:31:44 +09:00
co-authored by Claude Opus 5
parent 30a7553447
commit 93f7786c62
2 changed files with 95 additions and 15 deletions
@@ -14,9 +14,12 @@
좌표 규약: 종이 mm = (사업지 좌표 m - 콘텐츠 최소점) x MM (1/6,000 -> 1 m = 1/6 mm).
"""
import math
from typing import Any
from uuid import uuid5
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import (
_ENTITY_NS,
DRAWING_FORMAT,
FRAME_LAYER_ID,
TABLE_LABEL_COLOR,
@@ -61,8 +64,18 @@ BASIN_COLORS = (
"#b794f6",
)
# 유역 채칭 — 납품 도면은 유역마다 격자 해칭이 들어간다(2026-08-30 사용자 지적).
HATCH_STYLE = "cross"
HATCH_SPACING_MM = 2.5
HATCH_ANGLE_RAD = math.pi / 4
_FONT_SIZE = 2.2
_NUMBER_FONT_SIZE = 4.0
# 유역 번호는 해칭 위에 얹히므로 흰 원판을 깔고 그 안에 적는다(2026-08-30 사용자 지시).
_NUMBER_FONT_SIZE = 4.5
_BADGE_RADIUS_MM = 3.6
_BADGE_FILL = "#ffffff"
_BADGE_TEXT_COLOR = "#111111"
_BADGE_SEGMENTS = 24
_TITLE_FONT_SIZE = 7.0
# 구역별 정보표 치수(mm).
@@ -124,6 +137,76 @@ def _diameter_label(props: dict[str, Any]) -> str:
return "-"
def _hatch_entity(
drawing_id: str, number: int, ring: list[tuple[float, float]], color: str
) -> dict[str, Any]:
"""유역 안쪽 격자 해칭. openwebcad Hatch 엔티티라 CAD에서 무늬·간격을 그대로 편집한다."""
return {
"id": str(uuid5(_ENTITY_NS, f"{drawing_id}:basin:hatch:{number}")),
"type": "Hatch",
"lineColor": color,
"lineWidth": 1,
"layerId": BASIN_LAYER_ID,
"shapeData": {
"points": [{"x": x, "y": y} for x, y in ring],
"options": {
"style": HATCH_STYLE,
"color": color,
"spacing": HATCH_SPACING_MM,
"angle": HATCH_ANGLE_RAD,
},
},
}
def _number_badge_entities(
drawing_id: str, number: int, center: tuple[float, float], color: str
) -> list[dict[str, Any]]:
"""유역 번호를 동그라미 배지로 그린다 — 흰 원판 + 유역색 테두리 + 검은 숫자.
해칭 위에 그냥 문자만 올리면 무늬에 묻혀 읽히지 않는다. 원판은 solid 해치라
화면(어두운 배경)과 인쇄(흰 배경) 어느 쪽에서도 숫자가 남는다.
"""
cx, cy = center
disc = [
(
cx + _BADGE_RADIUS_MM * math.cos(2 * math.pi * i / _BADGE_SEGMENTS),
cy + _BADGE_RADIUS_MM * math.sin(2 * math.pi * i / _BADGE_SEGMENTS),
)
for i in range(_BADGE_SEGMENTS)
]
return [
{
"id": str(uuid5(_ENTITY_NS, f"{drawing_id}:basin:badge:fill:{number}")),
"type": "Hatch",
"lineColor": _BADGE_FILL,
"lineWidth": 1,
"layerId": BASIN_LAYER_ID,
"shapeData": {
"points": [{"x": x, "y": y} for x, y in disc],
"options": {"style": "solid", "color": _BADGE_FILL, "spacing": 1, "angle": 0},
},
},
{
"id": str(uuid5(_ENTITY_NS, f"{drawing_id}:basin:badge:ring:{number}")),
"type": "Circle",
"lineColor": color,
"lineWidth": 1,
"layerId": BASIN_LAYER_ID,
"shapeData": {"center": {"x": cx, "y": cy}, "radius": _BADGE_RADIUS_MM},
},
_text_entity(
f"{drawing_id}:basin:number:{number}",
str(number),
cx,
cy,
BASIN_LAYER_ID,
_NUMBER_FONT_SIZE,
_BADGE_TEXT_COLOR,
),
]
def _info_box_entities(
drawing_id: str,
number: int,
@@ -277,6 +360,7 @@ def build_watershed_drawing(
entities.append(route)
map_bbox = entities_bbox(entities)
badges: list[dict[str, Any]] = []
for order, basin in enumerate(basins):
ring = [paper(point) for point in basin.get("ring") or []]
if len(ring) < 3:
@@ -284,23 +368,14 @@ def build_watershed_drawing(
props = basin.get("props") or {}
number = int(props.get("index") or order + 1)
color = BASIN_COLORS[(number - 1) % len(BASIN_COLORS)]
entities.append(_hatch_entity(drawing_id, number, ring, color))
outline = polyline_entity(
drawing_id, [*ring, ring[0]], BASIN_LAYER_ID, color, suffix=f":basin:{number}"
)
if outline:
entities.append(outline)
center = _centroid(ring)
entities.append(
_text_entity(
f"{drawing_id}:basin:number:{number}",
f"({number})",
center[0],
center[1],
BASIN_LAYER_ID,
_NUMBER_FONT_SIZE,
color,
)
)
# 번호 배지는 맨 마지막에 몰아 그린다 — 뒤에 오는 유역 해칭·정보표에 가리면 안 된다.
badges.extend(_number_badge_entities(drawing_id, number, _centroid(ring), color))
# 정보표는 지형 콘텐츠 오른쪽에 위에서부터 쌓는다.
if map_bbox:
@@ -323,6 +398,8 @@ def build_watershed_drawing(
)
)
entities.extend(badges) # 그리기 순서상 가장 위
bbox = entities_bbox(entities)
if bbox:
min_bx, _min_by, max_bx, max_by = bbox
@@ -4,7 +4,8 @@
- 제목 「유 토 곡 선」, 우측 상단 척도 표기(H=1:2,000 / V=1:50,000).
- 좌측 세로축(빨강) + 5,000㎥ 눈금·라벨, 누가토량 0 기준선.
- 유토곡선(자홍) + 블록 평형선(흰색) + 띠 경계현(노랑).
- 띠마다 balloon(장비별 육각/타원/사각) — `장비 번호 / Q= / L= / EA= / RR= / BR=`.
- 띠마다 balloon(장비별 육각/타원/사각) — `장비 / Q= / L= / EA= / RR= / BR=`.
(납품 도면은 장비명 뒤에 숫자가 붙지만 정체 미확인이라 적지 않는다.)
- 사토·토취 balloon — `사토 번호 / Q= / M.N= / EA= / RR= / BR=`.
- 하단 2행 테이블: 누가토량(세로쓰기) / 측점(No. 표기).
@@ -279,8 +280,10 @@ def _band_entities(
)
equipment = str(band.get("equipment") or "")
label = EQUIPMENT_LABEL.get(equipment, "운반")
# 납품 도면은 장비명 뒤에 숫자가 붙지만(도자 11 등) 그 숫자의 정체가 확인되지
# 않아 적지 않는다 (2026-08-30 사용자 확정 — 확인되면 그때 붙인다).
lines = [
f"{label} {index}",
label,
f"Q={_format(_number(band.get('volume_m3')))}M3",
f"L={_format(_number(band.get('haul_distance_m')))}M",
f"EA={_format(_number(band.get('ea_m3')))}M3",