From c7770b75296003422027366858740f44db9b2b17 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 7 Sep 2026 16:59:48 +0900 Subject: [PATCH] =?UTF-8?q?fix(=ED=9A=A1=EB=8B=A8):=20=EC=98=B9=EB=B2=BD?= =?UTF-8?q?=20=EC=9D=98=EB=AC=B4=20=ED=8C=90=EC=A0=95=EC=9D=B4=20=EC=86=8C?= =?UTF-8?q?=EB=8B=A8=EC=9D=84=20=EC=8B=A4=EC=A0=9C=EB=A1=9C=20=EB=B3=B4?= =?UTF-8?q?=EA=B2=8C=20=ED=95=A8=20=E2=80=94=20=EB=B6=80=ED=98=B8=C2=B7?= =?UTF-8?q?=EC=AA=BC=EA=B0=9C=EC=A7=90=20=EB=91=90=20=EA=B3=B3=20(?= =?UTF-8?q?=EA=B3=84=ED=9A=8D=EC=84=9C=203-9=20=E2=91=A5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 앞 커밋으로는 화면 값이 안 바뀌어 실화면에서 두 가지를 더 찾았음. ① **TS 결과에 소단을 안 실었음** — 파이썬만 되싣고 브라우저 판을 빠뜨렸음. 판정(`fillSlopeLengths`)은 브라우저에서 도는데 `design.berm` 이 없어 종전 식으로 갔음. ② **소단을 폭으로 찾으면 못 찾음** — 설계선 꼭짓점에는 지반 샘플(0.5m 격자)이 섞여 있어 폭 0.5m 짜리 소단이 **두 도막으로 쪼개짐**(실측: 평탄부 0개). 게다가 설계선은 오프셋 오름차순이라 **우측(음수) 성토면은 같은 소단이 `-기울기`로 나와** 부호를 그대로 대면 역시 못 찾음. ⇒ **기울기로 가름.** 소단은 2°(0.035), 성토는 1:1.2~2.0(0.5~0.83)이라 성토 기울기의 절반만 잡아도 확실히 갈림. 쪼개져도 부호가 뒤집혀도 걸림. 실화면 확인(8001·5174, `stale:false`) — 측점 2820.0m, 성토사면이 긴 자리: · 소단 없음 → **≥32.54m** (옹벽·석축 의무 대상) · 폭 0.5m·3m마다 놓음 → **≥3.00m** (5m 이내 — 의무 해소). 3.00m 은 소단 간격 그대로임 · 옆 측점(2840.0m, 안 놓음) → **≥32.54m 그대로** **소단 없는 측점은 값이 안 바뀜 — 272측점 전수 대조, 달라진 곳 0.** 구조로도 보장됨(`design.berm` 이 없으면 종전 식을 그대로 탐). 전체 542 passed · 18 skipped. TS 타입 검사 통과. Co-Authored-By: Claude Opus 5 (1M context) --- B06_Section/B06_Section_UI_Cross_Fit.ts | 15 ++++++++++----- common_util/common_util_cross_design.ts | 11 +++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/B06_Section/B06_Section_UI_Cross_Fit.ts b/B06_Section/B06_Section_UI_Cross_Fit.ts index af03913e..b5d09f6c 100644 --- a/B06_Section/B06_Section_UI_Cross_Fit.ts +++ b/B06_Section/B06_Section_UI_Cross_Fit.ts @@ -134,7 +134,12 @@ function longestFillRun( if (!berm) return Math.abs(endOffset - startOffset) * slant; const low = Math.min(startOffset, endOffset); const high = Math.max(startOffset, endOffset); - const bermRise = Math.tan((berm.slope_deg * Math.PI) / 180) * berm.width_m; + // 소단인지는 **기울기**로 가른다 — 폭으로 찾으면 못 찾는다. 설계선 꼭짓점에는 지반 + // 샘플(0.5m 격자)이 섞여 있어 폭 0.5m 짜리 소단이 두 도막으로 **쪼개지기** 때문이다 + // (2026-09-07 실측: 폭으로 찾으니 평탄부 0개). 소단 기울기는 2°(0.035)이고 성토 기울기는 + // 1:1.2~2.0(0.5~0.83)이라 절반만 잡아도 둘이 확실히 갈린다. + const fillGradient = 1 / Math.max(design.fill_slope_ratio, 1e-6); + const bermMaxGradient = fillGradient * 0.5; let longest = 0; let current = 0; const line = design.design_line; @@ -145,10 +150,10 @@ function longestFillRun( const to = Math.min(Math.max(a.offset_m, b.offset_m), high); if (to - from <= 1e-9) continue; // 이 도막은 사면 밖이다 const run = Math.abs(b.offset_m - a.offset_m); - const rise = b.elevation_m - a.elevation_m; - const isBerm = Math.abs(run - berm.width_m) < 1e-6 && Math.abs(rise - bermRise) < 1e-6; - if (isBerm) { - longest = Math.max(longest, current); + if (run <= 1e-9) continue; + const gradient = Math.abs(b.elevation_m - a.elevation_m) / run; + if (gradient < bermMaxGradient) { + longest = Math.max(longest, current); // 소단에서 도막이 끊긴다 current = 0; continue; } diff --git a/common_util/common_util_cross_design.ts b/common_util/common_util_cross_design.ts index 83f060b0..a3c5c58a 100644 --- a/common_util/common_util_cross_design.ts +++ b/common_util/common_util_cross_design.ts @@ -134,6 +134,8 @@ export interface CrossDesignResult { design_line: CrossDesignEdge[]; /** 절토 사면 경사 구간(소단 제외) — 법정 경사 검사가 읽는다. 짝: `cut_slope_segments`. */ cut_slope_segments: CutSlopeSegment[]; + /** 이 측점에 놓인 소단 제원 — 옹벽 의무 판정이 사면을 도막으로 끊는 데 쓴다. */ + berm?: { width_m: number; interval_m: number; slope_deg: number }; surface_drop_m?: number; pavement_thickness_m?: number; rock_boundary_offset_m?: number; @@ -440,6 +442,15 @@ export function computeCrossDesign( cut_slope_segments: geometry.cutSlopeSegments(), }; if (drop > 0) result.surface_drop_m = round4(drop); + if (options.berm) { + // 소단 제원을 설계에 되싣는다 — 짝 파이썬과 같은 까닭이고, **옹벽 의무 판정 + // (`fillSlopeLengths`)이 이 값을 보고** 사면을 도막으로 끊어 잰다(계획서 3-9). + result.berm = { + width_m: round4(options.berm.widthM), + interval_m: round4(options.berm.intervalM), + slope_deg: round4(options.berm.slopeDeg), + }; + } if (paved) result.pavement_thickness_m = round4(pavedGroup.pavement_thickness_m); if (presetKey === "rock" && rockBoundaryOffsetM !== null) { result.rock_boundary_offset_m = round4(rockBoundaryOffsetM);