/* ============================================================================= * B06_Section_Cross_Wall_Geom.ts * 기슭막이 벽 1매 **기하** — 배관 세트 벽(`_Cross_Culvert_Geom`/`_Culvert`)과 독립 기슭막이 * (`_Cross_Revetment`)가 같은 산식을 쓴다. 그리기는 `../B06_Section_UI_Cross_Wall`(472-3). * ========================================================================== */ import { materialLabel, REVET_EMBED_DEPTH_M, REVET_LEAN_RATIO, REVET_THICKNESS_M, WALL_FILL_TOP_INSET_M, WALL_MIN_PURE_HEIGHT_M, } from "./B06_Section_UI_Cross_Culvert_Const"; import type { RevetMaterial } from "./B06_Section_UI_Cross_Culvert_Const"; import type { OffsetPoint, WallLayout } from "./B06_Section_Cross_Culvert_Types"; /** 벽 1매의 기하 입력 — 자리 기준은 **하단선 중점**(anchor.offset)과 그 자리 기준표고 * (anchor.elevation = 관 invert 또는 지반). 벽은 그 위로 `height`만큼 선다. */ export interface RevetWallGeometryInput { /** 하단선 중점 offset + 기준표고(벽이 이 위로 선다). */ anchor: OffsetPoint; /** +1 = 화면 좌측(계류측), −1 = 우측. 전면 1:0.3이 이 부호로 기운다. */ outward: number; /** 계산용 높이(기준표고~상단, 근입 0.5 제외). */ height: number; material: RevetMaterial; role: "inlet" | "outlet" | "extra"; /** 표기용 형태 — 미지정이면 재질 라벨(메/찰/콘크리트). */ form?: string | null; lengthM?: number | null; thickness?: number; floatGapM?: number; extraIndex?: number; /** 전면 기울기 1:n 의 n — 표준경사(`leanOf`). 없으면 종전 0.3. */ lean?: number; /** 노면(계획고) — 벽 윗면이 이보다 높으면 여기서 멈춤. 높이 한도는 이것 하나. */ roadElevation?: number | null; } /** * 합성 단면(하부 사다리꼴 + 상부 평행사변형) 꼭짓점을 만든다. 배관 벽(`buildWall`, * `_Cross_Culvert_Geom` 335~376행)과 **같은 산식** — 배면 수직, 상단 변 = 사다리꼴 * 상단(t/2)+띠(t), 전면 1:0.3, 하단선 = 기준표고 −0.5m 수평 기초. */ export function buildRevetWallGeometry(input: RevetWallGeometryInput): WallLayout { const { anchor, outward, material, role } = input; const road = input.roadElevation; const height = road != null && anchor.elevation + input.height > road + 1e-9 ? Math.max(road - anchor.elevation, WALL_MIN_PURE_HEIGHT_M - REVET_EMBED_DEPTH_M) : input.height; const thickness = input.thickness ?? REVET_THICKNESS_M; const lean = input.lean ?? REVET_LEAN_RATIO; const baseWidth = thickness * 1.5 + lean * height; const backOffset = anchor.offset - outward * (baseWidth / 2); const topJoint = backOffset + outward * (thickness / 2); const topElevation = anchor.elevation + height; const topBack: OffsetPoint = { offset: backOffset, elevation: topElevation }; const topFront = topJoint + outward * thickness; const frontXAt = (elevation: number): number => topFront + outward * lean * (topElevation - elevation); const bottomElevation = anchor.elevation - REVET_EMBED_DEPTH_M; const bottomBack: OffsetPoint = { offset: backOffset, elevation: bottomElevation }; const bottomFront: OffsetPoint = { offset: frontXAt(bottomElevation), elevation: bottomElevation, }; return { role, extraIndex: input.extraIndex, form: input.form ?? materialLabel(material), lengthM: input.lengthM ?? null, backOffset, outerOffset: bottomFront.offset, base: anchor.elevation, height, floatGapM: input.floatGapM ?? 0, material, lean, outward, topBack, topJoint: { offset: topJoint, elevation: topElevation }, fillTop: { offset: topFront - outward * WALL_FILL_TOP_INSET_M, elevation: topElevation }, bottomBack, bottomFront, points: [bottomBack, topBack, { offset: topFront, elevation: topElevation }, bottomFront], }; }