diff --git a/B06_Section/B06_Section_UI_Cross_Body_Guard.ts b/B06_Section/B06_Section_UI_Cross_Body_Guard.ts index 640af0277..b3fdefb2b 100644 --- a/B06_Section/B06_Section_UI_Cross_Body_Guard.ts +++ b/B06_Section/B06_Section_UI_Cross_Body_Guard.ts @@ -271,3 +271,20 @@ export function withoutGuardedFill( label: { ...ford.label, text: `${ford.label.text} L${Math.ceil(lengthM - 1e-6)}m` }, } as T; } + +/** 세월교 · 박스 유입 흙막이 수평공간 시작 = 본체 바닥(세월교 바닥판 · 박스 저판 상면) 높이의 + * 흙막이 전면 자리(89-3). 본체가 이 측점에 없으면 null(배관 규칙). */ +export function guardFloorStart( + section: CrossSection, + base: BodyGuardBase | null, + wall: WallLayout, +): OffsetPoint | null { + const end = base?.ends.inlet; + if (!end) return null; + const slab = section.culvert?.body === "box" ? (section.box?.slab_thickness_m ?? 0) : 0; + const floor = end.invert + slab; + return { + offset: wall.points[2].offset + wall.outward * wall.lean * (wall.topJoint.elevation - floor), + elevation: floor, + }; +} diff --git a/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts b/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts index 7c3b5bbd9..14c2cecde 100644 --- a/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts +++ b/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts @@ -25,6 +25,7 @@ import { buildRevetWallGeometry } from "./B06_Section_UI_Cross_Wall"; import { bodyGuardAxes, bodyGuardBase, + guardFloorStart, bodyGuardHeight, fordPipeFit, mergeGuardTrim, @@ -596,7 +597,7 @@ export function computeCulvertLayout( outletBottom: outletWall?.bottomBack.elevation ?? 0, basinStart: culvert.hidden_pipe ? inletWall - ? pipeCorners.inlet.bottom + ? (guardFloorStart(section, body, inletWall) ?? pipeCorners.inlet.bottom) : null : basinFillStart, basinBottom: culvert.hidden_pipe ? (inletWall?.bottomBack.elevation ?? 0) : basinFillBottom, diff --git a/resources/tester/helper_79_guard_shape.cjs b/resources/tester/helper_79_guard_shape.cjs index 69a30ad20..aacab3ac3 100644 --- a/resources/tester/helper_79_guard_shape.cjs +++ b/resources/tester/helper_79_guard_shape.cjs @@ -154,6 +154,19 @@ out.cutBerm2 = cutTails( sections.map((s) => (s === at(352.14) ? berm2 : s)), ); +// ⑤b 유입 수평공간 시작 높이 = 본체 바닥(세월교 바닥판 상면 · 박스 저판 상면 · 89-3) +out.inletFloor = [352.14, 173.087].map((chainage) => { + const section = at(chainage); + const layouts = computeStoredLayouts(section, sections); + const start = layouts.culvert.basinFill.segments[0]?.points[0]; + const inletLeft = (section.uphill_side ?? "left") === "left"; + const floor = layouts.ford + ? layouts.ford.sides.find((s) => s.role === "inlet").slabTopElevation + : layouts.box.sides.find((s) => (s.role === "left") === inletLeft).bottomElevation + + layouts.box.box.slab_thickness_m; + return { start: start ? r3(start.elevation) : null, floor: r3(floor) }; +}); + // ⑥ 관 보호공 기본 자리 = 제일 위(성토선 0 지점 · d 0) · 관 길이 m 단위 const pipe = clone(at(180)); pipe.culvert = { diff --git a/resources/tester/test_79_guard_shape.py b/resources/tester/test_79_guard_shape.py index f33cbcafe..f12a573dc 100644 --- a/resources/tester/test_79_guard_shape.py +++ b/resources/tester/test_79_guard_shape.py @@ -87,6 +87,12 @@ def test_흙막이_끝_절토는_수평공간_뒤(shape: dict) -> None: assert tail["berm"] == pytest.approx(2.0, abs=1e-3) +def test_유입_수평공간_시작은_본체_바닥(shape: dict) -> None: + """89-3 — 박스 저판 상면 · 세월교 바닥판 상면 높이에서 수평 → 절토.""" + for case in shape["inletFloor"]: + assert case["start"] == pytest.approx(case["floor"], abs=1e-3) + + def test_관_보호공_기본_자리는_제일_위(shape: dict) -> None: pipe = shape["pipe"] assert pipe is not None