fix(B07): 유역도에서 노선을 살리고 방위표·정보표 배치와 절취면을 정리한다
노선이 유역 해칭 아래에 깔려 보이지 않았다. polyline_entity에 width를 더해 노선만 굵게(3) 그리고, 그리기 순서를 해칭·정보표 다음으로 옮긴다. bbox 계산에는 그대로 포함시켜 오른쪽 칸 위치는 흔들리지 않는다. 오른쪽 칸을 방위표(맨 위) → 유역 정보표(아래로 쌓기) 순으로 바꾸고 방위표를 14mm에서 26mm로 키운다. 도면 오른쪽 위 구석에 따로 놓던 옛 배치는 걷어낸다. 등고선·세류선 절취가 경계 바깥 점을 하나씩 물고 나와 외곽이 들쭉날쭉했다. Liang-Barsky 선분 절취로 바꿔 끝점이 경계 위에 정확히 놓이게 하고, 그만큼 두었던 물림 여유(_CLIP_SLACK 14mm)를 없앤다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -60,12 +60,13 @@ def _line_entity(
|
||||
end: tuple[float, float],
|
||||
layer_id: str,
|
||||
color: str,
|
||||
width: int = 1,
|
||||
) -> dict[str, Any]:
|
||||
return {
|
||||
"id": str(uuid5(_ENTITY_NS, seed)),
|
||||
"type": "Line",
|
||||
"lineColor": color,
|
||||
"lineWidth": 1,
|
||||
"lineWidth": width,
|
||||
"layerId": layer_id,
|
||||
"shapeData": {
|
||||
"startPoint": {"x": start[0], "y": start[1]},
|
||||
@@ -81,6 +82,7 @@ def polyline_entity(
|
||||
color: str,
|
||||
suffix: str = "",
|
||||
dash: list[int] | None = None,
|
||||
width: int = 1,
|
||||
) -> dict[str, Any] | None:
|
||||
"""점열을 openwebcad PolyLine(자식 Line 묶음)으로 직렬화한다 (점 2개 미만이면 None)."""
|
||||
if len(points) < 2:
|
||||
@@ -89,7 +91,7 @@ def polyline_entity(
|
||||
children = []
|
||||
for index in range(len(points) - 1):
|
||||
child = _line_entity(
|
||||
f"{seed_base}:{index}", points[index], points[index + 1], layer_id, color
|
||||
f"{seed_base}:{index}", points[index], points[index + 1], layer_id, color, width
|
||||
)
|
||||
if dash:
|
||||
child["lineDash"] = dash
|
||||
|
||||
@@ -86,7 +86,13 @@ _BOX_GAP = 3.0
|
||||
_BOX_MARGIN = 12.0 # 지형 콘텐츠 오른쪽 여백
|
||||
_BOX_ROWS = (("유역면적", "area"), ("유역표고", "relief"), ("유하거리", "flow"))
|
||||
_TITLE_BAND = 22.0 # 제목·척도가 차지하는 위쪽 띠(mm)
|
||||
_CLIP_SLACK = 14.0 # 절취 경계 물림 여유(mm)
|
||||
_CLIP_SLACK = 0.0 # 절취가 경계에서 정확히 끊기므로 여유가 필요 없다(2026-08-31)
|
||||
|
||||
# 오른쪽 칸: 방위표를 맨 위에 놓고 그 아래로 유역 정보표를 쌓는다(2026-08-31 사용자 지시).
|
||||
_COMPASS_SIZE = 26.0 # 방위표 긴 변(mm)
|
||||
_COMPASS_GAP = 6.0 # 방위표와 첫 정보표 사이(mm)
|
||||
# 노선은 지형·유역 위에 얹혀야 보인다 — 굵기도 배경선보다 굵게 한다.
|
||||
_ROUTE_WIDTH = 3
|
||||
|
||||
|
||||
def map_area_mm() -> tuple[float, float]:
|
||||
@@ -307,13 +313,15 @@ def build_watershed_drawing(
|
||||
)
|
||||
if stream:
|
||||
entities.append(stream)
|
||||
# 노선은 지형·유역 위에 그려야 보인다 — 여기서 만들어 두고 맨 마지막에 얹는다.
|
||||
route = polyline_entity(
|
||||
drawing_id, [paper(point) for point in route_xy], ROUTE_LAYER_ID, ROUTE_COLOR
|
||||
drawing_id,
|
||||
[paper(point) for point in route_xy],
|
||||
ROUTE_LAYER_ID,
|
||||
ROUTE_COLOR,
|
||||
width=_ROUTE_WIDTH,
|
||||
)
|
||||
if route:
|
||||
entities.append(route)
|
||||
|
||||
map_bbox = entities_bbox(entities)
|
||||
map_bbox = entities_bbox([*entities, route] if route else entities)
|
||||
badges: list[dict[str, Any]] = []
|
||||
for order, basin in enumerate(basins):
|
||||
ring = [paper(point) for point in basin.get("ring") or []]
|
||||
@@ -331,12 +339,20 @@ def build_watershed_drawing(
|
||||
# 번호 배지는 맨 마지막에 몰아 그린다 — 뒤에 오는 유역 해칭·정보표에 가리면 안 된다.
|
||||
badges.extend(_number_badge_entities(drawing_id, number, _centroid(ring), color))
|
||||
|
||||
# 정보표는 지형 콘텐츠 오른쪽에 위에서부터 쌓는다.
|
||||
# 오른쪽 칸은 방위표가 맨 위, 그 아래로 유역 정보표를 쌓는다.
|
||||
if map_bbox:
|
||||
box_x = map_bbox[2] + _BOX_MARGIN
|
||||
box_y = map_bbox[3]
|
||||
column_top = map_bbox[3]
|
||||
else:
|
||||
box_x, box_y = 0.0, 0.0
|
||||
box_x, column_top = 0.0, 0.0
|
||||
entities.extend(
|
||||
compass_entities(
|
||||
drawing_id,
|
||||
(box_x + _BOX_WIDTH / 2.0, column_top - _COMPASS_SIZE / 2.0),
|
||||
_COMPASS_SIZE,
|
||||
)
|
||||
)
|
||||
box_y = column_top - _COMPASS_SIZE - _COMPASS_GAP
|
||||
for order, basin in enumerate(basins):
|
||||
props = basin.get("props") or {}
|
||||
number = int(props.get("index") or order + 1)
|
||||
@@ -352,6 +368,8 @@ def build_watershed_drawing(
|
||||
)
|
||||
)
|
||||
|
||||
if route:
|
||||
entities.append(route) # 지형·유역 위
|
||||
entities.extend(badges) # 그리기 순서상 가장 위
|
||||
|
||||
bbox = entities_bbox(entities)
|
||||
@@ -380,7 +398,6 @@ def build_watershed_drawing(
|
||||
align="right",
|
||||
)
|
||||
)
|
||||
entities.extend(compass_entities(drawing_id, (max_bx - 8.0, max_by - 8.0), 14.0))
|
||||
entities.extend(
|
||||
frame_entities(
|
||||
drawing_id, entities_bbox(entities) or bbox, fit=False, fields={"도면명": "유역도"}
|
||||
|
||||
@@ -137,28 +137,65 @@ def _geometry_lines(geometry: Any) -> list[list[tuple[float, float]]]:
|
||||
return []
|
||||
|
||||
|
||||
def _clip_segment(
|
||||
start: tuple[float, float],
|
||||
end: tuple[float, float],
|
||||
box: tuple[float, float, float, float],
|
||||
) -> tuple[tuple[float, float], tuple[float, float]] | None:
|
||||
"""선분에서 상자 안에 드는 부분만 돌려준다(Liang-Barsky). 겹치지 않으면 None."""
|
||||
min_x, min_y, max_x, max_y = box
|
||||
dx = end[0] - start[0]
|
||||
dy = end[1] - start[1]
|
||||
t0, t1 = 0.0, 1.0
|
||||
for numerator, denominator in (
|
||||
(start[0] - min_x, -dx),
|
||||
(max_x - start[0], dx),
|
||||
(start[1] - min_y, -dy),
|
||||
(max_y - start[1], dy),
|
||||
):
|
||||
if denominator == 0.0:
|
||||
if numerator < 0.0:
|
||||
return None # 경계와 나란한데 바깥이다
|
||||
continue
|
||||
t = numerator / denominator
|
||||
if denominator < 0.0:
|
||||
if t > t1:
|
||||
return None
|
||||
t0 = max(t0, t)
|
||||
else:
|
||||
if t < t0:
|
||||
return None
|
||||
t1 = min(t1, t)
|
||||
return (
|
||||
(start[0] + t0 * dx, start[1] + t0 * dy),
|
||||
(start[0] + t1 * dx, start[1] + t1 * dy),
|
||||
)
|
||||
|
||||
|
||||
def clip_line_to_box(
|
||||
line: list[tuple[float, float]], box: tuple[float, float, float, float]
|
||||
) -> list[list[tuple[float, float]]]:
|
||||
"""범위 안에 든 연속 구간만 조각으로 잘라 낸다(경계 한 점씩 물려 끊긴 티를 줄인다).
|
||||
"""범위 안에 든 부분만 조각으로 잘라 낸다 — 끝점은 경계 위에 정확히 놓인다.
|
||||
|
||||
도엽 등고선 한 줄은 도엽 끝까지 이어지므로, 걸치면 통째로 남기면 도면이 A1을 넘는다.
|
||||
예전 구현은 경계 바깥 점을 하나씩 물고 나와 외곽선이 들쭉날쭉했다(2026-08-31 사용자
|
||||
지적). 이제 교차점을 계산해 자르므로 절취면이 곧게 떨어진다.
|
||||
"""
|
||||
min_x, min_y, max_x, max_y = box
|
||||
runs: list[list[tuple[float, float]]] = []
|
||||
current: list[tuple[float, float]] = []
|
||||
for index, point in enumerate(line):
|
||||
x, y = point
|
||||
if min_x <= x <= max_x and min_y <= y <= max_y:
|
||||
if not current and index > 0:
|
||||
current.append(line[index - 1]) # 바깥쪽 직전 점까지 물린다
|
||||
current.append(point)
|
||||
elif current:
|
||||
current.append(point)
|
||||
runs.append(current)
|
||||
for start, end in zip(line, line[1:]):
|
||||
piece = _clip_segment(start, end, box)
|
||||
if piece is None:
|
||||
current = []
|
||||
if current:
|
||||
runs.append(current)
|
||||
continue
|
||||
head, tail = piece
|
||||
if head == tail:
|
||||
continue # 경계에 점으로만 닿았다
|
||||
if current and current[-1] == head:
|
||||
current.append(tail)
|
||||
else:
|
||||
current = [head, tail]
|
||||
runs.append(current)
|
||||
return [run for run in runs if len(run) >= 2]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user