feat(b06): 사면길이 3m 초과 소단 검토 정보 한 줄 — 성토사면 경고 밑 흐린 색 · 절·성토 둘 다 소단 사이 가장 긴 도막 · 벽 선 쪽 뺌 · 경고 아님(별표2 차.(5) 「붕괴 우려 지역」은 우리가 판정 못 함 · 브레인 ③)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
This commit is contained in:
@@ -146,6 +146,69 @@ export function fillSlopeLengths(section: CrossSection): {
|
||||
return lengths;
|
||||
}
|
||||
|
||||
/**
|
||||
* 좌·우 사면(**절토·성토 둘 다**)에서 소단 사이 **가장 긴 도막**의 경사길이(m) — 소단 검토 정보용
|
||||
* (2026-09-15 브레인 ③). 구간·교차점은 `fillSlopeLengths` 와 같고, 절토는 2단 경사라 설계선
|
||||
* 도막마다 경사길이를 더한다. 평탄한 도막(소단)에서 끊는다. 사면이 없는 측은 null.
|
||||
*/
|
||||
export function slopeRunLengths(section: CrossSection): {
|
||||
left: FillSlopeLength | null;
|
||||
right: FillSlopeLength | null;
|
||||
} {
|
||||
const lengths: { left: FillSlopeLength | null; right: FillSlopeLength | null } = {
|
||||
left: null,
|
||||
right: null,
|
||||
};
|
||||
const design = section.design;
|
||||
if (!design) return lengths;
|
||||
const groundAt = groundInterpolator(section.samples);
|
||||
const designAt = designInterpolator(design.design_line);
|
||||
const edges = design.road_edges;
|
||||
if (!groundAt || !designAt || !edges) return lengths;
|
||||
const lineOffsets = design.design_line.map((point) => point.offset_m);
|
||||
if (lineOffsets.length < 2) return lengths;
|
||||
const { protectMax, protectMin } = protectedSpan(design, edges);
|
||||
// 소단(2°≈0.035)과 사면(1:0.3~2.0)을 가르는 기울기 — 가장 누운 사면 기울기의 절반.
|
||||
const gradients = [design.fill_slope_ratio, design.cut_slope_ratio, design.soil_cut_slope_ratio]
|
||||
.filter((ratio): ratio is number => typeof ratio === "number" && ratio > 0)
|
||||
.map((ratio) => 1 / ratio);
|
||||
const flatBelow = (gradients.length ? Math.min(...gradients) : 0.5) * 0.5;
|
||||
for (const side of ["left", "right"] as const) {
|
||||
const outward = side === "left" ? 1 : -1;
|
||||
const start = side === "left" ? protectMax : protectMin;
|
||||
const limit = side === "left" ? Math.max(...lineOffsets) : Math.min(...lineOffsets);
|
||||
const meet = meetOffset(designAt, groundAt, start, limit, outward);
|
||||
const end = Math.abs(meet) > Math.abs(limit) ? limit : meet;
|
||||
const low = Math.min(start, end);
|
||||
const high = Math.max(start, end);
|
||||
let longest = 0;
|
||||
let current = 0;
|
||||
for (let index = 1; index < design.design_line.length; index += 1) {
|
||||
const a = design.design_line[index - 1];
|
||||
const b = design.design_line[index];
|
||||
const run = Math.abs(b.offset_m - a.offset_m);
|
||||
const from = Math.max(Math.min(a.offset_m, b.offset_m), low);
|
||||
const to = Math.min(Math.max(a.offset_m, b.offset_m), high);
|
||||
if (run <= 1e-9 || to - from <= 1e-9) continue;
|
||||
const rise = Math.abs(b.elevation_m - a.elevation_m);
|
||||
if (rise / run < flatBelow) {
|
||||
longest = Math.max(longest, current);
|
||||
current = 0;
|
||||
continue;
|
||||
}
|
||||
current += ((to - from) / run) * Math.hypot(run, rise);
|
||||
}
|
||||
const lengthM = Math.max(longest, current);
|
||||
if (lengthM > 0) {
|
||||
lengths[side] = {
|
||||
lengthM,
|
||||
open: Math.abs(designAt(end) - groundAt(end)) > MEET_TOLERANCE_M,
|
||||
};
|
||||
}
|
||||
}
|
||||
return lengths;
|
||||
}
|
||||
|
||||
/**
|
||||
* 소단으로 끊긴 성토사면에서 **가장 긴 한 구간**의 경사길이(m).
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user