- 옮김: B04 2 · B05 23 · B06 11 · A00_Common/design 13 (9,299 줄) · 이미 logic/ 에 있던 16 파일 그대로 - 안 옮김: 묶음 입구(package.json build:*) · 파이썬 · 순수지만 계산 아닌 8 (보기 결선 · 콜백 · API 타입 · 갈아끼움 알림판) - import 266 줄(142 파일) · 시험 · 주석 · 짝 표시 문자열 경로 고침 · B05 · B06 CLAUDE.md 짝 줄 logic/ - 시험 도우미 새 경로: 가짜 require 열쇠 · B05()/B06() 부름 · 형제 import 자리 · tsc 묶어 돌리는 시험 · 정규식 · rglob - design_view_cross 줄 한도 시험 685 → 688 (긴 import 경로 줄바꿈 3줄) - 확인: typecheck 0 · 서버 묶음 넷(corridor · server-calc · b07-extent · b07-note) 다시 지어 코드 같음(//#region 경로 주석만 다름) · 관련 시험 270 파일 — 남은 실패 19 는 옮기기 전과 같음 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
145 lines
6.8 KiB
TypeScript
145 lines
6.8 KiB
TypeScript
/* =============================================================================
|
||
* 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 "./logic/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 "./logic/B06_Section_UI_Cross_Culvert_Solve";
|
||
import type { OffsetPoint, WallAdjust, WallLayout } from "./B06_Section_UI_Cross_Culvert_Types";
|
||
import { outlineLeanOf } from "./logic/B06_Section_UI_Cross_Lean";
|
||
import { structureCutSpec } from "./logic/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),
|
||
};
|
||
}
|