fix(B06): I형 집수정 관 하단을 벽면 안으로 제한 — 관이 벽을 관통하게

관 하단 자리는 벽 계류측 변과 원지반의 교점인데, 원지반이 벽 상단보다 높은 자리에서는
교점이 벽 상단으로 밀려 관이 벽 위에 얹혔다(측벽과 배관이 만나지 않음). ㄴ·ㄷ형은 관이
바닥판 윗면에서 시작해 벽 안에 물리는 것과 대비된다.

교점을 벽 상단에서 관경+여유(0.5m)만큼 아래로 제한한다. 원지반이 그보다 낮으면 종전대로
교점을 그대로 쓴다. 벽을 키우는 방향은 벽이 1:0.3으로 기울어 base가 계류측으로 밀리며
지반 교점이 함께 올라가 벽이 7.5m까지 자라 폐기했다.

검증 — 공용 브라우저 B06 I형 8개 단면 실측: 관 유입측 끝단과 벽의 세로 겹침
18.1~56.4px(수정 전 0 — 벽 위로 이탈). ㄴ형 대조(13+4.1을 조정창에서 ㄴ형으로 바꿔 확인)
겹침 유지, 확인 후 세션값 원복. pytest 370 passed·17 skipped, typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-03 10:32:20 +09:00
co-authored by Claude Opus 5
parent 9b4abecf9d
commit a7f6232c01
@@ -174,21 +174,15 @@ export function buildBasin(input: BasinBuildInput): BasinBuildResult {
}; };
// I형 벽 자리 = **상단 도로측 꼭짓점이 노견 끝 지점에 닿는다**(2026-08-22 사용자 // I형 벽 자리 = **상단 도로측 꼭짓점이 노견 끝 지점에 닿는다**(2026-08-22 사용자
// 확정). 벽 상단이 도로측으로 1:0.3 물러나므로 하단(base)은 그만큼 계류측 바깥. // 확정). 벽 상단이 도로측으로 1:0.3 물러나므로 하단(base)은 그만큼 계류측 바깥.
const wallBase = anchor.offset + outward * (lean * wallHeight); const wallBaseAt = (height: number): number => anchor.offset + outward * (lean * height);
// I형 벽 하단은 **원지반까지 묻는다**(2026-08-22 사용자 — 지반 아래 부재두께만큼 // I형 벽 하단은 **원지반까지 묻는다**(2026-08-22 사용자 — 지반 아래 부재두께만큼
// 근입 후 배관을 둔다). ㄴ·ㄷ형은 바닥판이 원지반을 대체하므로 현행 유지. // 근입 후 배관을 둔다). ㄴ·ㄷ형은 바닥판이 원지반을 대체하므로 현행 유지.
const iWallBottom = const iBottomAt = (base: number): number =>
shape === "I" Math.min(anchor.elevation, groundAt(base), groundAt(base + outward * memberT)) - floorThickness;
? Math.min(anchor.elevation, groundAt(wallBase), groundAt(wallBase + outward * memberT)) - /** 벽 계류측 변(하단-전면 → 상단-전면)과 원지반의 교점 — I형 관 하단 자리. */
floorThickness const groundOnFace = (points: OffsetPoint[]): OffsetPoint => {
: defaultBottom; const low = points[3];
const parts: BasinLayout["parts"] = [ const high = points[2];
{ kind: "wall", points: wallOf(wallBase, outward, iWallBottom) },
];
const iWallPoints = parts[0].points;
const iGroundIntersection = (): OffsetPoint => {
const low = iWallPoints[3];
const high = iWallPoints[2];
let lo = 0; let lo = 0;
let hi = 1; let hi = 1;
for (let pass = 0; pass < 32; pass += 1) { for (let pass = 0; pass < 32; pass += 1) {
@@ -206,6 +200,34 @@ export function buildBasin(input: BasinBuildInput): BasinBuildResult {
elevation: low.elevation + (high.elevation - low.elevation) * t, elevation: low.elevation + (high.elevation - low.elevation) * t,
}; };
}; };
const wallBase = wallBaseAt(wallHeight);
const iWallBottom = shape === "I" ? iBottomAt(wallBase) : defaultBottom;
const parts: BasinLayout["parts"] = [
{ kind: "wall", points: wallOf(wallBase, outward, iWallBottom) },
];
const iWallPoints = parts[0].points;
/** 벽 계류측 변 위에서 그 표고에 해당하는 점 — 관 하단을 벽면을 따라 내릴 때 쓴다. */
const pointOnFaceAt = (elevation: number): OffsetPoint => {
const low = iWallPoints[3];
const high = iWallPoints[2];
const span = high.elevation - low.elevation;
const t = span > 1e-9 ? Math.min(Math.max((elevation - low.elevation) / span, 0), 1) : 0;
return {
offset: low.offset + (high.offset - low.offset) * t,
elevation: low.elevation + span * t,
};
};
/**
* I형 관 하단 자리 = 벽 계류측 변과 원지반의 교점. 다만 **관이 벽을 관통해야** 하므로
* (ㄴ·ㄷ형은 바닥판 위에서 이미 물린다) 벽 상단에서 관경+여유만큼 아래로 제한한다.
* 원지반이 벽 상단보다 높은 자리에서 제한이 없으면 관이 벽 위로 떠 벽과 만나지 않았다
* (2026-09-03 사용자 지적: 「측벽과 배관의 형상이 교차하고 있어야 함」).
*/
const iGroundIntersection = (): OffsetPoint => {
const hit = groundOnFace(iWallPoints);
const invertCeiling = anchor.elevation + wallHeight - (diameterM + REVET_FREEBOARD_M);
return hit.elevation <= invertCeiling ? hit : pointOnFaceAt(invertCeiling);
};
// ㄴ형 = I형 + 바닥판. 바닥은 벽 바깥(계류측) 변에 맞대고 그 너머로 뻗는다 // ㄴ형 = I형 + 바닥판. 바닥은 벽 바깥(계류측) 변에 맞대고 그 너머로 뻗는다
// (2026-08-20 확정). 벽이 기울어 있어 안쪽 변도 벽 바깥면 선을 그대로 따른다. // (2026-08-20 확정). 벽이 기울어 있어 안쪽 변도 벽 바깥면 선을 그대로 따른다.
const wallOuterAt = (elevation: number): number => { const wallOuterAt = (elevation: number): number => {