/* ============================================================================= * B06_Section_UI_Cross_Culvert_Tiers.ts * 배수관 세트 기하(`_Cross_Culvert_Geom`)에서 떼어 낸 **벽 자리 도우미 + 다단 흙막이** 층. * 다단 벽은 `buildExtrasAt`(공용 `buildRevetWallGeometry`)이 세우고, 여기서는 유출측과 * 집수정 계류측 두 채널의 입력을 짜고 면적용(미확정 단 제외) 결과를 함께 돌려준다. * ========================================================================== */ import type { CrossSection, CulvertSideSpec } 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"; // 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`)과 // 같은 선을 면적도 보게 맞춘다 — 그리지 않는 `cut` 갈래(벽이 원지반에 묻힌 자리)는 뺀다. // 세월교 · 박스 흙막이(`withCut`)는 수평공간 뒤 절토선을 그리므로 넣음(79-7). 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, }); // 세월교 · 박스 흙막이 끝 절토 = 컨테이너 수평공간 뒤 절토 경사(79-7) · 배관은 종전(3도 선). const cutBerm = (spec: CulvertSideSpec | undefined) => culvert?.body ? { lengthM: Math.max(spec?.revet_berm_m ?? GUARD_CUT_BERM_M, 0), slopeRatio: section.design?.cut_slope_ratio ?? 1.0, } : undefined; // 성토부선 + 다단 기슭막이(2026-08-22 — 보호공 삭제, 윗면 선만 성토부선으로). // 유출측과 **집수정 계류측**이 같은 체계를 쓴다: 5m 넘으면 다단을 둘 수 있다. const outletInput = { cutBerm: cutBerm(culvert?.outlet), 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?.inlet), 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), }; }