diff --git a/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts b/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts index 7bd6f75a..393d988a 100644 --- a/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts +++ b/B06_Section/B06_Section_UI_Cross_Culvert_Geom.ts @@ -63,6 +63,7 @@ import { designInterpolator, intersect, slopeToeOffset, + placeInletWall, placePipeWall, slopeLengthAlong, STRAY_LIMIT_M, @@ -205,7 +206,8 @@ export function computeCulvertLayout( inletAutoOffset = inletOptions.revetAutoOffset; // 4축 배치는 공용 풀이(placePipeWall — Solve, 700줄 제한): 좌우 = 노견 연장 // 평행이동, 상하 = 성토선 대각, 위 한계 = 최소 성토고·토피 상한. - const placed = placePipeWall({ + // 유입을 내리면 유출도 따라 내려간다 — 유출이 못 받으면 유입도 못 내려간다(가드). + const placed = placeInletWall({ autoOffset: inletAutoOffset, outward: inletInfo.outward, height: inletWallSpec.height, @@ -215,7 +217,13 @@ export function computeCulvertLayout( adjust: adjInlet, groundAt, limitOffset: inletInfo.limit, - requireCrossing: false, + outletGuard: { + edge: outletInfo.edge, + outward: outletInfo.outward, + height: outletWallSpec.height, + toeOffset: slopeToeOffset(designAt, groundAt, outletInfo.edge.offset_m, outletInfo.limit), + limitOffset: outletInfo.limit, + }, }); appliedAdjust.inlet.x = placed.x; appliedAdjust.inlet.d = placed.d; diff --git a/B06_Section/B06_Section_UI_Cross_Culvert_Solve.ts b/B06_Section/B06_Section_UI_Cross_Culvert_Solve.ts index 1a6e9480..9d12f548 100644 --- a/B06_Section/B06_Section_UI_Cross_Culvert_Solve.ts +++ b/B06_Section/B06_Section_UI_Cross_Culvert_Solve.ts @@ -413,3 +413,82 @@ export function placePipeWall(input: { invert: topElevation - input.height, }; } + +/** + * 유입 invert를 그 높이로 내렸을 때 **유출 벽이 성립하는가**(2026-08-22 사용자 확정). + * 유입이 유출보다 높아야 하므로 유입을 내리면 유출도 따라 내려간다 — 그런데 유출이 + * 원지반에 묻히고 3도 절토선이 지반과 다시 만나지 못하면 그 자리는 둘 수 없다. + * 이 경우 **유입도 더 내려가면 안 된다**(종전에는 유입만 내려가 유출이 깨졌다). + */ +export function outletReceivable(input: { + inletElevation: number; + edge: { offset_m: number; elevation_m: number }; + outward: number; + height: number; + toeOffset: number; + limitOffset: number; + groundAt: (offset: number) => number; +}): boolean { + const invertAt = (offset: number): number => + Math.min(input.groundAt(offset), input.inletElevation); + const auto = + minShoulderWallOffset( + input.edge, + input.outward, + input.height, + invertAt, + input.toeOffset, + input.limitOffset, + ) ?? input.toeOffset; + const invert = invertAt(auto); + if (invert >= input.groundAt(auto) - 0.01) return true; + return ( + slopedCrossing( + { offset: auto, elevation: invert }, + input.outward, + -PIPE_CONNECT_GRADE, + input.groundAt, + input.limitOffset, + ) !== null + ); +} + +/** + * 유입 기슭막이 4축 배치 — placePipeWall에 **유출 수용 가능성 가드**를 얹은 것. + * 유입을 내리면 유출도 따라 내려가므로(유입 > 유출 조건), 유출이 못 받는 높이면 + * 유입도 그만큼 못 내려간다(2026-08-22 사용자 확정). + */ +export function placeInletWall(input: { + autoOffset: number; + outward: number; + height: number; + baseElevation0: number; + edgeElevation: number; + invertCap: number; + adjust: WallAdjust; + groundAt: (offset: number) => number; + limitOffset: number; + /** 유출 쪽 수용 검사 인자 — 없으면 가드 없이 배치한다. */ + outletGuard?: { + edge: { offset_m: number; elevation_m: number }; + outward: number; + height: number; + toeOffset: number; + limitOffset: number; + }; +}): PipeWallPlacement { + const place = (adjust: WallAdjust): PipeWallPlacement => + placePipeWall({ ...input, adjust, requireCrossing: false }); + let placed = place(input.adjust); + const guard = input.outletGuard; + if (!guard) return placed; + const receivable = (invert: number): boolean => + outletReceivable({ inletElevation: invert, groundAt: input.groundAt, ...guard }); + for (let pass = 0; pass < 400 && placed.d > 1e-9 && !receivable(placed.invert); pass += 1) { + placed = place({ + ...input.adjust, + d: Math.max(0, Math.round((placed.d - 0.05) * 100) / 100), + }); + } + return placed; +} diff --git a/B06_Section/B06_Section_UI_Cross_View.ts b/B06_Section/B06_Section_UI_Cross_View.ts index bbff9ecc..22240986 100644 --- a/B06_Section/B06_Section_UI_Cross_View.ts +++ b/B06_Section/B06_Section_UI_Cross_View.ts @@ -473,9 +473,11 @@ export function createCrossSectionCard( toggleArea, ); // 배수관 세트 기하 계산 + 이동량 되받기(토스트) — 분리 파일(Wire, 700줄 제한). + // 계산은 **표시 반폭으로 자르지 않은 전체 샘플**로 한다(2026-08-22 사용자 확정) — + // 표시 반폭은 보기용이고, 사면 끝·지반 교차 판정은 계산용 전 구간이 필요하다. const culvertLayout = computeCardCulvert( section, - sourceSamples, + section.samples, activeRevet, revetOffset, inletStructure,