Files
Aislo/B06_Section/B06_Section_UI_Cross_Culvert_Tiers.ts
T
eomsangdonandClaude Opus 5.5 e055de13e1 feat(b06): 묻힌 흙막이 끝 = 3도 수평 절토선 · 못 만나면 복합경사 · 유출 절토 면적 반영(139-4 · 139-5)
- 끝 연결선 한 규칙: 묻히면 3도 내림 선 먼저 · 원지반 못 만나면 수평공간 뒤 절토 경사(배관 · 세월교 · 박스)
- 유출 3도 절토선 = 보라 선으로 그림 · 폐회로 절토 면적에 넣음(집수정 계류측은 그림만)
- 시험 test_139_4_buried_outlet.py 새로 · test_79 · test_91 유출 기대 고침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018jonfdxn1Xit9bdkNJpE4G
2026-10-09 15:12:57 +09:00

145 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* B06_Section_UI_Cross_Culvert_Tiers.ts
* 배수관 세트 기하(`_Cross_Culvert_Geom`)에서 떼어 낸 **벽 자리 도우미 + 다단 흙막이** 층.
* 다단 벽은 `buildExtrasAt`(공용 `buildRevetWallGeometry`)이 세우고, 여기서는 유출측과
* 집수정 계류측 두 채널의 입력을 짜고 면적용(미확정 단 제외) 결과를 함께 돌려준다.
* ========================================================================== */
import type { CrossSection } from "./B06_Section_Api_Fetch";
import {
GUARD_CUT_BERM_M,
REVET_EMBED_DEPTH_M,
REVET_TRAP_TOP_M,
} from "./B06_Section_UI_Cross_Culvert_Const";
import type { OutletExtrasResult } from "./B06_Section_UI_Cross_Culvert_Extra";
import { buildExtrasAt } from "./B06_Section_UI_Cross_Culvert_Extra";
import { fillWallBaseWidth } from "./B06_Section_UI_Cross_Culvert_Solve";
import type { OffsetPoint, WallAdjust, WallLayout } from "./B06_Section_UI_Cross_Culvert_Types";
import { outlineLeanOf } from "./B06_Section_UI_Cross_Lean";
import { structureCutSpec } from "./B06_Section_UI_Cross_Structure_Cut";
// d의 0점 = **성토선 길이 0 지점**(벽 상단 = 노견 — 2026-08-23 사용자 확정).
// 그 자리(하단선 중점)는 이음선 상단점이 노견 끝에 오는 offset이고, 기본 자리는
// 그 점 자체다 · 손으로 x 를 넣은 벽만 경사길이 4m 지점(`pipeWallDefaultD` · 79-5 · 79b).
export const slopeZeroAnchor = (
edge: { offset_m: number },
outward: number,
wallHeight: number,
lean: number,
): number => edge.offset_m + outward * (fillWallBaseWidth(wallHeight, lean) / 2 - REVET_TRAP_TOP_M);
/** 유출 관 하단 끝점 목표 = 벽 바닥 기준 0.5m 상단(기준선)과 **전면 경사선의
* 교차점**(2026-08-22 사용자 ① — 성토부선 시작 규칙과 같은 자리). */
export const outletPipeEnd = (wall: WallLayout): OffsetPoint => {
// 기준 = 바닥 +0.5(= 관 invert 자리 — 2026-08-23부터 바닥은 invert−0.5 고정).
const reference = wall.bottomBack.elevation + REVET_EMBED_DEPTH_M;
return {
offset:
wall.points[2].offset + wall.outward * wall.lean * (wall.topJoint.elevation - reference),
elevation: reference,
};
};
// 다단 기슭막이의 성토부선까지 트림에 넣는다 — 넣지 않으면 단을 올려도 폐회로가 그대로라
// **물량이 안 바뀐다**(2026-09-07 사용자 확정). 화면이 그리는 선(`outletFill.segments`)과
// 같은 선을 면적도 보게 맞춘다 — `withCut` 이면 묻힌 끝 절토선(`cut` 갈래)도 넣음(유출 = 늘 ·
// 139-4 · 계류측 = 세월교 · 박스만 79-7). 집수정 계류측 3도 선은 그림만.
export const drawnFillPoints = (result: OutletExtrasResult, withCut = false): OffsetPoint[] =>
result.segments
.filter((segment) => withCut || segment.kind !== "cut")
.flatMap((segment) => segment.points);
interface SideInfo {
outward: number;
limit: number;
}
/** 다단 흙막이 두 채널 — 유출측(`extras`)과 집수정 계류측·독립 기슭막이 유입측(`basinExtras`). */
export function buildCulvertTiers(input: {
section: CrossSection;
outletStart: OffsetPoint | null;
outletBottom: number;
basinStart: OffsetPoint | null;
basinBottom: number;
outletInfo: SideInfo;
inletInfo: SideInfo;
groundAt: (offset: number) => number;
extras: WallAdjust[];
basinExtras: WallAdjust[];
outletForm: string;
inletForm: string;
equalize: boolean;
}): {
extras: OutletExtrasResult;
basinExtras: OutletExtrasResult;
extrasForArea: OutletExtrasResult;
basinExtrasForArea: OutletExtrasResult;
} {
const { section, groundAt } = input;
const culvert = section.culvert;
// 다단 벽 기울기 — 같은 측점·같은 설치 측 규칙으로 높이마다 표준경사(형태 무관 · B08 은 다단을 안 셈).
const extraLean = (form: string, pureHeightM: number): number =>
outlineLeanOf({
form,
height_m: pureHeightM,
section_mode: section.design?.section_mode,
side: culvert?.hidden_pipe ? culvert.inlet.face_side : null,
});
// 묻힌 끝 = 3도 절토선 먼저 · 원지반을 못 만나면 수평공간 뒤 절토 경사(139-4 · 139-5 — 배관 ·
// 세월교 · 박스 한 규칙). 수평공간 = 세월교 · 박스 유입 흙막이 값(79-7)만 · 나머지는 0.
const cutBerm = (lengthM: number) => ({
lengthM: Math.max(lengthM, 0),
cut: structureCutSpec(section.design),
});
// 성토부선 + 다단 기슭막이(2026-08-22 — 보호공 삭제, 윗면 선만 성토부선으로).
// 유출측과 **집수정 계류측**이 같은 체계를 쓴다: 5m 넘으면 다단을 둘 수 있다.
const outletInput = {
cutBerm: cutBerm(0), // 유출 수평공간 없음 — 3도 선이 못 만날 때만 바로 절토(89-4)
startBottomElevation: input.outletBottom,
outward: input.outletInfo.outward,
groundAt,
limitOffset: input.outletInfo.limit,
adjusts: input.extras,
ownerForm: input.outletForm,
equalize: input.equalize,
leanFor: extraLean,
};
const extras = buildExtrasAt(input.outletStart, outletInput);
// 독립 기슭막이(관 숨김)는 집수정이 없어 이 채널이 **유입측 벽의 성토부선·다단**이다 —
// 유출측(`extras`)과 같은 시작점(관 하단 꼭짓점)·같은 `bextra` 키(2026-08-29 사용자).
const basinInput = {
cutBerm: cutBerm(culvert?.body ? (culvert.inlet.revet_berm_m ?? GUARD_CUT_BERM_M) : 0),
startBottomElevation: input.basinBottom,
outward: input.inletInfo.outward,
groundAt,
limitOffset: input.inletInfo.limit,
adjusts: input.basinExtras,
ownerForm: input.inletForm,
equalize: false,
leanFor: extraLean,
};
const basinExtras = buildExtrasAt(input.basinStart, basinInput);
/** ⭐ 면적용 다단 — **미확정 단(높이 안 적음)부터 아래는 없는 것**으로 봄(2026-09-14 브레인 판정
* 「미확정 벽은 성토도 안 깎음 · 벽 금액과 성토 감소가 함께」). 그림은 그대로 다 그림.
* B08 도 첫 미확정 단 아래는 미확정으로 셈(`facility_structures`) — 두 곳이 같은 선에서 끊김. */
const confirmedOnly = (
built: OutletExtrasResult,
start: OffsetPoint | null,
channel: typeof outletInput,
): OutletExtrasResult => {
const firstUnset = built.appliedAdjusts.findIndex((applied) => applied.h == null);
if (firstUnset < 0) return built;
return buildExtrasAt(start, {
...channel,
adjusts: channel.adjusts.slice(0, firstUnset),
equalize: false,
});
};
return {
extras,
basinExtras,
extrasForArea: confirmedOnly(extras, input.outletStart, outletInput),
basinExtrasForArea: confirmedOnly(basinExtras, input.basinStart, basinInput),
};
}