feat(B06): I형 관 하단 수평 되메움선 — 지반 교점 절단 철회

- 지반 교점 절단(직전 커밋)은 사용자 확인 후 철회 — 관 끝은 벽 안쪽 변
  절단으로 복귀.
- 대신 관 유입 하단 꼭짓점이 원지반 위에 뜨면 그 표고 그대로 계류측으로
  **수평 되메움(성토)선**을 그어 원지반과 연결(applyBasinPipeFill —
  Basin 분리, Geom 700줄 유지).
- 검증: 13+15.7 I형 실화면 — 성토선 수평·관 꼭짓점 부착 0.4px, 스크린샷.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-22 12:42:12 +09:00
co-authored by Claude Opus 5
parent 7ded6be4d1
commit 97053ab0d2
3 changed files with 34 additions and 28 deletions
+4 -1
View File
@@ -239,7 +239,10 @@ export function appendCulvertOverlay(
fill.setAttribute("y2", String(toDisplayY(basin.fillLine.to.elevation)));
fill.setAttribute("class", "b06-chart__design-cross");
const fillTitle = document.createElementNS(SVG_NS, "title");
fillTitle.textContent = `집수정(${shapeName}) 성토(되메움)선 — 구조물이 원지반 밖으로 나와 5° 경사로 채움`;
fillTitle.textContent =
basin.shape === "I"
? "집수정(I형) 되메움(성토)선 — 관 하단에서 수평으로 원지반과 연결"
: `집수정(${shapeName}) 성토(되메움)선 — 구조물이 원지반 밖으로 나와 5° 경사로 채움`;
fill.append(fillTitle);
layer.append(fill);
}
@@ -250,3 +250,24 @@ export function inletChoiceAvailability(input: {
});
return { revetAllowed, basinLUAllowed: probe.basin.cutLine != null, revetAutoOffset };
}
/**
* I형 집수정의 **관 하단 수평 되메움(성토)선**(2026-08-22 사용자). 관 유입 하단
* 꼭짓점이 원지반 위에 뜨면 그 표고 그대로 계류측으로 나가 원지반과 만나는 점까지
* 잇고, 그 아래를 성토로 본다. 지반 교점 절단 방식은 보고 후 철회.
*/
export function applyBasinPipeFill(
basin: BasinLayout,
corner: OffsetPoint,
outward: number,
groundAt: (offset: number) => number,
): void {
if (basin.shape !== "I" || corner.elevation <= groundAt(corner.offset) + 0.02) return;
for (let run = 0.05; run <= 30; run += 0.05) {
const probe = corner.offset + outward * run;
if (groundAt(probe) >= corner.elevation) {
basin.fillLine = { from: corner, to: { offset: probe, elevation: corner.elevation } };
return;
}
}
}
@@ -39,6 +39,7 @@ export * from "./B06_Section_UI_Cross_Culvert_Const";
export * from "./B06_Section_UI_Cross_Culvert_Types";
import {
applyBasinPipeFill,
BASIN_INNER_WIDTH_M,
buildBasin,
inletChoiceAvailability,
@@ -137,16 +138,12 @@ export function computeCulvertLayout(
: ruleReason;
// 집수정 형식 기본값 = ㄴ(L)형(2026-08-20 확정) — 선택 시 그 형식.
const basinShape: BasinShape = choice === "I" || choice === "L" || choice === "U" ? choice : "L";
// ㄴ·ㄷ형은 **측면·바닥 사이즈 유지한 채 통째로 노견 끝점 일치**시킨다(2026-08-22
// 사용자 확정 — 노견과 빈 공간을 채운다). 바닥(=관 유입 invert)이 따라 올라가므로
// 관 물매도 바뀐다. 유출단은 원지반 위 원칙 그대로. I형은 원지반 배관을 유지하고
// 벽 높이로만 노견에 연결한다(buildWall).
// ㄴ·ㄷ형은 사이즈 유지한 채 통째로 노견 끝점 일치(2026-08-22 확정) — 바닥(=관
// 유입 invert)이 따라 올라 물매도 바뀐다. I형은 원지반 배관 유지(상세 Basin 참조).
if (basinReason && basinShape !== "I") {
inlet.elevation = inletInfo.edge.elevation_m - BASIN_INNER_HEIGHT_M;
} else if (basinReason) {
// I형 — 관 시작점(내공 1.0m 자리) invert는 **그 x의 원지반 위**에 앉힌다(토피
// 상한 이내). 종전에는 노견 x의 원지반을 써서 지반이 오르내리는 자리에서 관이
// 지반에 파묻히거나 떴다(2026-08-22 사용자 ② — 13+15.7 확인).
// I형 — 관 시작점(내공 1.0m 자리) invert = 그 x의 원지반(토피 상한 이내).
const startOffset = inlet.offset + inletInfo.outward * BASIN_INNER_WIDTH_M;
inlet.elevation = Math.min(groundAt(startOffset), invertCap(inletInfo.edge));
}
@@ -475,26 +472,6 @@ export function computeCulvertLayout(
};
};
deriveAxis();
// I형 집수정: 관 유입 끝의 마감면을 **관 하단선이 원지반과 만나는 점**으로 옮긴다
// (2026-08-22 사용자 — 관 파란선 시작 꼭짓점이 지반 녹색선 위에 있어야 한다).
// invert가 토피 상한에 걸려 지반보다 낮으면 벽 변에서 자른 꼭짓점이 지반과 떠서
// 어긋나 보였다. 마감면 기울기(벽과 나란)는 유지하고 기준점만 교점으로 바꾼다.
if (basin && basinShape === "I" && endFaces.inlet) {
let prevDiff = pipeStart.elevation - groundAt(pipeStart.offset);
for (let t = 0.02; t <= 3; t += 0.02) {
const px = pipeStart.offset + axis.offset * t;
const pe = pipeStart.elevation + axis.elevation * t;
const diff = pe - groundAt(px);
if (Math.abs(diff) < 0.005 || Math.sign(diff) !== Math.sign(prevDiff)) {
endFaces.inlet = {
base: { offset: px, elevation: pe },
direction: endFaces.inlet.direction,
};
break;
}
prevDiff = diff;
}
}
const endCorners = (role: "inlet" | "outlet", endPoint: OffsetPoint): PipeEnd => {
const fallback: PipeEnd = {
bottom: endPoint,
@@ -600,6 +577,11 @@ export function computeCulvertLayout(
: 0;
}
// I형 집수정: 관 하단 꼭짓점이 지반 위에 뜨면 수평 되메움선(Basin 분리 — 700줄).
if (basin) {
applyBasinPipeFill(basin, pipeCorners.inlet.bottom, inletInfo.outward, groundAt);
}
// 보호공(돌붙임) — 유출측. 2026-08-20 사용자 ①·②:
// ① 시작 변은 **기슭막이 전면과 같은 경사**(밑면을 벽 전면 방향으로 민다),
// 윗면 시작점 = **관 하단 꼭짓점**(관 밑면과 한 점에서 만난다).