feat(B06): 집수정 조건 재정의 — 성토사면 3m 기준·통째 노견 일치

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) <noreply@anthropic.com>
This commit is contained in:
2026-08-22 10:46:58 +09:00
co-authored by Claude Opus 5
parent 0ebfa6c2aa
commit 934419bbaa
4 changed files with 42 additions and 15 deletions
+2 -2
View File
@@ -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(
@@ -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;
@@ -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)은 원지반 자리를 그대로 지킨다.
@@ -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;
}