feat(B07): 종단도 장 = 일반측점 30 간격 600 m · Y축 여유 10 mm · 그림 자리 +50 mm (525-1 · 요구 343-3)
- 장 나눔 = 추가 · MC 측점 안 세고 일반측점 30 간격(20 m 면 600 m) · 경계 측점 나눠 가짐 · 넘치면 줄이는 규칙 그대로 - Y축 · 측점표 머리 = 첫 측점 왼쪽 10 mm - 그림 = 작도 영역 좌하단에서 오른쪽 50 · 위 50 mm(도각 · 평면 칸 · 넘침 판정이 같은 장 bbox) - 장 나눔 · 도각을 Engine_Cad_LongSheet.py 로 뗌(Engine_Cad_Long.py 700줄 한도) - 옛 종단 확정본 풂 = 칸 층 찾기 → 매니페스트 long_sheet_rule(2) 비교 — 521-2 뒤 확정본도 자리가 바뀌어 한 번 더 풂 · 낡은 시험(칸 층 기준) 고침 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -35,7 +35,7 @@ TABLE_LINE_COLOR = "#8ea0b5"
|
||||
TABLE_LABEL_COLOR = "#e8edf4"
|
||||
TABLE_VALUE_COLOR = "#ffe066"
|
||||
|
||||
# 종단도 분할 기준: 측점 30개 초과 시 30개 단위(경계 1측점 중복)로 나눈다.
|
||||
# 종단도 분할 기준: 일반측점 30 간격(20 m 면 600 m) 단위(경계 1측점 중복) · 추가 · MC 측점은 안 셈.
|
||||
LONG_SPLIT_STATION_COUNT = 30
|
||||
|
||||
_ENTITY_NS = UUID("f15df4cc-fbb1-4bc9-b04c-63052fe43f96")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""B07 종단도 CAD 조립 — 30측점 N분할 + 그래프 축·격자 + 하단 측점 테이블.
|
||||
"""B07 종단도 CAD 그림 — 그래프 축·격자 + 하단 측점 테이블(장 나눔 · 자리 · 도각 = `_LongSheet`).
|
||||
|
||||
납품 양식:
|
||||
- 그래프 영역: 좌측 Y축(표고 눈금·라벨), 기준선(X축, 최저 표고에서 5m 이상
|
||||
@@ -17,11 +17,8 @@ from typing import Any
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import (
|
||||
DESIGN_COLOR,
|
||||
DESIGN_LAYER_ID,
|
||||
DRAWING_FORMAT,
|
||||
FRAME_LAYER_ID,
|
||||
GROUND_COLOR,
|
||||
GROUND_LAYER_ID,
|
||||
LONG_SPLIT_STATION_COUNT,
|
||||
LONG_TABLE_LAYER_ID,
|
||||
TABLE_LABEL_COLOR,
|
||||
TABLE_LINE_COLOR,
|
||||
@@ -31,7 +28,6 @@ from B07_DesignDetail.B07_DesignDetail_Engine_Cad import (
|
||||
_design_profile_points,
|
||||
_format,
|
||||
_interpolate,
|
||||
_layer,
|
||||
_line_entity,
|
||||
_profile_alignment,
|
||||
_stations,
|
||||
@@ -42,18 +38,7 @@ from B07_DesignDetail.B07_DesignDetail_Engine_Cad import (
|
||||
polyline_entity,
|
||||
station_no_label,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_LongPlan import (
|
||||
LONG_PLAN_LAYER_ID,
|
||||
long_plan_entities,
|
||||
plan_box_overlaps,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Template import (
|
||||
drawing_scale,
|
||||
entities_bbox,
|
||||
frame_entities,
|
||||
scale_fields,
|
||||
usable_area,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Template import drawing_scale
|
||||
from common_util.common_util_route_profile import design_elevation_from_longitudinal
|
||||
|
||||
|
||||
@@ -88,55 +73,7 @@ _LONG_TABLE_ROWS: tuple[tuple[str, str, float], ...] = (
|
||||
)
|
||||
_FONT_SIZE = 2.2
|
||||
_TICK_LEN = 0.8 # 측점 눈금 길이
|
||||
|
||||
|
||||
def _span(stations: list[dict[str, Any]], index: int, take: int) -> dict[str, float]:
|
||||
return {
|
||||
"start_m": float(stations[index]["chainage_m"]),
|
||||
"end_m": float(stations[index + take - 1]["chainage_m"]),
|
||||
}
|
||||
|
||||
|
||||
def _fits_sheet(longitudinal: dict[str, Any], span: dict[str, float]) -> bool:
|
||||
"""이 구간을 고정 축척으로 그렸을 때 A1 작도 영역에 들고 평면 위치도 칸을 안 덮는가."""
|
||||
content = _long_content(longitudinal, "probe", span)
|
||||
bbox = entities_bbox(content)
|
||||
if bbox is None:
|
||||
return True
|
||||
usable_w, usable_h = usable_area()
|
||||
fits = bbox[2] - bbox[0] <= usable_w + 1e-6 and bbox[3] - bbox[1] <= usable_h + 1e-6
|
||||
return fits and not plan_box_overlaps(longitudinal, content, bbox)
|
||||
|
||||
|
||||
def longitudinal_chunks(longitudinal: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
"""측점 30개 단위 분할 목록. 각 항목: {id, label, start_m, end_m}.
|
||||
|
||||
한 장이면 단일 도면(id="longitudinal", 기존 manifest 호환). 여러 장이면
|
||||
경계 측점 1개를 중복시켜 도면 간 선이 이어지게 한다(납품 도면 관례).
|
||||
축척(H 1/1,000 · V 1/200)은 모든 장이 같다 — 작도 영역을 넘으면 측점을 줄여
|
||||
장을 더 나눌 뿐 줄이지 않는다.
|
||||
"""
|
||||
stations = sorted(_stations(longitudinal), key=lambda s: float(s["chainage_m"]))
|
||||
if not stations:
|
||||
return [{"id": "longitudinal", "label": "종단도", "start_m": None, "end_m": None}]
|
||||
spans: list[dict[str, float]] = []
|
||||
index = 0
|
||||
while True:
|
||||
take = min(LONG_SPLIT_STATION_COUNT, len(stations) - index)
|
||||
# ponytail: 한 측점씩 줄이며 다시 잰다 — 넘치는 장에서만 돎
|
||||
while take > 2 and not _fits_sheet(longitudinal, _span(stations, index, take)):
|
||||
take -= 1
|
||||
spans.append(_span(stations, index, take))
|
||||
if index + take >= len(stations):
|
||||
break
|
||||
index += take - 1 # 경계 1측점 중복
|
||||
if len(spans) == 1:
|
||||
return [{"id": "longitudinal", "label": "종단도", "number": 1, "total": 1, **spans[0]}]
|
||||
return [
|
||||
{"id": f"longitudinal_{number}", "label": f"종단도({number})", "number": number, **span}
|
||||
| {"total": len(spans)}
|
||||
for number, span in enumerate(spans, start=1)
|
||||
]
|
||||
_AXIS_GAP = 10.0 # Y축 · 측점표 머리 ↔ 첫 측점 여유(종이 mm · 525-1)
|
||||
|
||||
|
||||
def _long_table_values(
|
||||
@@ -474,7 +411,8 @@ def _long_table_entities(
|
||||
"""종단 테이블: 가로 구분선 + 측점 눈금(세로선 없음) + 세로쓰기 값."""
|
||||
chainages = [float(s["chainage_m"]) * _mm_h() for s in stations]
|
||||
header_width = max(interval_m * _mm_h(), 12.0)
|
||||
left = chainages[0] - header_width
|
||||
header_x = chainages[0] - _AXIS_GAP
|
||||
left = header_x - header_width
|
||||
right = chainages[-1]
|
||||
entities: list[dict[str, Any]] = []
|
||||
|
||||
@@ -507,7 +445,7 @@ def _long_table_entities(
|
||||
)
|
||||
)
|
||||
# 외곽·헤더 구분 세로선 (측점 세로선은 없음)
|
||||
for seed, x in (("v-left", left), ("v-header", chainages[0]), ("v-right", right)):
|
||||
for seed, x in (("v-left", left), ("v-header", header_x), ("v-right", right)):
|
||||
entities.append(
|
||||
_line_entity(
|
||||
f"{drawing_id}:ltgrid:{seed}",
|
||||
@@ -637,7 +575,9 @@ def _long_content(
|
||||
if stations:
|
||||
chainages = [float(s["chainage_m"]) * mm_h for s in stations]
|
||||
entities.extend(
|
||||
_graph_grid_entities(drawing_id, chainages, chainages[0], chainages[-1], datum_m, top_m)
|
||||
_graph_grid_entities(
|
||||
drawing_id, chainages, chainages[0] - _AXIS_GAP, chainages[-1], datum_m, top_m
|
||||
)
|
||||
)
|
||||
|
||||
values = _long_table_values(stations, all_stations, ground_all, longitudinal, interval_m)
|
||||
@@ -657,41 +597,3 @@ def _long_content(
|
||||
)
|
||||
|
||||
return entities
|
||||
|
||||
|
||||
def build_longitudinal_drawing(
|
||||
longitudinal: dict[str, Any], drawing_id: str, chunk: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
"""종단도 청크 하나를 지반선+계획선+그래프 축·격자+측점 테이블로 만든다."""
|
||||
entities = _long_content(longitudinal, drawing_id, chunk)
|
||||
scale = drawing_scale("longitudinal")
|
||||
# A1 도각: 콘텐츠가 이미 종이 mm라 도각도 실치수(1:1)로 두고 위치만 맞춘다.
|
||||
# 그림 좌하단 = 작도영역 좌하단 — 장마다 같은 자리에서 시작한다.
|
||||
bbox = entities_bbox(entities)
|
||||
entities.extend(long_plan_entities(longitudinal, drawing_id, chunk, bbox)) # 521-2
|
||||
if bbox:
|
||||
entities.extend(
|
||||
frame_entities(
|
||||
drawing_id,
|
||||
bbox,
|
||||
fit=False,
|
||||
bottom_left=True,
|
||||
fields={
|
||||
"도면명": "종단면도",
|
||||
**scale_fields(("H", scale["x"]), ("V", scale["y"])),
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
"format": DRAWING_FORMAT,
|
||||
"entities": entities,
|
||||
"layers": [
|
||||
_layer(GROUND_LAYER_ID, "원지반", locked=True),
|
||||
_layer(DESIGN_LAYER_ID, "계획선"),
|
||||
_layer(LONG_GRID_LAYER_ID, "그래프 격자", locked=True),
|
||||
_layer(LONG_TABLE_LAYER_ID, "측점표"),
|
||||
_layer(LONG_PLAN_LAYER_ID, "평면 위치도"),
|
||||
_layer(FRAME_LAYER_ID, "도각", locked=True),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"""B07 종단도 우측 상단 평면 위치도(521-2 · 요구 340-2) — 장마다 작도 영역 우상단 칸 하나.
|
||||
|
||||
칸 = 작도 영역 우상단 5 mm 안쪽 150×100 mm(작도 영역 좌하단 = 그림 좌하단 · `frame_entities`
|
||||
bottom_left). 척도 = 종단 축척 `plan` 칸(None = 자동). 방위표만 도각 층.
|
||||
칸 = 작도 영역 우상단 5 mm 안쪽 150×100 mm(작도 영역 좌하단 = 장 bbox 좌하단 = 그림 좌하단
|
||||
− 50 mm · `LongSheet._sheet_bbox` · `frame_entities` bottom_left). 척도 = 종단 축척 `plan` 칸
|
||||
(None = 자동). 방위표만 도각 층.
|
||||
- 자동: 전체 노선을 칸에 맞춤(분모 500 단위 올림) · 전체 노선 가는 선 · 이 장 구간 굵게.
|
||||
- 척도 정함: 이 장 중심 확대 · 이 장 굵게 · 밖 노선 흐린 파선 · 칸 밖은 잘림.
|
||||
칸 내용 = 노선 · 이 장 시 · 종점 측점 글 · 방위표 · 「평면도 S=1/n」 · 「n/N 장」.
|
||||
@@ -43,7 +44,7 @@ _ROUTE_COLOR = "#7d8590" # 전체 · 밖 노선 — 흐리게
|
||||
|
||||
|
||||
def plan_box(content_bbox: tuple[float, float, float, float]) -> tuple[float, float, float, float]:
|
||||
"""칸 (x0, y0, x1, y1) — 그림 좌표(작도 영역 좌하단 = 그림 좌하단)."""
|
||||
"""칸 (x0, y0, x1, y1) — 그림 좌표(작도 영역 좌하단 = 장 bbox 좌하단)."""
|
||||
usable_w, usable_h = usable_area()
|
||||
x1 = content_bbox[0] + usable_w - _INSET
|
||||
y1 = content_bbox[1] + usable_h - _INSET
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
"""B07 종단도 장 — 일반측점 30 간격(600 m) 나눔 · 장 자리(그림 = 작도 영역 좌하단 + 50 mm) · 도각.
|
||||
|
||||
추가 · MC 측점은 세지 않고 구간 안이면 함께 그린다(525-1 · 요구 343-3). 그림 본체는
|
||||
`Engine_Cad_Long._long_content`.
|
||||
"""
|
||||
|
||||
from bisect import bisect_right
|
||||
from typing import Any
|
||||
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import (
|
||||
DESIGN_LAYER_ID,
|
||||
DRAWING_FORMAT,
|
||||
FRAME_LAYER_ID,
|
||||
GROUND_LAYER_ID,
|
||||
LONG_SPLIT_STATION_COUNT,
|
||||
LONG_TABLE_LAYER_ID,
|
||||
_layer,
|
||||
_stations,
|
||||
infer_station_interval,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Long import LONG_GRID_LAYER_ID, _long_content
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_LongPlan import (
|
||||
LONG_PLAN_LAYER_ID,
|
||||
long_plan_entities,
|
||||
plan_box_overlaps,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Template import (
|
||||
drawing_scale,
|
||||
entities_bbox,
|
||||
frame_entities,
|
||||
scale_fields,
|
||||
usable_area,
|
||||
)
|
||||
|
||||
_SHEET_SHIFT = 50.0 # 그림 = 작도 영역 좌하단에서 오른쪽 · 위로(종이 mm · 525-1)
|
||||
|
||||
|
||||
def _sheet_bbox(
|
||||
bbox: tuple[float, float, float, float] | None,
|
||||
) -> tuple[float, float, float, float] | None:
|
||||
"""그림 bbox 를 좌 · 하로 넓힌 장 기준 — 도각 · 평면 칸 · 넘침 판정이 이 좌하단에 맞춤."""
|
||||
if bbox is None:
|
||||
return None
|
||||
return (bbox[0] - _SHEET_SHIFT, bbox[1] - _SHEET_SHIFT, bbox[2], bbox[3])
|
||||
|
||||
|
||||
def _span(stations: list[dict[str, Any]], index: int, take: int) -> dict[str, float]:
|
||||
return {
|
||||
"start_m": float(stations[index]["chainage_m"]),
|
||||
"end_m": float(stations[index + take - 1]["chainage_m"]),
|
||||
}
|
||||
|
||||
|
||||
def _fits_sheet(longitudinal: dict[str, Any], span: dict[str, float]) -> bool:
|
||||
"""이 구간을 고정 축척으로 그렸을 때 A1 작도 영역에 들고 평면 위치도 칸을 안 덮는가."""
|
||||
content = _long_content(longitudinal, "probe", span)
|
||||
bbox = _sheet_bbox(entities_bbox(content))
|
||||
if bbox is None:
|
||||
return True
|
||||
usable_w, usable_h = usable_area()
|
||||
fits = bbox[2] - bbox[0] <= usable_w + 1e-6 and bbox[3] - bbox[1] <= usable_h + 1e-6
|
||||
return fits and not plan_box_overlaps(longitudinal, content, bbox)
|
||||
|
||||
|
||||
def longitudinal_chunks(longitudinal: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
"""일반측점 30 간격(20 m 면 600 m) 단위 분할 목록. 각 항목: {id, label, start_m, end_m}.
|
||||
|
||||
추가 · MC 측점은 세지 않고 구간 안이면 함께 그린다(525-1 · 요구 343-3).
|
||||
|
||||
한 장이면 단일 도면(id="longitudinal", 기존 manifest 호환). 여러 장이면
|
||||
경계 측점 1개를 중복시켜 도면 간 선이 이어지게 한다(납품 도면 관례).
|
||||
축척(H 1/1,000 · V 1/200)은 모든 장이 같다 — 작도 영역을 넘으면 측점을 줄여
|
||||
장을 더 나눌 뿐 줄이지 않는다.
|
||||
"""
|
||||
stations = sorted(_stations(longitudinal), key=lambda s: float(s["chainage_m"]))
|
||||
if not stations:
|
||||
return [{"id": "longitudinal", "label": "종단도", "start_m": None, "end_m": None}]
|
||||
chainages = [float(s["chainage_m"]) for s in stations]
|
||||
length_m = LONG_SPLIT_STATION_COUNT * infer_station_interval(stations)
|
||||
spans: list[dict[str, float]] = []
|
||||
index = 0
|
||||
while True:
|
||||
take = max(2, bisect_right(chainages, chainages[index] + length_m + 1e-6) - index)
|
||||
take = min(take, len(stations) - index)
|
||||
# ponytail: 한 측점씩 줄이며 다시 잰다 — 넘치는 장에서만 돎
|
||||
while take > 2 and not _fits_sheet(longitudinal, _span(stations, index, take)):
|
||||
take -= 1
|
||||
spans.append(_span(stations, index, take))
|
||||
if index + take >= len(stations):
|
||||
break
|
||||
index += take - 1 # 경계 1측점 중복
|
||||
if len(spans) == 1:
|
||||
return [{"id": "longitudinal", "label": "종단도", "number": 1, "total": 1, **spans[0]}]
|
||||
return [
|
||||
{"id": f"longitudinal_{number}", "label": f"종단도({number})", "number": number, **span}
|
||||
| {"total": len(spans)}
|
||||
for number, span in enumerate(spans, start=1)
|
||||
]
|
||||
|
||||
|
||||
def build_longitudinal_drawing(
|
||||
longitudinal: dict[str, Any], drawing_id: str, chunk: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
"""종단도 청크 하나를 지반선+계획선+그래프 축·격자+측점 테이블로 만든다."""
|
||||
entities = _long_content(longitudinal, drawing_id, chunk)
|
||||
scale = drawing_scale("longitudinal")
|
||||
# A1 도각: 콘텐츠가 이미 종이 mm라 도각도 실치수(1:1)로 두고 위치만 맞춘다.
|
||||
# 그림 좌하단 = 작도영역 좌하단 + (50, 50) mm — 장마다 같은 자리에서 시작한다.
|
||||
bbox = _sheet_bbox(entities_bbox(entities))
|
||||
entities.extend(long_plan_entities(longitudinal, drawing_id, chunk, bbox)) # 521-2
|
||||
if bbox:
|
||||
entities.extend(
|
||||
frame_entities(
|
||||
drawing_id,
|
||||
bbox,
|
||||
fit=False,
|
||||
bottom_left=True,
|
||||
fields={
|
||||
"도면명": "종단면도",
|
||||
**scale_fields(("H", scale["x"]), ("V", scale["y"])),
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
"format": DRAWING_FORMAT,
|
||||
"entities": entities,
|
||||
"layers": [
|
||||
_layer(GROUND_LAYER_ID, "원지반", locked=True),
|
||||
_layer(DESIGN_LAYER_ID, "계획선"),
|
||||
_layer(LONG_GRID_LAYER_ID, "그래프 격자", locked=True),
|
||||
_layer(LONG_TABLE_LAYER_ID, "측점표"),
|
||||
_layer(LONG_PLAN_LAYER_ID, "평면 위치도"),
|
||||
_layer(FRAME_LAYER_ID, "도각", locked=True),
|
||||
],
|
||||
}
|
||||
@@ -40,7 +40,7 @@ from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Development import (
|
||||
development_list,
|
||||
read_development,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Long import (
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_LongSheet import (
|
||||
build_longitudinal_drawing,
|
||||
longitudinal_chunks,
|
||||
)
|
||||
|
||||
@@ -18,6 +18,8 @@ _STAGE_DIR = "B07_DesignDetail"
|
||||
|
||||
_CROSS_ID = re.compile(r"^cross_(\d+)m$")
|
||||
_LONG_ID = re.compile(r"^longitudinal(?:_(\d+))?$")
|
||||
# 종단 장 규칙 — 장 나눔 · 자리가 바뀌면 올림(옛 종단 확정본을 한 번 풂 · 521-2 칸 · 525-1 600 m)
|
||||
LONG_SHEET_RULE = 2
|
||||
# 노선 전장에 한 장씩만 나오는 도면 — 라우터가 원본 자료를 따로 실어 넘긴다.
|
||||
MASS_HAUL_ID = "mass_haul"
|
||||
COVER_ID = "cover"
|
||||
@@ -83,28 +85,16 @@ def _write_manifest(project_root: Path, manifest: dict[str, Any]) -> None:
|
||||
|
||||
|
||||
def release_old_long_sheets(project_root: Path) -> None:
|
||||
"""평면 위치도(521-2) 앞 확정 종단 저장본 — 종단만 모두 한 번 풂(칸 · 장 나눔이 바뀜).
|
||||
"""종단 장 규칙이 바뀌기 전 확정 저장본 — 종단만 모두 한 번 풂(칸 · 장 나눔 · 자리가 바뀜).
|
||||
|
||||
풀린 장의 저장본은 `_write_manifest` 가 걷으니 다음부터는 새 저장본(칸 층 있음)만 남음.
|
||||
매니페스트 `long_sheet_rule` 이 지금 규칙과 다르면 풀고 적음 — 다음부터는 새 규칙
|
||||
저장본만 남음. 풀린 장의 저장본은 `_write_manifest` 가 걷음.
|
||||
"""
|
||||
# 엔진 → 양식 → 이 모듈 차례라 위에서 부르면 돌고 돎
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_LongPlan import LONG_PLAN_LAYER_ID
|
||||
|
||||
manifest = _read_manifest(project_root)
|
||||
confirmed = [
|
||||
(drawing_id, entry)
|
||||
for drawing_id, entry in manifest["drawings"].items()
|
||||
if _LONG_ID.fullmatch(drawing_id) and entry.get("confirmed")
|
||||
]
|
||||
if not confirmed:
|
||||
if manifest.get("long_sheet_rule") == LONG_SHEET_RULE:
|
||||
return
|
||||
# ponytail: 첫 장만 봄 — 옛 장은 한꺼번에 풀리므로 새 장과 섞이지 않음
|
||||
saved = _design_root(project_root) / "drawings" / f"{confirmed[0][0]}.json"
|
||||
try:
|
||||
if f'"{LONG_PLAN_LAYER_ID}"' in saved.read_text(encoding="utf-8"):
|
||||
return
|
||||
except OSError:
|
||||
pass
|
||||
for _drawing_id, entry in confirmed:
|
||||
entry["confirmed"] = False
|
||||
for drawing_id, entry in manifest["drawings"].items():
|
||||
if _LONG_ID.fullmatch(drawing_id):
|
||||
entry["confirmed"] = False
|
||||
manifest["long_sheet_rule"] = LONG_SHEET_RULE
|
||||
_write_manifest(project_root, manifest)
|
||||
|
||||
@@ -19,7 +19,7 @@ from B07_DesignDetail.B07_DesignDetail_Engine_Cad import build_cross_drawing
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Development_Draw import (
|
||||
build_development_drawing,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Long import (
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_LongSheet import (
|
||||
build_longitudinal_drawing,
|
||||
longitudinal_chunks,
|
||||
)
|
||||
|
||||
@@ -1,29 +1,34 @@
|
||||
"""B07 종단도 — 장마다 그림 좌하단 = 작도영역 좌하단 · 고정 축척 · 넘치면 장을 더 나눔 (59-3).
|
||||
"""B07 종단도 — 장마다 그림 좌하단 = 작도영역 좌하단 + 50 mm · 고정 축척 · 넘치면 장 더 나눔 (59-3).
|
||||
|
||||
장 = 일반측점 30 간격(600 m) · 추가 · MC 측점은 안 셈 · Y축 ↔ 첫 측점 10 mm(525-1).
|
||||
|
||||
우측 상단 평면 위치도(521-2) — 칸 자리 · 그래프와 안 겹침 · 장마다 강조 구간 · 척도.
|
||||
"""
|
||||
|
||||
import json
|
||||
import math
|
||||
from uuid import uuid5
|
||||
|
||||
import pytest
|
||||
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import FRAME_LAYER_ID
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Long import (
|
||||
build_longitudinal_drawing,
|
||||
longitudinal_chunks,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import _ENTITY_NS, FRAME_LAYER_ID
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_LongPlan import (
|
||||
LONG_PLAN_LAYER_ID,
|
||||
plan_box_overlaps,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_LongSheet import (
|
||||
build_longitudinal_drawing,
|
||||
longitudinal_chunks,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Template import (
|
||||
drawing_scales_path,
|
||||
entities_bbox,
|
||||
frame_guide_area,
|
||||
usable_area,
|
||||
use_project_templates,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Router_Support_Io import (
|
||||
LONG_SHEET_RULE,
|
||||
_read_manifest,
|
||||
_write_manifest,
|
||||
release_old_long_sheets,
|
||||
@@ -121,13 +126,14 @@ def test_평면_칸_작도영역_우상단_안_겹침_장마다_강조() -> None
|
||||
usable_w, usable_h = usable_area()
|
||||
lows = []
|
||||
for number, sheet in enumerate(sheets, start=1):
|
||||
x0, y0 = sheet["bbox"][0], sheet["bbox"][1]
|
||||
# 작도 영역 좌하단 = 그림 좌하단 − 50 mm(525-1)
|
||||
x0, y0 = sheet["bbox"][0] - 50.0, sheet["bbox"][1] - 50.0
|
||||
box = entities_bbox(sheet["plan"])
|
||||
# 칸 = 작도 영역 우상단 5 mm 안쪽 150×100 mm
|
||||
assert box == pytest.approx(
|
||||
(x0 + usable_w - 155.0, y0 + usable_h - 105.0, x0 + usable_w - 5.0, y0 + usable_h - 5.0)
|
||||
)
|
||||
assert not plan_box_overlaps(longitudinal, sheet["graph"], sheet["bbox"])
|
||||
assert not plan_box_overlaps(longitudinal, sheet["graph"], (x0, y0, *sheet["bbox"][2:]))
|
||||
assert f"{number}/{len(sheets)} 장" in sheet["texts"]
|
||||
assert any(text.startswith("평면도 S=1/") for text in sheet["texts"])
|
||||
assert sheet["bold"] # 이 장 구간 굵게
|
||||
@@ -142,7 +148,8 @@ def test_칸을_덮으면_측점_줄여_장_더_나눔() -> None:
|
||||
with_box = _with_xy(_longitudinal(60, 25.0, 0.1))
|
||||
assert longitudinal_chunks(with_box)[0]["end_m"] < plain[0]["end_m"]
|
||||
for sheet in _plan_sheets(with_box):
|
||||
assert not plan_box_overlaps(with_box, sheet["graph"], sheet["bbox"])
|
||||
x0, y0, x1, y1 = sheet["bbox"]
|
||||
assert not plan_box_overlaps(with_box, sheet["graph"], (x0 - 50.0, y0 - 50.0, x1, y1))
|
||||
|
||||
|
||||
def test_좌표_없으면_칸_없음() -> None:
|
||||
@@ -168,32 +175,77 @@ def test_척도_정하면_이_장_확대_밖_흐린_파선(tmp_path) -> None:
|
||||
assert (min(xs) + max(xs)) / 2.0 == pytest.approx((box[0] + box[2]) / 2.0, abs=1.0)
|
||||
|
||||
|
||||
def test_칸_앞_확정_종단만_한_번_풂(tmp_path) -> None:
|
||||
def test_장_규칙_앞_확정_종단만_한_번_풂(tmp_path) -> None:
|
||||
drawings = tmp_path / "B07_DesignDetail" / "drawings"
|
||||
drawings.mkdir(parents=True)
|
||||
old = {"kind": "longitudinal", "confirmed": True, "file": "x"}
|
||||
_write_manifest(
|
||||
tmp_path,
|
||||
{
|
||||
"long_sheet_rule": LONG_SHEET_RULE - 1, # 옛 규칙(521-2 칸까지)
|
||||
"drawings": {
|
||||
"longitudinal_1": dict(old),
|
||||
"longitudinal_2": dict(old),
|
||||
"cross_00020m": {"kind": "cross", "confirmed": True, "file": "x"},
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
for name in ("longitudinal_1", "longitudinal_2", "cross_00020m"):
|
||||
(drawings / f"{name}.json").write_text('{"layers": []}', encoding="utf-8")
|
||||
release_old_long_sheets(tmp_path)
|
||||
after = _read_manifest(tmp_path)["drawings"]
|
||||
manifest = _read_manifest(tmp_path)
|
||||
after = manifest["drawings"]
|
||||
assert not after["longitudinal_1"]["confirmed"] and not after["longitudinal_2"]["confirmed"]
|
||||
assert after["cross_00020m"]["confirmed"]
|
||||
assert not (drawings / "longitudinal_1.json").exists() # 옛 저장본 걷힘
|
||||
# 새 확정본(칸 층 있음)은 그대로
|
||||
assert manifest["long_sheet_rule"] == LONG_SHEET_RULE
|
||||
# 새 규칙 뒤 확정본은 그대로
|
||||
after["longitudinal_1"] = dict(old)
|
||||
_write_manifest(tmp_path, {"drawings": after})
|
||||
(drawings / "longitudinal_1.json").write_text(
|
||||
json.dumps({"layers": [{"id": LONG_PLAN_LAYER_ID}]}), encoding="utf-8"
|
||||
)
|
||||
_write_manifest(tmp_path, manifest)
|
||||
release_old_long_sheets(tmp_path)
|
||||
assert _read_manifest(tmp_path)["drawings"]["longitudinal_1"]["confirmed"]
|
||||
|
||||
|
||||
# ── 일반측점 30 간격 = 600 m · 축 여유 10 mm · 그림 자리 +50 mm(525-1 · 요구 343-3) ─────────
|
||||
def _with_extras(count: int) -> dict:
|
||||
"""20 m 일반측점 + 추가(irregular) · MC(curve) 측점을 섞은 종단."""
|
||||
longitudinal = _longitudinal(count, 20.0, 0.0)
|
||||
extras = [{"chainage_m": x + 7.0, "kind": "irregular"} for x in range(0, count * 20 - 20, 60)]
|
||||
extras += [{"chainage_m": x + 13.0, "kind": "curve"} for x in range(0, count * 20 - 20, 100)]
|
||||
longitudinal["stations"] = sorted(
|
||||
longitudinal["stations"] + extras, key=lambda s: s["chainage_m"]
|
||||
)
|
||||
return longitudinal
|
||||
|
||||
|
||||
def test_추가_MC_측점은_안_세고_600m_로_나눔() -> None:
|
||||
longitudinal = _with_extras(70) # 0 ~ 1,380 m
|
||||
chunks = longitudinal_chunks(longitudinal)
|
||||
assert [(c["start_m"], c["end_m"]) for c in chunks] == [
|
||||
(0.0, 600.0),
|
||||
(600.0, 1200.0),
|
||||
(1200.0, 1380.0),
|
||||
]
|
||||
regular = [s["chainage_m"] for s in longitudinal["stations"] if "kind" not in s]
|
||||
for chunk in chunks[:-1]:
|
||||
inside = [x for x in regular if chunk["start_m"] < x <= chunk["end_m"]]
|
||||
assert len(inside) == 30 # 경계 측점은 앞 장과 나눠 가짐
|
||||
|
||||
|
||||
def test_Y축_첫_측점_사이_10mm_그림_자리_50mm() -> None:
|
||||
longitudinal = _longitudinal(40, 20.0, 0.05)
|
||||
chunk = longitudinal_chunks(longitudinal)[0]
|
||||
entities = build_longitudinal_drawing(longitudinal, "t", chunk)["entities"]
|
||||
by_id = {e["id"]: e for e in entities}
|
||||
|
||||
def start_x(seed: str) -> float:
|
||||
return by_id[str(uuid5(_ENTITY_NS, seed))]["shapeData"]["startPoint"]["x"]
|
||||
|
||||
assert start_x("t:lgv0") - start_x("t:lgy") == pytest.approx(10.0)
|
||||
assert start_x("t:ltgrid:v-header") == pytest.approx(start_x("t:lgy"))
|
||||
content = entities_bbox(
|
||||
[e for e in entities if e["layerId"] not in (FRAME_LAYER_ID, LONG_PLAN_LAYER_ID)]
|
||||
)
|
||||
area = frame_guide_area(entities)
|
||||
assert content[0] - area[0] == pytest.approx(50.0)
|
||||
assert content[1] - area[1] == pytest.approx(50.0)
|
||||
|
||||
Reference in New Issue
Block a user