Merge remote-tracking branch 'origin/main_laptop_1' into sub_desktop_1
This commit is contained in:
@@ -131,3 +131,38 @@ def _bench_cut_length(offsets: list[float], grounds: list[float], diffs: list[fl
|
||||
continue
|
||||
total += ((run**2 + rise**2) ** 0.5) * share
|
||||
return total
|
||||
|
||||
|
||||
def _split_ditch_area(ditch_spec: dict, depth_to_boundary_m: float | None) -> tuple[float, float]:
|
||||
"""측구 단면적을 (토사, 암반)으로 가른다 — 암반 경계선까지의 깊이 기준.
|
||||
|
||||
`depth_to_boundary_m` 은 **측구 상단에서 암반 경계선까지의 깊이(m)** 다.
|
||||
`None` 이면 가를 근거가 없다는 뜻이라 부르는 쪽이 처리한다(여기서는 안 부른다).
|
||||
|
||||
⚠ 측구 단면은 **공칭 도형**(사다리꼴·L형 근사)이라 지반선을 따라 적분하지 않는다.
|
||||
경계선도 그 자리 한 높이로 본다 — 폭 1m 안팎에서 지반선 기울기 차이는 도형 근사보다
|
||||
작다. 절토 면적 분리(`_split_cut_areas`)가 균일 두께를 쓰는 것과 같은 태도다.
|
||||
"""
|
||||
kind = str(ditch_spec.get("type") or "none")
|
||||
if kind == "l_type":
|
||||
width = float(ditch_spec.get("width_m") or 0.0)
|
||||
depth = float(ditch_spec.get("depth_m") or 0.0)
|
||||
total = width * depth / 2.0
|
||||
if depth <= 0 or width <= 0:
|
||||
return 0.0, 0.0
|
||||
d0 = min(max(depth_to_boundary_m or 0.0, 0.0), depth)
|
||||
# 깊이 d 에서의 가로 폭 = W(1 − d/D). 위에서 d0 까지 적분한다.
|
||||
soil = width * d0 - width * d0 * d0 / (2.0 * depth)
|
||||
return soil, max(total - soil, 0.0)
|
||||
if kind == "standard":
|
||||
top = float(ditch_spec.get("top_width_m") or 0.0)
|
||||
bottom = min(float(ditch_spec.get("bottom_width_m") or 0.0), top)
|
||||
depth = float(ditch_spec.get("depth_m") or 0.0)
|
||||
total = (top + bottom) / 2.0 * depth
|
||||
if depth <= 0 or top <= 0:
|
||||
return 0.0, 0.0
|
||||
d0 = min(max(depth_to_boundary_m or 0.0, 0.0), depth)
|
||||
# 깊이 d 에서의 폭 = top − (top−bottom)·d/depth. 위에서 d0 까지 적분한다.
|
||||
soil = top * d0 - (top - bottom) * d0 * d0 / (2.0 * depth)
|
||||
return soil, max(total - soil, 0.0)
|
||||
return 0.0, 0.0
|
||||
|
||||
@@ -34,6 +34,7 @@ from typing import Any
|
||||
from B06_Section.B06_Section_Engine_Areas import (
|
||||
_bench_cut_length,
|
||||
_split_cut_areas,
|
||||
_split_ditch_area,
|
||||
_trapezoid_areas,
|
||||
)
|
||||
from common_util.common_util_cross_berm import (
|
||||
@@ -749,6 +750,28 @@ def compute_cross_design(
|
||||
"depth_m": group["ditch_depth_m"],
|
||||
}
|
||||
|
||||
# 측구터파기 토사/암 분리 — **새 입력을 만들지 않는다.** 절토 분리와 같은 근거
|
||||
# (지반 유형 + 암반 경계선)를 그대로 쓴다. 별표2 Ⅰ.1.나.(5) 「측구터파기 단면적」이
|
||||
# 횡단도 표의 법정 칸이라 반만 채워 나가면 안 된다(2026-09-09).
|
||||
# 근거가 없으면 **나누지 않고** 사유를 함께 내보낸다 — 절반을 임의로 가르지 않는다.
|
||||
if not geometry.has_ditch:
|
||||
ditch_soil_area, ditch_rock_area = 0.0, 0.0
|
||||
ditch_split_basis = "no_ditch"
|
||||
elif preset_key != "rock":
|
||||
# 토사 지반 — 암반 경계선 자체가 없다. 전량 토사(절토 분리와 같은 판정).
|
||||
ditch_soil_area, ditch_rock_area = ditch_area, 0.0
|
||||
ditch_split_basis = "soil_ground"
|
||||
elif rock_boundary_offset_m is None or not geometry.ditch_points:
|
||||
# 암 지반인데 경계선 값이 없다(구 데이터) — 가를 근거가 없으므로 전량 암.
|
||||
ditch_soil_area, ditch_rock_area = 0.0, ditch_area
|
||||
ditch_split_basis = "rock_ground_no_boundary"
|
||||
else:
|
||||
ditch_top_z = geometry.ditch_points[0][1]
|
||||
mid_offset = sum(point[0] for point in geometry.ditch_points) / len(geometry.ditch_points)
|
||||
boundary_z = ground_at(mid_offset) - abs(float(rock_boundary_offset_m))
|
||||
ditch_soil_area, ditch_rock_area = _split_ditch_area(ditch_spec, ditch_top_z - boundary_z)
|
||||
ditch_split_basis = "rock_boundary"
|
||||
|
||||
# 자동 판정된 절/성토 역할에서 실제 단면 유형을 도출해 echo한다(D-2, 표시·저장용).
|
||||
if geometry.left_role == "cut" and geometry.right_role == "cut":
|
||||
resolved_mode = "both_cut"
|
||||
@@ -824,6 +847,12 @@ def compute_cross_design(
|
||||
round(fill_ground_slope, 4) if fill_ground_slope is not None else None
|
||||
),
|
||||
"ditch_area_m2": round(ditch_area, 4),
|
||||
# 측구터파기 내역(합=ditch_area_m2). 가른 근거는 `ditch_split_basis` 로 함께 낸다:
|
||||
# rock_boundary(암반 경계선으로 가름) · soil_ground(토사 지반이라 전량 토사) ·
|
||||
# rock_ground_no_boundary(암 지반인데 경계선 없음 — 전량 암) · no_ditch(측구 없음).
|
||||
"ditch_soil_area_m2": round(ditch_soil_area, 4),
|
||||
"ditch_rock_area_m2": round(ditch_rock_area, 4),
|
||||
"ditch_split_basis": ditch_split_basis,
|
||||
"design_line": design_line,
|
||||
# 절토 사면을 경사 구간별로 쪼갠 목록(소단 제외).
|
||||
# ⚠ **지금 이 값을 읽는 곳은 없다**(2026-09-07). 원래 임자였던 별표2 법정 경사 검사는
|
||||
|
||||
@@ -24,7 +24,12 @@
|
||||
* ========================================================================== */
|
||||
|
||||
import type { BermSpec } from "./common_util_cross_berm";
|
||||
import { benchCutLength, splitCutAreas, trapezoidAreas } from "./common_util_cross_design_areas";
|
||||
import {
|
||||
benchCutLength,
|
||||
splitCutAreas,
|
||||
splitDitchArea,
|
||||
trapezoidAreas,
|
||||
} from "./common_util_cross_design_areas";
|
||||
// 단면 기하(노면·측구·사면 설계고)는 파일이 700줄을 넘어 떼어냈다(2026-09-04).
|
||||
import {
|
||||
CURVE_WIDENING_MAX_WIDTH_M,
|
||||
@@ -136,6 +141,10 @@ export interface CrossDesignResult {
|
||||
slope_unclosed: boolean;
|
||||
fill_ground_slope: number | null;
|
||||
ditch_area_m2: number;
|
||||
/** 측구터파기 내역(합=ditch_area_m2)과 가른 근거. 짝: 파이썬 `ditch_split_basis`. */
|
||||
ditch_soil_area_m2: number;
|
||||
ditch_rock_area_m2: number;
|
||||
ditch_split_basis: string;
|
||||
design_line: CrossDesignEdge[];
|
||||
/** 절토 사면 경사 구간(소단 제외). 짝: `cut_slope_segments`.
|
||||
* ⚠ **지금 읽는 곳은 없다**(2026-09-07) — 임자였던 별표2 검사는 폐기됐고 저장분에도
|
||||
@@ -395,6 +404,37 @@ export function computeCrossDesign(
|
||||
};
|
||||
}
|
||||
|
||||
// 측구터파기 토사/암 분리 — **새 입력을 만들지 않는다.** 절토 분리와 같은 근거
|
||||
// (지반 유형 + 암반 경계선)를 그대로 쓴다. 근거가 없으면 나누지 않고 사유를 낸다.
|
||||
// ⚠ 파이썬 짝: `B06_Section_Engine_Design` 의 같은 자리.
|
||||
let ditchSoilArea: number;
|
||||
let ditchRockArea: number;
|
||||
let ditchSplitBasis: string;
|
||||
if (!geometry.hasDitch) {
|
||||
ditchSoilArea = 0;
|
||||
ditchRockArea = 0;
|
||||
ditchSplitBasis = "no_ditch";
|
||||
} else if (presetKey !== "rock") {
|
||||
ditchSoilArea = ditchArea;
|
||||
ditchRockArea = 0;
|
||||
ditchSplitBasis = "soil_ground";
|
||||
} else if (
|
||||
rockBoundaryOffsetM === null ||
|
||||
rockBoundaryOffsetM === undefined ||
|
||||
!geometry.ditchPoints.length
|
||||
) {
|
||||
ditchSoilArea = 0;
|
||||
ditchRockArea = ditchArea;
|
||||
ditchSplitBasis = "rock_ground_no_boundary";
|
||||
} else {
|
||||
const ditchTopZ = geometry.ditchPoints[0][1];
|
||||
const midOffset =
|
||||
geometry.ditchPoints.reduce((sum, point) => sum + point[0], 0) / geometry.ditchPoints.length;
|
||||
const boundaryZ = groundAt(midOffset) - Math.abs(rockBoundaryOffsetM);
|
||||
[ditchSoilArea, ditchRockArea] = splitDitchArea(ditchSpec, ditchTopZ - boundaryZ);
|
||||
ditchSplitBasis = "rock_boundary";
|
||||
}
|
||||
|
||||
// 자동 판정된 절/성토 역할에서 실제 단면 유형을 도출해 echo 한다(D-2).
|
||||
let resolvedMode: string;
|
||||
if (geometry.leftRole === "cut" && geometry.rightRole === "cut") resolvedMode = "both_cut";
|
||||
@@ -457,6 +497,9 @@ export function computeCrossDesign(
|
||||
slope_unclosed: slopeUnclosed,
|
||||
fill_ground_slope: fillGroundSlope === null ? null : round4(fillGroundSlope),
|
||||
ditch_area_m2: round4(ditchArea),
|
||||
ditch_soil_area_m2: round4(ditchSoilArea),
|
||||
ditch_rock_area_m2: round4(ditchRockArea),
|
||||
ditch_split_basis: ditchSplitBasis,
|
||||
design_line: designLine,
|
||||
cut_slope_segments: geometry.cutSlopeSegments(),
|
||||
};
|
||||
|
||||
@@ -126,3 +126,36 @@ export function benchCutLength(offsets: number[], grounds: number[], diffs: numb
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 측구 단면적을 [토사, 암반]으로 가른다 — 측구 상단에서 암반 경계선까지의 깊이(m) 기준.
|
||||
* ⚠ 파이썬 짝: `B06_Section_Engine_Areas._split_ditch_area`. 한 벌로 움직인다.
|
||||
* 측구 단면은 공칭 도형이라 지반선을 따라 적분하지 않는다 — 경계선도 그 자리 한 높이로 본다.
|
||||
*/
|
||||
export function splitDitchArea(
|
||||
ditchSpec: Record<string, unknown>,
|
||||
depthToBoundaryM: number | null,
|
||||
): [number, number] {
|
||||
const kind = String(ditchSpec.type ?? "none");
|
||||
const clamp = (depth: number): number => Math.min(Math.max(depthToBoundaryM ?? 0, 0), depth);
|
||||
if (kind === "l_type") {
|
||||
const width = Number(ditchSpec.width_m ?? 0);
|
||||
const depth = Number(ditchSpec.depth_m ?? 0);
|
||||
if (depth <= 0 || width <= 0) return [0, 0];
|
||||
const total = (width * depth) / 2;
|
||||
const d0 = clamp(depth);
|
||||
const soil = width * d0 - (width * d0 * d0) / (2 * depth);
|
||||
return [soil, Math.max(total - soil, 0)];
|
||||
}
|
||||
if (kind === "standard") {
|
||||
const top = Number(ditchSpec.top_width_m ?? 0);
|
||||
const bottom = Math.min(Number(ditchSpec.bottom_width_m ?? 0), top);
|
||||
const depth = Number(ditchSpec.depth_m ?? 0);
|
||||
if (depth <= 0 || top <= 0) return [0, 0];
|
||||
const total = ((top + bottom) / 2) * depth;
|
||||
const d0 = clamp(depth);
|
||||
const soil = top * d0 - ((top - bottom) * d0 * d0) / (2 * depth);
|
||||
return [soil, Math.max(total - soil, 0)];
|
||||
}
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user