From 934419bbaa4fe547378b6603d17f9cac44bda5fd Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sat, 22 Aug 2026 10:46:58 +0900 Subject: [PATCH] =?UTF-8?q?feat(B06):=20=EC=A7=91=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20=EC=9E=AC=EC=A0=95=EC=9D=98=20=E2=80=94=20?= =?UTF-8?q?=EC=84=B1=ED=86=A0=EC=82=AC=EB=A9=B4=203m=20=EA=B8=B0=EC=A4=80?= =?UTF-8?q?=C2=B7=ED=86=B5=EC=A7=B8=20=EB=85=B8=EA=B2=AC=20=EC=9D=BC?= =?UTF-8?q?=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2026-08-22 사용자 재정의 5개 조건 반영: - 판정: 유입측 성토사면 길이 ≤ 3m(BASIN_MAX_FILL_SLOPE_M)면 집수정 기본(절토측은 사면 없음으로 자동 포함). 종전 절토/30% 막힘 판정 대체. 기슭막이 유지 선택은 향후 추가(reason: cut | short). - ㄴ(L)형: 내부 높이 1.2 등 사이즈 유지한 채 **통째로 노견 끝점에 일치** — 바닥(=관 유입 invert)이 따라 올라 관 물매 재계산. 유출단은 원지반 위 원칙 유지. ㄷ형 동일(L+측벽), 기본값 L형. - I형: 원지반 배관 유지, 벽 상단만 노견 연결(기존 분기 유지). - 검증: 13+15.7 reason=short, 벽상단=노견 (dx=0, dz=0), 내부높이 1.200, 관 유입 (-3.000, 539.241=노견-1.2)·유출 원지반, 물매 재계산. 4+4.3 유입 사면>3m — 집수정 없음·벽 2개 유지. Co-Authored-By: Claude Opus 5 (1M context) --- B06_Section/B06_Section_UI_Cross_Culvert.ts | 4 +- .../B06_Section_UI_Cross_Culvert_Const.ts | 7 ++++ .../B06_Section_UI_Cross_Culvert_Geom.ts | 41 ++++++++++++++----- .../B06_Section_UI_Cross_Culvert_Types.ts | 5 ++- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/B06_Section/B06_Section_UI_Cross_Culvert.ts b/B06_Section/B06_Section_UI_Cross_Culvert.ts index 90049599..975d8087 100644 --- a/B06_Section/B06_Section_UI_Cross_Culvert.ts +++ b/B06_Section/B06_Section_UI_Cross_Culvert.ts @@ -199,8 +199,8 @@ export function appendCulvertOverlay( shape === "L" ? "ㄴ형" : shape === "U" ? "ㄷ형" : "I형")(basin.shape); const reasonText = basin.reason === "cut" - ? "절토측 유입 — 자동 집수정" - : "성토측 유입이 원지반에 30% 이상 막혀 집수정으로 전환"; + ? "절토측 유입 — 집수정 기본(성토사면 없음)" + : "유입측 성토사면 3m 이하 — 기슭막이 대신 집수정 기본(선택 기능 추가 예정)"; for (const part of basin.parts) { layer.append( polygon( diff --git a/B06_Section/B06_Section_UI_Cross_Culvert_Const.ts b/B06_Section/B06_Section_UI_Cross_Culvert_Const.ts index 45c33ffd..cad9aa4b 100644 --- a/B06_Section/B06_Section_UI_Cross_Culvert_Const.ts +++ b/B06_Section/B06_Section_UI_Cross_Culvert_Const.ts @@ -128,3 +128,10 @@ export const BASIN_BOTTOM_CLEARANCE_M = 0.5; * I형(공유벽뿐)은 이 값을 쓰지 않고 노견 끝 접점까지 벽을 키운다. */ export const BASIN_INNER_HEIGHT_M = 1.2; + +/** + * 집수정 전환 기준 성토사면 길이(m) — 유입측 성토사면이 이 길이 **이하**면 기슭막이 + * +배관을 두기 어려워 집수정이 기본이 된다(2026-08-22 사용자 확정). 절토측 유입은 + * 성토사면이 없으므로 자동 포함. 사용자 선택(기슭막이 유지)은 향후 추가 예정. + */ +export const BASIN_MAX_FILL_SLOPE_M = 3.0; diff --git a/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts b/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts index 2a482fbc..03160ac8 100644 --- a/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts +++ b/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts @@ -19,6 +19,7 @@ import type { CrossSection, CulvertSideSpec, SectionSample } from "./B06_Section import { BASIN_BOTTOM_CLEARANCE_M, BASIN_INNER_HEIGHT_M, + BASIN_MAX_FILL_SLOPE_M, FILL_MIN_RISE_M, FILL_SLOPE_MAX_LENGTH_M, FILL_SLOPE_RATIO_MAX, @@ -98,24 +99,42 @@ export function computeCulvertLayout( const inletInvert = Math.min(groundAt(inletInfo.edge.offset_m), invertCap(inletInfo.edge)); const inlet: OffsetPoint = { offset: inletInfo.edge.offset_m, elevation: inletInvert }; - // 유입측 절토 판정: 절토측 유입은 자연스럽게 집수정(ㄴ형 기본)이 된다. + // 유입측 집수정 판정(2026-08-22 사용자 재정의): **성토사면 길이 ≤ 3m**면 기슭막이 + // +배관을 두기 어려워 집수정이 기본이 된다(종전 절토/30% 막힘 판정 대체 — 절토측 + // 유입은 성토사면이 없으므로 자동 포함). 사용자 선택(기슭막이 유지)은 향후 추가. const mode = section.design?.section_mode; const inletSideName: "left" | "right" = uphill === "left" ? "left" : "right"; const inletIsCut = mode === "both_cut" || (mode === "left_cut" && inletSideName === "left") || (mode === "right_cut" && inletSideName === "right"); - // 성토측 유입이라도 관 앞 원지반이 관 단면의 30% 이상을 막으면 집수정으로 바꾼다 - // (2026-08-20 사용자 확정 — 자연 유입이 불가해 옆도랑 물을 받아야 하는 형상). - const inletFrontOffset = - inlet.offset + inletInfo.outward * (REVET_THICKNESS_M * 2 + REVET_LEAN_RATIO * diameter); - const blockedRatio = (groundAt(inletFrontOffset) - inletInvert) / Math.max(diameter, 1e-6); - const inletBlocked = !inletIsCut && blockedRatio >= 0.3; + let inletFillSlopeLen = 0; + if (!inletIsCut && designAt) { + const toe = slopeToeOffset(designAt, groundAt, inletInfo.edge.offset_m, inletInfo.limit); + const from = inletInfo.edge.offset_m; + const steps = 60; + let prevElev = designAt(from); + for (let i = 1; i <= steps; i += 1) { + const o = from + ((toe - from) * i) / steps; + const e = designAt(o); + inletFillSlopeLen += Math.hypot((toe - from) / steps, e - prevElev); + prevElev = e; + } + } const basinReason: BasinLayout["reason"] | null = inletIsCut ? "cut" - : inletBlocked - ? "blocked" + : inletFillSlopeLen <= BASIN_MAX_FILL_SLOPE_M + 1e-6 + ? "short" : null; + // 집수정 형식 기본값 = ㄴ(L)형(2026-08-20 확정). 사용자 선택은 향후 추가 예정. + const basinShape: BasinShape = "L"; + // ㄴ·ㄷ형은 **측면·바닥 사이즈를 유지한 채 통째로 노견 끝점에 일치**시킨다(2026-08-22 + // 사용자 확정 — 노견과 빈 공간을 채운다). 바닥(=관 유입 invert)이 따라 올라가므로 + // 관 물매도 바뀐다. 유출단은 원지반 위 원칙 그대로. I형은 원지반 배관을 유지하고 + // 벽 높이로만 노견에 연결한다(buildWall). + if (basinReason && basinShape !== "I") { + inlet.elevation = inletInfo.edge.elevation_m - BASIN_INNER_HEIGHT_M; + } // 성토측 유입 기슭막이는 노견에 붙여 두지 않는다 — 사면선이 벽 이음선 상단점을 지나야 // 하므로 자리를 풀어서 정한다(집수정은 측구부 자리 그대로). @@ -241,8 +260,8 @@ export function computeCulvertLayout( forceBasinReason ?? (spec.structure === "집수정" ? "cut" : null); if (reason) { // 집수정 = 기슭막이와 같은 **평행사변형 벽**(+ 형식에 따라 바닥·반대측 막음). - // 형식 기본값 = ㄴ(L)형(2026-08-20 사용자 확정). 실무 내공 1.0m(울진 돌집수정). - const shape: BasinShape = "L"; + // 형식 기본값 = ㄴ(L)형(함수 상단 basinShape). 실무 내공 1.0m(울진 돌집수정). + const shape: BasinShape = basinShape; const innerWidth = 1.0; // 집수정은 기슭막이와 달리 **물을 모으려고 만든 구조물**이다(2026-08-22 사용자 // 확정) — 형태별로 별도 구성하고, 배관(invert)은 원지반 자리를 그대로 지킨다. diff --git a/B06_Section/B06_Section_UI_Cross_Culvert_Types.ts b/B06_Section/B06_Section_UI_Cross_Culvert_Types.ts index 9aa8602f..b8704528 100644 --- a/B06_Section/B06_Section_UI_Cross_Culvert_Types.ts +++ b/B06_Section/B06_Section_UI_Cross_Culvert_Types.ts @@ -71,8 +71,9 @@ export interface BasinLayout { /** 부재별 폴리곤(벽·바닥·막음벽). 각각 분리 도형으로 그린다. */ parts: Array<{ kind: "wall" | "floor"; points: OffsetPoint[] }>; label: OffsetPoint; - /** 자동 전환 사유 — 툴팁·라벨에 쓴다. */ - reason: "cut" | "blocked"; + /** 자동 전환 사유 — 툴팁·라벨에 쓴다(2026-08-22 재정의: 성토사면 ≤3m 기준). + * "cut" = 절토측 유입(성토사면 없음), "short" = 성토사면 3m 이하. */ + reason: "cut" | "short"; /** 집수정이 원지반 안으로 들어갈 때의 절토선(구조물 바깥 끝 → 지반 교차점). */ cutLine: { from: OffsetPoint; to: OffsetPoint } | null; }