diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py index 214aedfe..0f656b00 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py @@ -58,7 +58,14 @@ DEFAULT_BACK_LENGTH_CM = 45 # 돌쌓기 전개식의 상수 — 실무 수식에 박혀 있던 값을 뺀 것. STONE_MASONRY = { - "face_to_slope_factor": 1.04, # 돌쌓기 면적 = 정면적 × 1.04 (비탈 기울기 몫) + # ⚠ **곱하는 값이 아니라 검산 참고값이다** (2026-09-08 ㉘ 에서 고침). + # 실무 시트의 「돌쌓기 = 정면적 × 1.04」에서 그 1.04 가 **곧 기울기 몫**이다 + # (1:0.3 → √(1+0.3²) = 1.0440 ≈ 1.04). 시트가 반올림해 적은 것을 우리가 + # **별도 계수로 오해해 `hypot` 위에 또 곱하고 있었다** — 면적이 4 % 부풀었고 + # 그 면적이 고임돌·야면석·채움콘크리트·모르터·물구멍 **전부의 밑수**였다. + # ⚠ 하드코딩하면 안 되는 값이다 — 큰돌쌓기는 「1:0.3 **이상**」이라 기울기가 + # 바뀔 수 있고, 그때 1.04 는 틀린 값이 되지만 `hypot` 은 따라간다. + "sheet_check_factor_at_0_3": 1.04, # 1:0.3 에서 시트값과 맞는지 대조하는 자리 "thickness_base_m": 0.45, # 평균두께 식의 밑돌 두께 "thickness_top_coeff": 0.10, # 상부 두께 계수 (0.45 + 0.10·H) "thickness_bottom_coeff": 0.40, # 하부 두께 계수 (0.45 + 0.40·H) @@ -178,7 +185,9 @@ BOULDER_DIAMETERS = ("40~60", "60~80", "80~100") #: 큰돌쌓기 전개 상수. **재료 원단위는 품셈에 없다** — 13-6 [주]⑦ 「재료량은 설계수량을 적용한다」. #: 그래서 여기서 내는 것은 **면적과 터파기 계열까지**이고 큰돌 자체는 미확보로 둔다. BOULDER_MASONRY = { - "face_to_slope_factor": 1.04, # 돌쌓기 면적 = 정면적 × 1.04 (13-4 와 같은 기울기 몫) + # ⚠ 검산 참고값 — 곱하지 않는다. 까닭은 `STONE_MASONRY` 의 같은 칸 주석을 볼 것. + # 큰돌쌓기는 전면 기울기가 「1:0.3 **이상**」이라 특히 하드코딩하면 안 된다. + "sheet_check_factor_at_0_3": 1.04, "excavation_extra_m": 0.2, # 터파기 폭 여유 "backfill_thickness_m": 0.2, # 되메우기 두께 } @@ -221,8 +230,8 @@ def boulder_masonry( slope_ratio = _num(options.get("face_slope_ratio"), 0.3) # 레지스트리에 칸 없음 — 기본 0.3 constants = BOULDER_MASONRY face_area = height_m * length_m - slope_area = face_area * math.hypot(1.0, slope_ratio) - masonry_area = slope_area * constants["face_to_slope_factor"] + # 기울기 몫은 **한 번만** — 13-4 와 같은 자리다(㉘). + masonry_area = face_area * math.hypot(1.0, slope_ratio) components = [ Component( @@ -230,7 +239,7 @@ def boulder_masonry( "㎡", masonry_area, DESTINATION["돌쌓기"], - f"비탈면적 × 1.04 · 직경 {diameter}㎝ (품셈 13-6)", + f"정면적 × √(1+{slope_ratio}²) · 직경 {diameter}㎝ (품셈 13-6)", ) ] @@ -300,9 +309,9 @@ def stone_masonry( constants = STONE_MASONRY face_area = height_m * length_m # 정면적 - # 비탈면적 = 정면적 × √(1+n²) — 기울어진 만큼 길어진다. - slope_area = face_area * math.hypot(1.0, slope_ratio) - masonry_area = slope_area * constants["face_to_slope_factor"] + # 돌쌓기 면적 = 정면적 × √(1+n²) — 기울어진 만큼 길어진다. **기울기 몫은 한 번만.** + # 실무 시트의 「정면적 × 1.04」가 바로 이 값이다(1:0.3 에서 1.0440 ≈ 1.04). + masonry_area = face_area * math.hypot(1.0, slope_ratio) thickness = ( (constants["thickness_base_m"] + constants["thickness_top_coeff"] * height_m) + (constants["thickness_base_m"] + constants["thickness_bottom_coeff"] * height_m) @@ -310,7 +319,13 @@ def stone_masonry( volume = face_area * thickness # 입적 components = [ - Component("돌쌓기", "㎡", masonry_area, DESTINATION["돌쌓기"], "비탈면적 × 1.04"), + Component( + "돌쌓기", + "㎡", + masonry_area, + DESTINATION["돌쌓기"], + f"정면적 × √(1+{slope_ratio}²) — 비탈면적", + ), Component( "고임돌", "㎥", diff --git a/B08_Quantity/B08_Quantity_UI_Page.ts b/B08_Quantity/B08_Quantity_UI_Page.ts index 2b3099d9..2ede41d7 100644 --- a/B08_Quantity/B08_Quantity_UI_Page.ts +++ b/B08_Quantity/B08_Quantity_UI_Page.ts @@ -352,6 +352,13 @@ function buildQuantitySidePanel( placing.is_default ? `${label} (${L("B08_Quantity_Placing_Default_Tag")})` : label, ), ); + // ⚠ 고른 값이 **아직 금액에 안 닿는다**는 사실을 숨기지 않는다(2026-09-08 ㉘). + // 인계가 타설 공종 줄을 아직 안 세운다 — B09 일위대가가 타설 품을 이미 갖고 있는지 + // 확인되기 전까지는 세우면 같은 콘크리트를 두 번 센다(이중계상 ㉢ 과 같은 자리). + const pending = document.createElement("p"); + pending.className = "b08-quantity__note"; + pending.textContent = L("B08_Quantity_Placing_NotApplied"); + panel.append(pending); } // ⚠ 「정하면 얼마나 달라지는지」까지 보여야 사용자가 판단한다. 이 값은 **참고 표시 전용**이고 // B08 의 어떤 계산에도 안 들어간다(금액은 B09 몫). diff --git a/ui_template/ui_template_locale_b2.ts b/ui_template/ui_template_locale_b2.ts index 35671a42..b25bbbb4 100644 --- a/ui_template/ui_template_locale_b2.ts +++ b/ui_template/ui_template_locale_b2.ts @@ -628,6 +628,10 @@ export const ui_locales_b2 = { B08_Quantity_Method_Unset: ["안 정함", "Not set"], B08_Quantity_Method_Ripping: ["긁어내기(암절취)", "Ripping"], B08_Quantity_Method_Blasting: ["터뜨리기(발파암)", "Blasting"], + B08_Quantity_Placing_NotApplied: [ + "⚠ 이 선택은 아직 금액에 반영되지 않습니다 — 견적의 일위대가가 타설 품을 이미 갖고 있는지 확인 중입니다.", + "Not yet applied to cost — checking whether the unit-price already covers placing labour.", + ], B08_Quantity_Side_Topsoil: ["표토제거", "Topsoil Removal"], B08_Quantity_Side_Topsoil_Label: ["표토 두께(m)", "Topsoil thickness (m)"], B08_Quantity_Unset_Placeholder: ["안 정함", "Not set"],