Merge remote-tracking branches 'origin/main_laptop_1' and 'origin/sub_laptop_7' into sub_laptop_8
This commit is contained in:
@@ -501,6 +501,8 @@ export interface CrossDesign {
|
||||
/** 물넘이포장 · 맹암거 자리(286-2 · 287-4) — 측구 없음 · 노면 = 지대 낮은 쪽 한 경사(`tilt_pct`). */
|
||||
tilted?: boolean;
|
||||
tilt_pct?: number;
|
||||
/** 물넘이 낮은 쪽 늘인 노면 끝(294-3) — 그 쪽 절토 사면 대신 노면 · 콘크리트가 원지반까지. */
|
||||
tilt_reach?: { side: "left" | "right"; offset_m: number; elevation_m: number };
|
||||
cut_area_m2: number;
|
||||
/**
|
||||
* 절토 내역(합 = `cut_area_m2`). 지표면~암반 경계선이 토사, 그 아래가 암이다.
|
||||
|
||||
@@ -314,6 +314,7 @@ def compute_cross_design(
|
||||
shoulder_extend_left_m=shoulder_extend_left_m,
|
||||
shoulder_extend_right_m=shoulder_extend_right_m,
|
||||
tilted=bool(tilted),
|
||||
ground_span=(valid[0][0], valid[-1][0]),
|
||||
)
|
||||
|
||||
# 적분 오프셋 = 지반 샘플 ∪ 설계 꼭짓점(샘플 범위 안쪽만). 꼭짓점을 넣어야
|
||||
@@ -546,6 +547,13 @@ def compute_cross_design(
|
||||
}
|
||||
if tilted:
|
||||
result["tilted"] = True
|
||||
for side, reach in geometry.reach.items(): # 낮은 쪽 늘인 노면 끝(294-3)
|
||||
signed = reach if side == "left" else -reach
|
||||
result["tilt_reach"] = {
|
||||
"side": side,
|
||||
"offset_m": round(signed, 4),
|
||||
"elevation_m": round(geometry.road_z(signed), 4),
|
||||
}
|
||||
if tilt_pct is not None:
|
||||
result["tilt_pct"] = round(float(tilt_pct), 4)
|
||||
if drop > 0:
|
||||
|
||||
@@ -63,6 +63,7 @@ class _SectionGeometry:
|
||||
shoulder_extend_left_m: float = 0.0,
|
||||
shoulder_extend_right_m: float = 0.0,
|
||||
tilted: bool = False,
|
||||
ground_span: tuple[float, float] | None = None,
|
||||
) -> None:
|
||||
half_road = group["road_width_m"] / 2.0
|
||||
# 곡선부 확폭은 **한쪽으로만** 붙는다(2026-09-06 사용자 확정: 곡선 바깥쪽).
|
||||
@@ -195,6 +196,36 @@ class _SectionGeometry:
|
||||
(edge_offset + outward * top, edge_z),
|
||||
]
|
||||
|
||||
# 물넘이 낮은 쪽 연장(294-3) — 노면이 내려가는 쪽 노면 끝 원지반이 노면보다 높으면(절토)
|
||||
# 노면을 같은 기울기로 늘여 원지반과 처음 만나는 자리까지(그 쪽 절토 사면 없음) · 반폭
|
||||
# (`ground_span`) 안에서 못 만나면 옛 길(절토 사면). ⚠ TS 짝 `SectionGeometry.reach`.
|
||||
self.reach: dict[str, float] = {}
|
||||
if tilted and ground_at is not None and abs(self.slope_per_offset) > 1e-12:
|
||||
self._extend_low_side(ground_at, ground_span)
|
||||
|
||||
def _extend_low_side(
|
||||
self, ground_at: Callable[[float], float], span: tuple[float, float] | None
|
||||
) -> None:
|
||||
side = "left" if self.slope_per_offset < 0 else "right"
|
||||
sign = 1.0 if side == "left" else -1.0
|
||||
extent = self.left_extent if side == "left" else self.right_extent
|
||||
limit = (span[1] if side == "left" else -span[0]) if span else extent + 50.0
|
||||
|
||||
def gap(dist: float) -> float: # > 0 = 원지반이 늘인 노면 위(절토)
|
||||
return ground_at(sign * dist) - self.road_z(sign * dist)
|
||||
|
||||
if gap(extent) <= 1e-3:
|
||||
return
|
||||
step = 0.05
|
||||
dist = extent
|
||||
while dist < limit - 1e-9:
|
||||
nxt = min(dist + step, limit)
|
||||
g0, g1 = gap(dist), gap(nxt)
|
||||
if g1 <= 0:
|
||||
self.reach[side] = dist + (nxt - dist) * g0 / (g0 - g1)
|
||||
return
|
||||
dist = nxt
|
||||
|
||||
def road_z(self, offset_m: float) -> float:
|
||||
"""노면(노견 포함) 설계고 — 중심 계획고에서 횡단경사로 기운 단일 평면."""
|
||||
return self.z_center + self.slope_per_offset * offset_m
|
||||
@@ -430,6 +461,9 @@ class _SectionGeometry:
|
||||
return points[-1][1]
|
||||
role = self.left_role if side == "left" else self.right_role
|
||||
dist = abs(offset_m)
|
||||
reach = self.reach.get(side)
|
||||
if reach is not None: # 늘인 노면 → 그 바깥은 원지반(294-3)
|
||||
return self.road_z(offset_m) if dist <= reach + 1e-9 else ground_m
|
||||
if role == "cut":
|
||||
# 지반과 1회 교차하면 그 이후 절토는 의미 없음 → 지반 추종(N-2-4).
|
||||
cross = self.cut_cross_dist(side)
|
||||
@@ -468,6 +502,9 @@ class _SectionGeometry:
|
||||
if cross is not None and offset > cross + 1e-9:
|
||||
break
|
||||
points.append(offset if side == "left" else -offset)
|
||||
# 늘인 노면 끝(294-3)
|
||||
for side, reach in self.reach.items():
|
||||
points.append(reach if side == "left" else -reach)
|
||||
# 절·성토 사면과 지반의 **첫** 교차점을 꼭짓점에 넣어 면적 절단을 정확히 한다(N-2-4).
|
||||
for side in ("left", "right"):
|
||||
role = self.left_role if side == "left" else self.right_role
|
||||
|
||||
@@ -103,7 +103,7 @@ export function appendFordPavementOverlay(
|
||||
x: (offset: number) => number,
|
||||
y: (elevation: number) => number,
|
||||
): boolean {
|
||||
const edges = design.road_edges;
|
||||
const edges = fordEdges(design);
|
||||
if (!spec || !edges) return false;
|
||||
const from = svg.children.length;
|
||||
// 노면공 항목은 안 넘김 — 물넘이 자체 포장(노면공 포장 고름과 안 섞임)
|
||||
@@ -134,6 +134,15 @@ export function appendFordPavementOverlay(
|
||||
return true;
|
||||
}
|
||||
|
||||
/** 콘크리트 · 노면 양 끝 — 낮은 쪽을 늘였으면(294-3 `tilt_reach`) 그 쪽은 원지반과 만난 자리까지. */
|
||||
export function fordEdges(design: CrossDesign): CrossDesign["road_edges"] | undefined {
|
||||
const edges = design.road_edges;
|
||||
const reach = design.tilt_reach;
|
||||
if (!edges || !reach) return edges;
|
||||
const end = { offset_m: reach.offset_m, elevation_m: reach.elevation_m };
|
||||
return reach.side === "left" ? { ...edges, left: end } : { ...edges, right: end };
|
||||
}
|
||||
|
||||
/**
|
||||
* 유출측(`inflowLeft` 반대) 노견 바깥 성토사면 선(136-2) — 설계선을 노견에서 바깥으로 따라
|
||||
* 내려가는 동안만(오르면 절토라 멈춤). 성토가 없으면 빈 선(띠 안 그림).
|
||||
|
||||
Reference in New Issue
Block a user