From fa7b4e515ca8f8a26b1c7acaa757231c4c1c5fa2 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 8 Sep 2026 20:06:11 +0900 Subject: [PATCH] =?UTF-8?q?feat(B07):=20=ED=91=9C=EC=A4=80=EB=8F=84=20?= =?UTF-8?q?=EA=B8=B0=EC=9A=B8=EA=B8=B0=EB=A5=BC=20=ED=8C=90=EC=A0=95?= =?UTF-8?q?=EA=B0=92=EC=9C=BC=EB=A1=9C=20=E2=80=94=20=EA=B7=B8=EB=A6=BC?= =?UTF-8?q?=C2=B7=EC=9E=A5=20=EC=A0=9C=EB=AA=A9=EC=9D=B4=20B08=20=ED=95=9C?= =?UTF-8?q?=20=EB=B2=8C=EC=9D=84=20=EA=B7=B8=EB=8C=80=EB=A1=9C=20=EB=B6=80?= =?UTF-8?q?=EB=A6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 데스크탑 메인이 넣은 품셈 표준경사 판정(13-4-4 [주]⑪)이 **근거 문구 안에만** 있었고 표준도는 못 읽고 있었음. 판정 함수 `face_slope_ratio()` 를 그대로 부르게 바꿈 — 판정을 두 벌로 짜면 그림과 수량이 갈림(CLAUDE.md 5장). 실측 (프로젝트 936be972 구조물 5기) 돌쌓기(찰) H=2.5 1:0.3 뒷길이 45㎝ 깬돌 단면 (0,0)(0.75,2.5)(1.45,2.5)(1.45,0) 돌쌓기(메) H=2.0 1:0.35 뒷길이 35㎝ 야면석 단면 (0,0)(0.70,2.0)(1.35,2.0)(1.25,0) 큰돌쌓기 H=2.5 1:0.3 옹벽 판정 대상 아님 — 안 적음 ⇒ **메쌓기가 찰쌓기와 다른 값으로 갈림**(0.35 대 0.3). 장 제목에 그대로 뜸. 메/찰은 종류에서 옴. 큰돌쌓기는 `bond` 칸이 그것이고 안 고르면 찰쌓기로 봄 — 품셈 13-6 은 둘 다 「1:0.3 이상」이라 그림 기울기가 갈리지 않음. 기울기 근거 문구를 그림 아래 한 줄로 실음 — 「사용자 지정」인지 「품셈 표준경사」인지, 그리고 「성토/절토가 저장에 없어 성토 열로 잠정」인지가 도면에서 보여야 함. ⚠ 시험 제원에서 `face_slope_ratio` 를 뺐음 — 실제 구조물 제원에 그 칸이 없는데 시험만 갖고 있어 **판정 경로가 한 번도 안 돌았음**(늘 「사용자 지정」으로 빠짐). 빼고 나니 메/찰이 갈리는 것이 시험으로 잡힘. 사용자 지정이 이기는 것도 따로 검사함. 자체검증 — 회귀 573 통과 · 0 실패. Co-Authored-By: Claude Opus 5 (1M context) --- ..._DesignDetail_Engine_Cad_StandardFigure.py | 56 ++++++++++++------- .../B07_DesignDetail_Engine_Standard_Sheet.py | 18 +++++- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/B07_DesignDetail/B07_DesignDetail_Engine_Cad_StandardFigure.py b/B07_DesignDetail/B07_DesignDetail_Engine_Cad_StandardFigure.py index 4f67dac4..30b56f71 100644 --- a/B07_DesignDetail/B07_DesignDetail_Engine_Cad_StandardFigure.py +++ b/B07_DesignDetail/B07_DesignDetail_Engine_Cad_StandardFigure.py @@ -3,9 +3,10 @@ 실무 원본 탭이 「위 치수조서 + 아래 수량산출서」인데, 아래 표는 1단계에서 섰고 여기가 위다. ⚠ **기울기를 값으로 받는다 — 0.3 을 박지 않는다.** - 품셈 표준경사 표 자동 판정이 다른 창에서 붙는 중이고, 붙으면 이 그림이 **그 값으로** - 기울어져야 한다. `face_slope_ratio` 가 비면(=자동 미구현·미입력) 교본 7-3 기본 0.3 으로 - 그리되 **그 사실을 그림에 적는다**. + 판정은 **B08 `face_slope_ratio()` 한 벌**을 그대로 부른다(품셈 13-4-4 [주]⑪ 표준경사 — + 직고·메찰·성절토). 사용자가 정했으면 그 값이 이긴다(확정 ⑨). 그림은 **수량이 쓴 바로 + 그 값**으로 기울고, 근거 문구도 같이 받아 그림에 적는다 — 판정을 두 벌로 짜면 그림과 + 수량이 갈린다. ⚠ **수량이 쓰는 그 상수로 그린다** — `B08_Quantity_Engine_UnitQuantity.STONE_MASONRY` 를 직접 읽는다. 치수를 여기서 다시 적으면 **그림과 표가 갈린다**(CLAUDE.md 5장). @@ -31,7 +32,7 @@ from B07_DesignDetail.B07_DesignDetail_Engine_Cad import ( _text_entity, polyline_entity, ) -from B08_Quantity.B08_Quantity_Engine_UnitQuantity import STONE_MASONRY +from B08_Quantity.B08_Quantity_Engine_UnitQuantity import STONE_MASONRY, face_slope_ratio #: 그림 축척 — 1/25(1m = 40㎜). 표(줄 높이 9㎜) 위에 얹어도 한 면에 드는 크기다. SCALE_MM_PER_M = 40.0 @@ -39,19 +40,25 @@ SCALE_MM_PER_M = 40.0 #: 이 그림을 그리는 종류. 옹벽·집수정처럼 단면이 다른 것은 아직 그리지 않는다. FIGURE_TYPE_IDS: frozenset[str] = frozenset({"masonry_wet", "masonry_dry", "boulder_masonry"}) -#: 기울기를 못 받았을 때 쓰는 값 — 교본 7-3 「돌 찰쌓기 3.0m 이하 1:0.3」. -DEFAULT_SLOPE_RATIO = 0.3 - _LABEL_FONT = 4.0 _DIM_FONT = 3.4 -def slope_ratio_of(options: dict[str, Any] | None) -> tuple[float, bool]: - """전면 기울기와 **그것이 입력된 값인지**. 비어 있으면 `(0.3, False)`.""" - raw = (options or {}).get("face_slope_ratio") - if isinstance(raw, (int, float)) and float(raw) > 0: - return float(raw), True - return DEFAULT_SLOPE_RATIO, False +def slope_of(sheet: dict[str, Any]) -> tuple[float, str]: + """장 하나의 전면 기울기와 근거 문구 — **판정은 B08 한 벌**을 그대로 쓴다. + + 메/찰은 종류에서 온다. 큰돌쌓기는 `bond` 칸(메쌓기/찰쌓기)이 그것이고, 안 고르면 + 찰쌓기로 본다 — 품셈 13-6 은 둘 다 「1:0.3 **이상**」이라 그림 기울기가 갈리지 않는다. + """ + options = sheet.get("options") or {} + type_id = str(sheet.get("type_id") or "") + if type_id == "masonry_dry": + wet = False + elif type_id == "boulder_masonry": + wet = options.get("bond") != "메쌓기" + else: + wet = True + return face_slope_ratio(options, wet=wet, height_m=float(sheet.get("height_m") or 0.0)) def section_points(height_m: float, slope_ratio: float) -> list[tuple[float, float]]: @@ -87,7 +94,7 @@ def build_figure( return [], 0.0 options = sheet.get("options") or {} - slope, slope_given = slope_ratio_of(options) + slope, slope_note = slope_of(sheet) scale = SCALE_MM_PER_M ox, oy = origin @@ -148,11 +155,7 @@ def build_figure( (f"H = {height_m:g} m", (-0.28, height_m / 2.0), "right"), (f"상부 {top_t:.2f} m", (slope * height_m + top_t / 2.0, height_m + 0.14), "center"), (f"하부 {bottom_t:.2f} m", (bottom_t / 2.0, -0.30), "center"), - ( - f"1 : {slope:g}" + ("" if slope_given else " (자동 판정 전 — 교본 기본값)"), - (slope * height_m / 2.0 - 0.30, height_m * 0.72), - "right", - ), + (f"1 : {slope:g}", (slope * height_m / 2.0 - 0.30, height_m * 0.72), "right"), ( f"터파기 폭 {dig_width:.2f} m (평균두께 {average_t:.2f} + 0.20)", (dig_width + 0.16, height_m * 0.92), @@ -185,6 +188,21 @@ def build_figure( ) ) + # 기울기 근거는 **그림 밑에** 한 줄 — 「사용자 지정」인지 「품셈 표준경사」인지 보여야 한다. + note_x, note_y = mm((0.0, -0.62)) + entities.append( + _text_entity( + f"{drawing_id}:fig:slopenote", + f"전면 기울기 — {slope_note}", + note_x, + note_y, + layer_id, + _DIM_FONT, + label_color, + "left", + ) + ) + title_x, title_y = mm((0.0, height_m + 0.52)) entities.append( _text_entity( diff --git a/B07_DesignDetail/B07_DesignDetail_Engine_Standard_Sheet.py b/B07_DesignDetail/B07_DesignDetail_Engine_Standard_Sheet.py index aabf0165..61fc58da 100644 --- a/B07_DesignDetail/B07_DesignDetail_Engine_Standard_Sheet.py +++ b/B07_DesignDetail/B07_DesignDetail_Engine_Standard_Sheet.py @@ -76,6 +76,18 @@ def sheet_key(structure: dict[str, Any]) -> str: return json.dumps(payload, ensure_ascii=False, sort_keys=True) +def _slope_or_none(structure: dict[str, Any]) -> float | None: + """돌쌓기 계열이면 판정된 전면 기울기, 아니면 `None`. 판정은 그림 모듈이 가진 한 벌.""" + from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardFigure import ( + FIGURE_TYPE_IDS, + slope_of, + ) + + if str(structure.get("type_id") or "") not in FIGURE_TYPE_IDS: + return None + return slope_of(structure)[0] + + def sheet_title(structure: dict[str, Any]) -> str: """장 제목 — 「이름 H=2.0 1:0.3 뒷길이 45㎝」처럼 **무엇이 갈랐는지**가 보이게.""" parts = [str(structure.get("name") or structure.get("type_id") or "구조물")] @@ -83,9 +95,11 @@ def sheet_title(structure: dict[str, Any]) -> str: if height > 0: parts.append(f"H={height:g}") options = structure.get("options") or {} - slope = options.get("face_slope_ratio") + # 기울기는 **판정 결과**를 적는다 — 사용자가 안 정해도 품셈 표준경사로 갈린다(확정 ⑨). + # 판정이 안 되는 종류(옹벽 등)는 안 적는다. + slope = _slope_or_none(structure) if slope is not None: - parts.append(f"1:{float(slope):g}") + parts.append(f"1:{slope:g}") back = options.get("back_len_cm") or options.get("stone_back_length_cm") if back: parts.append(f"뒷길이 {int(back)}㎝")