Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013rJwJMVL4EE31y77TB11AP
108 lines
4.6 KiB
TypeScript
108 lines
4.6 KiB
TypeScript
/* =============================================================================
|
|
* B06_Section_UI_Cross_Structure_Cut.ts
|
|
* 구조물 끝 → 원지반 **절토선 점열**(91-4) — 본선 절토 사면과 같은 복합경사.
|
|
*
|
|
* 꼭짓점은 `common_util_cross_berm.cutProfilePoints` 그대로(암 → 토사 무릎 · 소단) · 경계 = 원지반 +
|
|
* `rock_boundary_offset_m` · 켬 = 암 프리셋 + `two_stage_slope` · 소단 = `design.berm` · 소단 셈은
|
|
* 구조물 끝에서 새로 · 원지반 교점에서 자름.
|
|
* 쓰는 곳 = 흙막이 수평공간 뒤 · 박스 끝 · 세월교 측벽 끝 · ㄴ·ㄷ 집수정 끝 · 절토 쪽 배관 유출 벽 뒤.
|
|
* ========================================================================== */
|
|
|
|
import { bermSpec, cutProfilePoints } from "../common_util/common_util_cross_berm";
|
|
import type { BermSpec } from "../common_util/common_util_cross_berm";
|
|
import type { CrossDesign } from "./B06_Section_Api_Fetch";
|
|
import type { OffsetPoint } from "./B06_Section_UI_Cross_Culvert_Types";
|
|
|
|
/** 절토 제원 — 암(또는 단일) 경사 · 토사 경사 · 암 경계 오프셋(없으면 경사 하나) · 소단. */
|
|
export interface StructureCutSpec {
|
|
cutRatio: number;
|
|
soilRatio: number;
|
|
rockOffsetM: number | null;
|
|
berm: BermSpec | null;
|
|
}
|
|
|
|
/** 측점 설계값에서 절토 제원 — 본선 `computeCrossDesign` 과 같은 켬 조건. */
|
|
export function structureCutSpec(design?: Partial<CrossDesign> | null): StructureCutSpec {
|
|
const cutRatio = Math.max(design?.cut_slope_ratio ?? 1.0, 0.1);
|
|
const offset = design?.rock_boundary_offset_m;
|
|
const twoStage =
|
|
design?.geometry_preset === "rock" && design.two_stage_slope === true && offset != null;
|
|
const berm = design?.berm;
|
|
return {
|
|
cutRatio,
|
|
soilRatio: Math.max(design?.soil_cut_slope_ratio ?? cutRatio, 0.1),
|
|
rockOffsetM: twoStage ? offset : null,
|
|
berm: berm ? bermSpec(berm.width_m, berm.interval_m, berm.slope_deg) : null,
|
|
};
|
|
}
|
|
|
|
/** 원지반 교점 찾는 걸음(m). */
|
|
const STEP_M = 0.05;
|
|
|
|
/**
|
|
* `from`(원지반 아래) → 바깥(`outward`)으로 절토 꼭짓점 · 마지막 점 = 원지반 교점.
|
|
* `reachM` 안에서 못 만나면 null.
|
|
*/
|
|
export function structureCutPoints(
|
|
from: OffsetPoint,
|
|
outward: number,
|
|
spec: StructureCutSpec,
|
|
groundAt: (offset: number) => number,
|
|
reachM = 50,
|
|
): OffsetPoint[] | null {
|
|
const at = (dist: number): number => from.offset + outward * dist;
|
|
const rockOffsetM = spec.rockOffsetM;
|
|
const boundary = rockOffsetM === null ? null : (dist: number) => groundAt(at(dist)) + rockOffsetM;
|
|
const profile = cutProfilePoints(
|
|
0,
|
|
from.elevation,
|
|
spec.cutRatio,
|
|
spec.soilRatio,
|
|
boundary,
|
|
spec.berm,
|
|
Math.max(reachM, STEP_M),
|
|
// 소단이 있으면 경계를 오갈 때마다 꺾음 — 본선 `cutPoints` 와 같음.
|
|
spec.berm !== null,
|
|
);
|
|
const points: OffsetPoint[] = [from];
|
|
for (let i = 1; i < profile.length; i += 1) {
|
|
const [d0, z0] = profile[i - 1];
|
|
const [d1, z1] = profile[i];
|
|
const steps = Math.max(1, Math.ceil((d1 - d0) / STEP_M));
|
|
let previousGap = groundAt(at(d0)) - z0;
|
|
for (let k = 1; k <= steps; k += 1) {
|
|
const t = k / steps;
|
|
const gap = groundAt(at(d0 + (d1 - d0) * t)) - (z0 + (z1 - z0) * t);
|
|
if (gap <= 0) {
|
|
// 걸음 사이 교점 보간 — 선이 지반을 넘지 않게
|
|
const share = previousGap > 1e-12 ? previousGap / (previousGap - gap) : 1;
|
|
const tc = (k - 1 + share) / steps;
|
|
points.push({ offset: at(d0 + (d1 - d0) * tc), elevation: z0 + (z1 - z0) * tc });
|
|
return points;
|
|
}
|
|
previousGap = gap;
|
|
}
|
|
points.push({ offset: at(d1), elevation: z1 });
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 배관 유출 벽이 절토 쪽(벽 바깥 윗모서리가 원지반 아래)이면 그 모서리부터 절토선을 사면 점열 뒤에
|
|
* 이음 — 트림이 노견부터 설계선을 지워 절토사면이 사라지던 자리(91-4). 관 숨김(세월교 · 박스 흙막이 ·
|
|
* 독립 기슭막이)은 `enabled` 거짓.
|
|
*/
|
|
export function withBackCut<T extends { points: OffsetPoint[] }>(
|
|
slope: T,
|
|
wall: { role: string; outward: number; points: OffsetPoint[] },
|
|
design: Partial<CrossDesign> | null | undefined,
|
|
groundAt: (offset: number) => number,
|
|
enabled: boolean,
|
|
): T {
|
|
const corner = wall.points[2];
|
|
if (!enabled || wall.role !== "outlet" || !corner) return slope;
|
|
if (groundAt(corner.offset) <= corner.elevation + 0.05) return slope;
|
|
const cut = structureCutPoints(corner, wall.outward, structureCutSpec(design), groundAt);
|
|
return cut ? { ...slope, points: [...slope.points, ...cut] } : slope;
|
|
}
|