From dd8c86fa492c58a4d5db20ee587b5b4459f7641b Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 9 Sep 2026 23:50:29 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat(B07):=20=ED=91=9C=EC=A4=80=EB=8F=84=20?= =?UTF-8?q?=EA=B7=B8=EB=A6=BC=EC=9D=B4=20=EC=95=88=20=EC=84=9C=EB=8A=94=20?= =?UTF-8?q?=EC=9E=A5=EC=97=90=20=EA=B9=8C=EB=8B=AD=EC=9D=84=20=EC=A0=81?= =?UTF-8?q?=EC=9D=8C=20+=20=ED=81=B0=EB=8F=8C=EC=8C=93=EA=B8=B0=EB=8A=94?= =?UTF-8?q?=20=EA=B7=B8=EB=A6=BC=20=EB=8C=80=EC=83=81=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=BA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 계획서 4-12. **빈 자리를 그냥 두면 사용자가 「고장인가」로 읽음** — 장마다 왜 그림이 없는지와 **무엇을 받아야 서는지**를 표 위에 한 줄 적음. - 돌쌓기(찰) — 「뒷길이를 고르면 그림이 섭니다」 · 옹벽 — 「단면이 달라 아직 안 그림」 ⚠ **큰돌쌓기를 `FIGURE_TYPE_IDS` 에서 뺌** — 그림은 두께를 뒷길이에서 내는데 큰돌쌓기는 규격이 **직경**이라 목록에만 있고 결과는 늘 빈 그림이었음(품셈 13-6 직경 ↔ 13-4 뒷길이, 수량 쪽에서 이미 갈라 둔 자리). **식은 만들지 않음** — 「직경에서 두께를 내는 법」은 도메인 판단이라 사용자 몫이고, 지금은 사유만 띄움. ⚠ 곁가지 하나를 화면에서 잡아 함께 고침 — 그림 대상 목록을 **기울기 판정**에도 쓰고 있어 큰돌쌓기를 빼자 **장 제목의 「1:0.3」까지 사라졌음**. `SLOPE_TYPE_IDS` 로 갈라 되살림. 화면 실측 — 표준도 1장에 「그림 없음 — 큰돌쌓기는 규격이 직경이라 …」 한 줄이 표 위에 뜸. 시험 다섯 추가, 전체 1225 통과·실패 0. Co-Authored-By: Claude Opus 5 (1M context) --- ..._DesignDetail_Engine_Cad_StandardFigure.py | 40 ++++++++- ...7_DesignDetail_Engine_Cad_StandardSheet.py | 23 +++++- .../B07_DesignDetail_Engine_Standard_Sheet.py | 5 +- .../tester/test_b07_standard_figure_reason.py | 81 +++++++++++++++++++ 4 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 resources/tester/test_b07_standard_figure_reason.py diff --git a/B07_DesignDetail/B07_DesignDetail_Engine_Cad_StandardFigure.py b/B07_DesignDetail/B07_DesignDetail_Engine_Cad_StandardFigure.py index 92d55f35..bf871ce8 100644 --- a/B07_DesignDetail/B07_DesignDetail_Engine_Cad_StandardFigure.py +++ b/B07_DesignDetail/B07_DesignDetail_Engine_Cad_StandardFigure.py @@ -50,7 +50,45 @@ from common_util.common_util_excavation import ( SCALE_MM_PER_M = 40.0 #: 이 그림을 그리는 종류. 옹벽·집수정처럼 단면이 다른 것은 아직 그리지 않는다. -FIGURE_TYPE_IDS: frozenset[str] = frozenset({"masonry_wet", "masonry_dry", "boulder_masonry"}) +#: ⚠ **큰돌쌓기(`boulder_masonry`)를 뺐다**(2026-09-09). 여기 있었지만 그림이 두께를 +#: **뒷길이**에서 내는데 큰돌쌓기는 규격 축이 **직경**(`stone_cm`)이라 **영영 못 그렸다** — +#: 목록에만 있고 결과는 늘 빈 그림이었다. 품셈 **13-6(직경) ↔ 13-4(뒷길이)** 로 축이 +#: 갈리는 자리이고 수량 쪽에서 이미 갈라 둔 그것이다(`EXPANDERS` 주석). +#: ⇒ 「직경에서 두께를 내는 식」은 **도메인 판단이라 사용자 몫** — 계획서 4-12 답 대기 줄. +FIGURE_TYPE_IDS: frozenset[str] = frozenset({"masonry_wet", "masonry_dry"}) + +#: **전면 기울기 판정** 대상 — 그림 대상과 **다르다**. 큰돌쌓기는 그림은 못 그려도 +#: 기울기는 판정된다(품셈 13-6 「1:0.3 이상」). 한 목록으로 묶어 두었더니 큰돌쌓기를 +#: 그림에서 뺄 때 **장 제목의 「1:0.3」까지 사라졌다**(2026-09-09 화면 실측에서 잡음). +SLOPE_TYPE_IDS: frozenset[str] = frozenset({"masonry_wet", "masonry_dry", "boulder_masonry"}) + +#: 그림이 못 서는 장에 **까닭을 적는다** — 빈 자리를 그냥 두면 사용자가 「고장」으로 읽는다 +#: (2026-09-09 사용자 지시). 「무엇을 받아야 서는지」까지 적는다. +_NO_FIGURE_REASONS: dict[str, str] = { + "boulder_masonry": ( + "그림 없음 — 큰돌쌓기는 규격이 **직경**이라 두께를 낼 식이 아직 없습니다" + "(뒷길이로 내는 돌쌓기 식과 축이 다름 · 사용자 확정 대기)" + ), + "retaining_wall": "그림 없음 — 옹벽은 단면이 달라 아직 그리지 않습니다(표준도 3단계)", +} +_NO_FIGURE_DEFAULT = "그림 없음 — 이 형식은 아직 단면을 그리지 않습니다" + + +def figure_reason(sheet: dict[str, Any]) -> str | None: + """그림이 **안 서는 까닭** 한 줄. 서는 장이면 `None`. + + ⚠ 「무엇을 받아야 서는지」까지 적는다 — 뒷길이만 고르면 서는 장과, 식 자체가 없는 장은 + 사용자가 할 일이 다르다. + """ + type_id = str(sheet.get("type_id") or "") + if type_id not in FIGURE_TYPE_IDS: + return _NO_FIGURE_REASONS.get(type_id, _NO_FIGURE_DEFAULT) + if float(sheet.get("height_m") or 0.0) <= 0: + return "그림 없음 — 높이가 없습니다(제원에서 높이를 넣으면 그림이 섭니다)" + if back_length_cm(sheet) is None: + return "그림 없음 — **뒷길이를 고르면 그림이 섭니다**(두께가 뒷길이에서 나옵니다)" + return None + _LABEL_FONT = 4.0 _DIM_FONT = 3.4 diff --git a/B07_DesignDetail/B07_DesignDetail_Engine_Cad_StandardSheet.py b/B07_DesignDetail/B07_DesignDetail_Engine_Cad_StandardSheet.py index 4e2389d9..66d2d998 100644 --- a/B07_DesignDetail/B07_DesignDetail_Engine_Cad_StandardSheet.py +++ b/B07_DesignDetail/B07_DesignDetail_Engine_Cad_StandardSheet.py @@ -22,6 +22,7 @@ from typing import Any from B07_DesignDetail.B07_DesignDetail_Engine_Cad import ( DRAWING_FORMAT, + _text_entity, TABLE_LABEL_COLOR, TABLE_LINE_COLOR, TABLE_VALUE_COLOR, @@ -32,7 +33,7 @@ from B07_DesignDetail.B07_DesignDetail_Engine_Cad import ( from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardFigure import ( SCALE_MM_PER_M as FIGURE_SCALE_MM_PER_M, ) -from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardFigure import build_figure +from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardFigure import build_figure, figure_reason from B07_DesignDetail.B07_DesignDetail_Engine_Template import usable_bbox logger = logging.getLogger(__name__) @@ -58,6 +59,8 @@ def sheet_drawing_id(number: int, total: int) -> str: _COLUMN_WIDTHS: tuple[float, ...] = (90.0, 535.0, 70.0, 40.0) _ROW_HEIGHT = 9.0 _FONT = 4.2 +#: 그림이 없는 장에 까닭을 적는 글자 크기(㎜) — 표 글자보다 살짝 작게. +_NOTE_FONT = 4.0 #: 표 사이 간격 — 장이 여럿일 때 서로 붙어 보이지 않게. _GAP = 14.0 #: 그림을 왼쪽 여백에서 조금 들여 놓는다 — 치수 글자가 왼쪽으로 나가기 때문. @@ -181,6 +184,24 @@ def build_standard_drawing(drawing_id: str, label: str, payload: dict[str, Any]) if figure: entities.extend(figure) top -= used + _GAP + else: + # ⚠ **빈 자리를 그냥 두지 않는다** — 사용자가 「고장인가」로 읽는다(2026-09-09 + # 사용자 지시). 왜 안 서는지와 **무엇을 받아야 서는지**를 표 위에 한 줄 적는다. + reason = figure_reason(sheet) + if reason: + entities.append( + _text_entity( + f"{drawing_id}:sheet:{index}:nofigure", + reason.replace("**", ""), + x0, + top - _NOTE_FONT, + STANDARD_LAYER_ID, + _NOTE_FONT, + TABLE_LABEL_COLOR, + "left", + ) + ) + top -= _NOTE_FONT + _GAP grid = _sheet_rows(sheet) entities.append( table_entity( diff --git a/B07_DesignDetail/B07_DesignDetail_Engine_Standard_Sheet.py b/B07_DesignDetail/B07_DesignDetail_Engine_Standard_Sheet.py index 4bfe8988..6d4f3c10 100644 --- a/B07_DesignDetail/B07_DesignDetail_Engine_Standard_Sheet.py +++ b/B07_DesignDetail/B07_DesignDetail_Engine_Standard_Sheet.py @@ -79,11 +79,12 @@ def sheet_key(structure: dict[str, Any]) -> str: def _slope_or_none(structure: dict[str, Any]) -> float | None: """돌쌓기 계열이면 판정된 전면 기울기, 아니면 `None`. 판정은 그림 모듈이 가진 한 벌.""" from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardFigure import ( - FIGURE_TYPE_IDS, + SLOPE_TYPE_IDS, slope_of, ) - if str(structure.get("type_id") or "") not in FIGURE_TYPE_IDS: + # ⚠ **그림 대상 목록을 쓰지 않는다** — 큰돌쌓기는 그림은 못 그려도 기울기는 판정된다. + if str(structure.get("type_id") or "") not in SLOPE_TYPE_IDS: return None return slope_of(structure)[0] diff --git a/resources/tester/test_b07_standard_figure_reason.py b/resources/tester/test_b07_standard_figure_reason.py new file mode 100644 index 00000000..047bdd70 --- /dev/null +++ b/resources/tester/test_b07_standard_figure_reason.py @@ -0,0 +1,81 @@ +"""표준도 — 그림이 **안 서는 까닭을 화면에 드러낸다** (2026-09-09 사용자 지시). + +빈 자리를 그냥 두면 사용자가 「고장인가」로 읽는다. 장마다 **왜 그림이 없는지와 무엇을 +받아야 서는지**를 표 위에 한 줄 적는다. + +⚠ **큰돌쌓기를 그림 대상에서 뺐다** — 그림은 두께를 **뒷길이**에서 내는데 큰돌쌓기는 규격이 + **직경**이라 영영 못 그렸다(품셈 13-6 직경 ↔ 13-4 뒷길이). 식은 만들지 않는다 — + 「직경에서 두께를 내는 법」은 도메인 판단이라 사용자 몫이다. +⚠ 그림 대상과 **기울기 판정 대상은 다른 목록**이다 — 한 벌로 묶었더니 큰돌쌓기를 그림에서 + 뺄 때 **장 제목의 「1:0.3」까지 사라졌다**(화면 실측에서 잡음). +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +if str(ROOT) not in sys.path: + sys.path.insert(0, str(ROOT)) + +from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardFigure import ( # noqa: E402 + FIGURE_TYPE_IDS, + SLOPE_TYPE_IDS, + figure_reason, +) +from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardSheet import ( # noqa: E402 + build_standard_drawing, +) +from B07_DesignDetail.B07_DesignDetail_Engine_Standard_Sheet import sheet_title # noqa: E402 + + +def _sheet(type_id: str, **options) -> dict: + return { + "key": type_id, + "title": type_id, + "type_id": type_id, + "height_m": 2.5, + "options": {"height_m": 2.5, **options}, + "rows": [{"name": "콘크리트", "unit": "㎥", "amount": 1.0, "basis": "시험"}], + "member_count": 1, + "unit_label": "m당", + } + + +def test_뒷길이가_없으면_무엇을_고르면_되는지_적는다() -> None: + reason = figure_reason(_sheet("masonry_wet")) + assert reason is not None and "뒷길이" in reason and "섭니다" in reason + + +def test_뒷길이가_있으면_까닭이_없다() -> None: + assert figure_reason(_sheet("masonry_dry", back_len_cm=35)) is None + + +def test_큰돌쌓기는_그림_대상에서_빠지고_까닭이_적힌다() -> None: + assert "boulder_masonry" not in FIGURE_TYPE_IDS + reason = figure_reason(_sheet("boulder_masonry", stone_cm="60~80")) + assert reason is not None and "직경" in reason + + +def test_기울기_판정은_큰돌쌓기에도_남는다() -> None: + """그림 대상 목록으로 판정하면 장 제목에서 「1:0.3」이 사라진다 — 목록을 갈라 둔 자리.""" + assert "boulder_masonry" in SLOPE_TYPE_IDS + title = sheet_title( + {"name": "큰돌쌓기", "type_id": "boulder_masonry", "height_m": 2.5, "options": {}} + ) + assert "1:0.3" in title + + +def test_까닭이_도면에_한_줄로_실린다() -> None: + drawing = build_standard_drawing( + "standard_sheet_1", + "표준도 1장", + {"sheets": [_sheet("retaining_wall")], "structure_count": 1}, + ) + texts = [ + (entity.get("shapeData") or {}).get("label", "") + for entity in drawing["entities"] + if entity.get("type") == "Text" + ] + assert any("그림 없음" in text for text in texts), texts From abea34a446c0d16f2d88b571f0d2d1d3acaad2c1 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 9 Sep 2026 23:59:23 +0900 Subject: [PATCH 2/2] =?UTF-8?q?knowledge(verification):=20=EA=B3=84?= =?UTF-8?q?=ED=9A=8D=EC=84=9C=200=C2=B73=C2=B74=EC=9E=A5=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=EB=B6=84=20=EA=B7=BC=EA=B1=B0=20=EC=9D=B4=EA=B4=80=20?= =?UTF-8?q?(=EC=97=B4=20=EB=8D=A9=EC=9D=B4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2026-09-09 사용자 규칙 — 끝난 항목의 조사·검증 원문은 검증 폴더로 통째로 옮기고 계획서에는 결과 한두 줄만 남김. 링크는 남기지 않음(찾는 일은 위키·graphify 몫). 옮긴 것 — 재계산 취소 재판정 · 직행 files 가드 · purge 확인 · 업로드 잠금 표시 · 도수로 보류 확정 · 측구 토글 실측 · 표준도 두께 칸 · spoil_bank 예외 · 날개벽 원단위 · 표준도 「지금 무엇이 서 있나」 전수표. 계획서 1,547줄 · 80,056자 → 1,408줄 · 73,939자 (139줄 · 6,117자 줄었음). Co-Authored-By: Claude Opus 5 (1M context) --- ...026-09-09g_계획서_0_3_4장_완료근거_이관.md | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 docs/raw/verification/2026-09-09g_계획서_0_3_4장_완료근거_이관.md diff --git a/docs/raw/verification/2026-09-09g_계획서_0_3_4장_완료근거_이관.md b/docs/raw/verification/2026-09-09g_계획서_0_3_4장_완료근거_이관.md new file mode 100644 index 00000000..2c6a76e9 --- /dev/null +++ b/docs/raw/verification/2026-09-09g_계획서_0_3_4장_완료근거_이관.md @@ -0,0 +1,198 @@ +# 계획서 0·3·4장 완료분 근거 (이관 2026-09-09) + +> 계획서에는 결과 한두 줄만 남기고 **조사·검증 원문을 여기로 통째로** 옮긴 것 +> (2026-09-09 사용자 확정 규칙). 원문 그대로이며 손대지 않았음. + +- [x] **재계산 중간 취소 — 만들지 않기로 닫음** (2026-09-09 밤 재판정, 커밋 `5d4473b4`) + **잰 값** — 서버 계측 로그 네 번: **87.3 · 90.0 · 93.9 · 95.4초**(평균 91.7초). + 그중 **배수·관·측점 재생성이 80~86초(90 % 넘음)**, 노선 풀이는 3초대임. + 293초 → 90초로 이미 3분의 1이 됐음(0-11). + + **왜 안 만드나 — 취소가 「반쯤 부순 상태」를 만드는 단추가 되기 때문임.** + · 체인이 돌기 **전에** 관 지점을 이미 지움(`clear_pipe_points`) — 취소해도 안 돌아옴 + · 체인이 끊기면 **노선만 새것 · 종횡단은 옛것**으로 어긋남(2026-09-06 실측 사고: + 배수관 측점 9 → 0). 지금 코드도 그때는 계획노선을 되돌리라고 사유를 올려 보냄 + · 90초는 기다릴 수 있는 길이고, 급하면 **취소보다 그 80초 한 곳을 줄이는 것**이 이득임 + + **대신 한 것** — 대기 안내가 「몇 분 걸립니다」라는 **옛 문구**였음. **「1분 반쯤 걸립니다 + (N초 지남)」** 로 고쳐 **경과 시간이 1초마다 도는 것**을 보임(멈춘 것인지 도는 것인지 알 수 있게). + ⚠ **다시 열 조건** — 배수·관·측점 재생성이 더 커져 **3분을 넘거나**, 체인을 단계마다 + 되돌릴 수 있게 되면 그때 다시 볼 것. + +--- + +- [x] **직행 `/files` 가드 — 이미 붙어 있었음** (2026-09-09 확인) + `B03_FileInput_Router.py:212` 가 분석중 차단, `:249` 가 같은 이름 옛 행 내림(`supersede`)을 + 부름. 주석에 「이 갈래만 빠져 있어 분석 2개가 같은 산출물 경로에서 부딪혔다(2026-09-08)」로 + 까닭까지 적혀 있음. **세 갈래가 같아졌음.** + ~~[잔손질] 두 줄이면 세~~ + 갈래가 같아짐. **가장 싸고 효과 큼.** + +--- + +- [x] **purge 전 확인 단계 — 이미 있었음** (2026-09-09 확인) + `B03_FileInput_Router_Helpers.py:69` `_confirm_replace_response` 가 **409** 로 「지우고 + 진행할까?」를 묻고 **무엇이 지워지는지 함께 알림**. 사람이 고르면 `confirm_replace=True`. + ⇒ 0-8 은 셋 중 둘이 닫혔고 **남은 것은 업로드 잠금 표시 하나**임. + ~~[반나절] 「이 프로젝트에 설계 산출물이 있음. 지우고 새~~ + 자료로 갈까?」. 지금은 말없이 지움. + +--- + +- [x] **프로젝트 단위 업로드 잠금 표시** — **2026-09-09 밤 끝냄**(커밋 `018ebe7f`). + 막는 것은 이미 있었고(`is_analysis_running`) 없던 것은 **막힌 까닭을 사람에게 보이는 것**이었음. + - 분석을 띄우는 **경로 넷 모두** 시작한 사람을 `params.started_by` 에 담음 + - `analysis_lock_owner` 가 그 id 로 이름을 붙임 — 옛 자료는 **이름 없이 잠금만** 돎(막는 것이 먼저) + - `/upload-overview` 에 `analysis_lock` 을 실어 B03 화면이 띠로 보임 + + | 잰 것 | 결과 | + |---|---| + | API 칸이 서나 | ⭕ `analysis_lock` — 도는 분석이 없어 지금은 `null` (두 프로젝트 200 확인) | + | 화면 띠가 뜨나 | ⭕ 「엄상돈 님이 올린 자료를 분석하는 중입니다 — 끝난 뒤에 새 자료를 올릴 수 있습니다」 경고색 | + | 이름이 없으면 | ⭕ 「다른 자리에서 올린 자료를…」 로 뜸(잠금은 그대로) | + + ⚠ **실제 분석이 도는 화면은 아직 못 봄** — 지금 도는 분석이 없어 응답을 잠깐 가로채 확인하고 + 곧바로 되돌렸음. **다음 업로드 때 실물로 볼 것.** 시험 넷 추가, 전체 **1211 통과 · 실패 0**. + + +--- + +- [x] **[답 나옴 · 보류 확정 — 2026-09-09 밤 닫음]** **도수로·절토사면 배수로는 품셈에 그 공종이 없음** (2026-09-08). + 「수로」로 전수 검색해도 나오는 것이 위 셋뿐임. 지장목제거와 같은 모양 — + **수량은 서는데 단가 공종이 없는 것**. + ⇒ **2026-09-09 사용자 확정 5차 3번 — 「나중에 실무 문서 첨부. 임의로 만드는 건 지금은 + 무의미」로 보류.** B06 은 **배치만 받고** 단가·일위대가를 만들지 않음. 지금처럼 + 사유(`pending_user`)로 두는 것이 확정된 동작임 — **더 할 일 없음. 문서가 오면 그때 엶.** + ⇒ **미체크로 둘 까닭이 없어 닫음**(2026-09-09 밤 재훑기). 문서가 오면 4-12 쪽에서 새로 열 것. + + +--- + +- [x] **[잔손질]** 카드에서 측구를 **껐다 켰다** 해서 방금 가른 선택/결과가 화면에서 도는지. + **2026-09-09 밤 화면 실측으로 끝냄 — 안 돌던 까닭을 찾아 고쳤음.** + +**⚠ 왜 안 돌았나 — 「칸 하나」가 통로에서 빠져 있었음** + +측구 선택(`ditch_choice`)이 **캐시(`CrossDesignChoice`)에 없어** 눌린 값이 그 자리에서 +버려졌음. 브라우저 재계산은 저장분 결과만 읽었고, [저장] patch·서버 스키마에도 칸이 없었음. +⇒ 통로 넷 + 스키마를 이었음(`5907de85`). 화면 코드는 리로드, 스키마는 백엔드 재시작으로 반영. + +**잰 것 (측점 1+0.0 = 20.0m, 프로젝트 랩탑_보조 route 182)** + +| 잰 것 | 고치기 전 | 고친 뒤 | +|---|---|---| +| 캐시에 담기나 | ❌ `ditch_choice` 칸 자체가 없음 | ⭕ `"ditch_choice": false` 로 담김 | +| 끄면 사라지나 | ❌ `aria-pressed` 가 `true` 그대로 | ⭕ `false` · **절토 계 3.33 → 1.78㎡** | +| 다시 누르면 켜지나 | ❌ 안 돎 | ⭕ `true` · **절토 계 1.78 → 3.90㎡** | +| 「자동」으로 풀리나 | — | ⭕ **엔진이 자동값과 같은 선택은 `null` 로 되돌림**(시험 `test_자동과_같은_선택은_자동으로_푼다`) | +| 계획고를 바꾸면 자동이 다시 도나 | — | ⭕ **구조로 잠김** — 안 만진 측점은 캐시에 선택이 없어 자동 판정을 그대로 탐 | + +시험 `test_b06_ditch_choice.py` 에 통로 넷 + patch 병합 잠금 한 건 추가(전체 **1198 통과 · 실패 0**). +조작으로 바꾼 값은 **캐시를 걷고 리로드해 되돌렸음** — 절토 계 3.33㎡ 로 복귀 확인, [저장]·[확정] 안 눌렀음. + +**✅ 곁들여 본 것 — 카드를 만졌다 되돌리면 값이 저장분과 다름 (2026-09-09 밤 끝냄, 커밋 `2eb2c972`)** + +**거울 문제가 아니었음.** 같은 입력을 주면 파이썬·TS 가 같은 값을 냄 — +측점 20.0m 에서 **표준단면 측구 상폭 0.69 기준 절토 3.328 · 0.9 기준 3.899** 로 둘 다 같음. +갈린 것은 산식이 아니라 **입력(표준단면)** 이었음. + +⚠⚠ **더 큰 것이 나왔음 — 저장분 한 줄 안에 두 표준단면이 섞여 있었음.** + +| 잰 것 (측점 20.0m) | 값 | +|---|---| +| 저장분 절토 계 | 3.328㎡ — **옛 상폭 0.69 기준** | +| 저장분 측구 기하·면적 | 상폭 **0.9** · 0.18㎡ — **새 상폭 기준** | +| 저장분 옵션의 표준단면 | 상폭 **0.9** (사용자가 고쳐 저장한 값) | + +까닭 — 브라우저는 **만진 측점만** 다시 계산하는데 [저장]은 **전 측점 면적**을 실어 보냄. +그래서 표준단면을 고쳐도 안 만진 측점은 **옛 면적 그대로** 정본이 됨(수량이 갈리는 자리). + +고친 것 — ① [저장]·[확정]이 면적을 모으기 **전에** 전 측점을 다시 계산함 +② 표준단면 패널에 **「고친 값이 측점에 아직 안 실렸음」 알림**(언제 따라오는지 함께 적음). +화면 실측 — 패널 값을 저장분과 다르게 두면 알림이 뜨고, 같으면 사라짐(초안은 원래대로 되돌려 놓았음). +시험 둘 추가, 전체 **1205 통과 · 실패 0**. +⚠ **[저장] 실측은 아직 못 했음** — 사용자가 표토 축 확정 전까지 저장을 멈춰 둔 상태라 +저장 버튼을 안 눌렀음. 잠금이 풀리면 「표준단면 고치고 저장 → 전 측점 면적이 따라오나」를 볼 것. + + +--- + +- [x] **[잔손질]** 확정 ② 후반 「사용자가 값을 바꿀 수 있게」 — 표준도 입력 칸에 **상부·하부 두께**를 + 더할 것(비우면 식). ⑨ 야면석 계수 · ⑩ 채움 강도와 같은 자리. + **2026-09-09 밤 끝냄** — 등록부 돌쌓기 계열 다섯에 칸을 두고 표준도 제원 폼·저장·수량까지 이었음. + 큰돌쌓기는 두께 식이 달라 뺐음(그 장에는 칸이 안 뜸). + + | 잰 것 (랩탑_보조 · 표준도 3장 돌쌓기(찰) H=2.5) | 결과 | + |---|---| + | 칸이 뜨나 | ⭕ 「상부 두께(m)」·「하부 두께(m)」 — 자리표시 「비우면 식」 | + | 저장되나 | ⭕ 상부 0.60 저장 → 「2개소에 반영했습니다」 · 정본 `thickness_top_m: 0.6` | + | 수량에 닿나 | ⭕ **입적 24.375 → 20.625㎥** · 근거 「상부 0.60(사용자 입력) · 하부 1.05(상부 + 0.3×(H−1))」 | + | 비우면 식으로 돌아오나 | ⭕ 칸을 비워 저장 → 정본에서 칸이 지워지고 **입적 24.375㎥ 복귀** | + + ⚠ 빈 칸은 「안 정함」임 — 자동값을 저장에 박지 않음. 상부만 넣으면 하부는 그 위에서 식을 이음. + 시험 다섯 추가(`test_b08_coeff_and_strength.py`), 전체 **1203 통과 · 실패 0**. 커밋 `3a64bb5b`. + + +--- + +- [x] **끝남(2026-09-09, `0ff26d43`)** — `spoil_bank` 두 줄(`side`·`extra_distance_m`)을 뺐음. + **덮여 있었음** — 정본에 `default_basis` 로 까닭이 적혀 있어 목록 없이도 통과. + - 자체검증 — 두 줄을 지우고 `resources/tester/` 전체 실행: **1183 통과 / 22 건너뜀 / 실패 0**. + 그 자리에 「앞으로도 목록에 넣지 말 것」을 적어 둠. + - ⚠ **같은 상태가 13 개 더 있음** — `revetment`·`soil_guard`·C군 `side` 계열 + (`revetment.tiers`·`lift_m`·`shift_m`·`side` · `soil_guard.tiers`·`lift_m`·`shift_m`· + `stone_supply`·`side` · `masonry_wet/dry`·`boulder_masonry`·`retaining_wall`.`side`). + 빼도 통과하나 **주석 덩어리가 얽혀 있어** 이번 잔손질 범위를 넘음. 다음에 그 파일을 + 손댈 때 함께 걷을 자리. + - ⚠ 「혼자 지우지 말 것」 단서는 **풀렸음** — 시험이 `resources/tester`(git 안)로 옮겨져 + 창끼리 함께 건너감. + + +--- + +- [x] **날개벽·집수정 원단위** — **2026-09-09 밤 끝냄**(커밋 `0747979c`). + ⓘ **집수정 Ø800 은 이미 표에 있었음**(`pipe_inlet_basin` □형·콘크리트·800) — 다시 안 만듦. + 날개벽 다섯 형식을 원본 탭 소계 그대로 옮겼고, **고르는 칸**까지 이었음. + + | 형식 | 터파기 | 되메우기 | 잔토 | 기초잡석 | 콘크리트(저판+벽체) | 거푸집(저판+벽체) | + |---|---|---|---|---|---|---| + | Ø800 A-TYPE | 13.104 | 6.504 | 6.61 | 1.152 | 2.0595 | 유로폼 12.5376 | + | Ø800 C-TYPE | 7.7269 | 4.7148 | 3.0122 | 0.6821 | 1.109 | 유로폼 6.2309 | + | Ø1000 A-TYPE | 24.39 | 10.478 | 13.912 | 1.6 | 2.891 | 합판(4회) 18.255 | + | Ø1000 C-TYPE | 13.7179 | 7.9134 | 5.8046 | 1.0271 | 1.9109 | 합판(4회) 10.7220 | + | Ø800 A+집수정 | 16.256 | 8.44 | 7.816 | 1.152 | 2.6395 | 합판(4회) 18.3376 | + + - **거푸집 종류를 뭉개지 않았음** — 원본 탭에서 직접 읽어 Ø800 A·C 는 유로폼, + Ø1000 과 A+집수정은 합판(4회). 단가가 갈리는 자리라 이름을 그대로 둠. + - ⚠ **철근·면목은 원본 날개벽 탭에 없음** — 지어내지 않았고 시험이 그것을 지킴. + - ⚠ **겹쳐 세는 자리** — 「A-TYPE+집수정」은 집수정을 품은 형식이라 집수정 형식도 차 있으면 + **사유로 알림**(값은 안 고침). 원단위표 `double_count_rules` 에도 규칙으로 적었음. + - 등록부·배치 폼에 「관보호공 날개벽」 칸(A-TYPE·C-TYPE·A-TYPE+집수정, 비우면 안 셈). + + 검증 — 화면에서 배수관 폼에 칸이 뜨고 선택지 셋 확인. 엔진을 돌려 **날개벽 줄이 따로 서고** + 터파기 13.104 · 콘크리트 2.0595 · 유로폼 12.5376 이 나오는 것까지 봄. 시험 일곱 추가, + 전체 **1218 통과 · 실패 0**. ⚠ 정본 저장까지는 [저장]을 눌러야 하는데 지금 멈춰 둔 상태라 + **화면에서 고른 값이 수량에 실리는 것은 잠금 풀린 뒤에 볼 것.** + +--- + +**⭐ 2026-09-09 밤 — 지금 무엇이 서 있나 (도면 알맹이로 전수 확인, 랩탑 메인)** + +| 장 | 표(하단표) | 그림(상단) | 까닭 | +|---|---|---|---| +| 1장 큰돌쌓기 H=2.5 | ⭕ | ❌ | **규격 축이 직경**(`stone_cm` 60~80)인데 그림이 **뒷길이**(`back_len_cm`)를 찾음 | +| 2장 돌쌓기(메) H=2 뒷길이 35㎝ | ⭕ | ⭕ **섬** — 폴리선 2 · 선 1 · 글자 11 | 뒷길이가 정해져 있음 | +| 3장 돌쌓기(찰) H=2.5 | ⭕ | ❌ | **뒷길이를 아직 안 골랐음**(제원이 `기초유`·H 뿐) | +| 4장 옹벽 H=2 | ⭕ | ❌ | 그림 대상 아님 — `FIGURE_TYPE_IDS` 는 돌쌓기 계열 셋뿐 | + +⇒ **1단계(하단표)는 네 장 다 섬** — V-9 를 ⭕ 로 닫았음(7장 참조). +⇒ **2단계(그림)는 이미 절반 돌고 있음** — 코드(`..._Cad_StandardFigure.build_figure`)가 있고 +**뒷길이가 정해진 장에서는 실제로 그려짐.** 안 그려지는 것은 코드가 없어서가 아니라 +**제원이 없어서**임(뒷길이를 고르면 그 장도 바로 섬). + +**⚠ 하나는 코드 자리가 어긋나 있음 — 큰돌쌓기.** `FIGURE_TYPE_IDS` 에는 들어 있는데 +그림이 뒷길이를 찾으므로 **직경만 있는 큰돌쌓기는 영영 못 그림**(품셈 13-6 은 직경 축, +13-4 는 뒷길이 축 — 수량 쪽에서 이미 갈라 둔 그 자리와 같음). ⇒ **큰돌쌓기 그림은 +직경에서 두께를 내는 다른 식이 필요**하거나, 아니면 목록에서 빼고 사유를 적어야 함. +**만들지 않고 드러내만 둠**(2단계에서 다룰 자리). +