This commit is contained in:
2026-07-25 13:56:46 +09:00
parent 3ba6c6a4c2
commit 6bbed32f35
7 changed files with 223 additions and 73 deletions
@@ -195,6 +195,8 @@ class _SectionGeometry:
self._ground_at = ground_at
self._rock_offset = rock_boundary_offset_m or 0.0
self._rock_knee: dict[str, tuple[float, float] | None] = {}
# 절토 사면·지반 최초 교차거리(측별 캐시) — 교차 후 절토 종료용(N-2-4).
self._cut_cross: dict[str, float | None] = {}
self.ditch_type = ditch_type
# 횡단경사: 측구 방향으로 내려가는 단일 사면 (좌=+offset 규약).
slope = cross_slope_pct / 100.0
@@ -311,6 +313,41 @@ class _SectionGeometry:
self._rock_knee[side] = result
return result
def _cut_slope_z(self, side: str, dist: float) -> float:
"""절토 사면선 표고(2단계 무릎 반영). 지반 교차 클램프는 하지 않는다."""
start_dist, start_z = self._slope_start(side)
knee = self.knee(side) if self.two_stage else None
if knee is not None:
knee_dist, knee_z = knee
if dist <= knee_dist: # 암반 구간(경계 아래): 암 경사
return start_z + (dist - start_dist) / self.cut_ratio
return knee_z + (dist - knee_dist) / self.soil_cut_ratio # 토사 구간: 완만
return start_z + (dist - start_dist) / self.cut_ratio
def cut_cross_dist(self, side: str) -> float | None:
"""절토 사면이 지반선과 처음 만나는 거리(절대 오프셋). 이후는 절토 없음(N-2-4).
지면과 1회 교차하면 그다음 경사(2단계 전환 포함)는 의미가 없으므로 교차점에서
절토를 종료한다. 시작(노면 끝)부터 사면이 지반 위면 교차거리=시작(절토 없음),
끝까지 못 만나면 None.
"""
if side in self._cut_cross:
return self._cut_cross[side]
result: float | None = None
if self._ground_at is not None:
start_dist, _start_z = self._slope_start(side)
step = 0.05
dist = start_dist
max_dist = start_dist + 500.0
while dist <= max_dist:
signed = dist if side == "left" else -dist
if self._cut_slope_z(side, dist) - self._ground_at(signed) >= 0:
result = dist
break
dist += step
self._cut_cross[side] = result
return result
def design_z(self, offset_m: float, ground_m: float) -> float:
"""offset 하나의 설계 표고(사면은 지반 교차점 이후 지반 추종)."""
side = "left" if offset_m >= 0 else "right"
@@ -339,16 +376,11 @@ class _SectionGeometry:
dist = abs(offset_m)
run = dist - start_dist
if role == "cut":
knee = self.knee(side) if self.two_stage else None
if knee is not None:
knee_dist, knee_z = knee
if dist <= knee_dist: # 암반 구간(경계 아래): 암 경사
slope_line = start_z + (dist - start_dist) / self.cut_ratio
else: # 토사 구간(경계 위): 무릎에서 토사 경사로 완만
slope_line = knee_z + (dist - knee_dist) / self.soil_cut_ratio
else:
slope_line = start_z + run / self.cut_ratio
return min(slope_line, ground_m)
# 지반과 1회 교차하면 그 이후 절토는 의미 없음 → 지반 추종(N-2-4).
cross = self.cut_cross_dist(side)
if cross is not None and dist >= cross:
return ground_m
return min(self._cut_slope_z(side, dist), ground_m)
fill_line = start_z - run / self.fill_ratio
return max(fill_line, ground_m)
@@ -362,6 +394,13 @@ class _SectionGeometry:
knee = self.knee(side) if role == "cut" else None
if knee is not None:
points.append(knee[0] if side == "left" else -knee[0])
# 절토 사면·지반 교차점을 꼭짓점에 넣어 면적 절단을 정확히 한다(N-2-4).
for side in ("left", "right"):
role = self.left_role if side == "left" else self.right_role
if role == "cut":
cross = self.cut_cross_dist(side)
if cross is not None:
points.append(cross if side == "left" else -cross)
return points