"""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), ], }