Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1
This commit is contained in:
@@ -20,9 +20,12 @@
|
||||
그래서 지식DB 에도 적지 않는다(사용자 지시).
|
||||
"""
|
||||
|
||||
import logging
|
||||
import math
|
||||
from typing import Callable, NamedTuple
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 소단 기본값 — 근거는 위 모듈 설명.
|
||||
BERM_DEFAULT_WIDTH_M = 0.5
|
||||
BERM_DEFAULT_INTERVAL_M = 3.0
|
||||
@@ -31,6 +34,10 @@ BERM_DEFAULT_SLOPE_DEG = 0.0
|
||||
# 사면을 따라 걸어가는 보폭(m)과 최대 거리 — 무릎 탐색이 쓰던 값과 같다.
|
||||
_STEP_M = 0.05
|
||||
_MAX_REACH_M = 200.0
|
||||
# 걸음 수 상한 — 정상 경로의 최대는 200/0.05 = 4,000 이다. 소단은 걸음 없이 거리를 더하므로
|
||||
# 여유를 크게 두고 **10배**로 잡는다. 넘으면 조용히 자르지 않고 경고를 남긴다 — 조용히
|
||||
# 자르면 절토선이 짧아진 채 값이 나가 또 조용히 틀린다(2026-09-07 25 지적).
|
||||
_MAX_STEPS = int(_MAX_REACH_M / _STEP_M) * 10
|
||||
|
||||
|
||||
class BermSpec(NamedTuple):
|
||||
@@ -87,7 +94,24 @@ def cut_profile_points(
|
||||
in_soil = rock_boundary_z is None or elevation >= rock_boundary_z(dist)
|
||||
ratio = soil_cut_ratio if (rock_boundary_z is not None and in_soil) else cut_ratio
|
||||
|
||||
# ⚠ 제자리 무릎을 막는 자리 — 경계선 기울기가 **암 경사와 토사 경사 사이**면 「토사로
|
||||
# 바꾸면 경계 아래, 암으로 바꾸면 경계 위」가 되어 같은 자리에서 영원히 뒤집힌다
|
||||
# (보간 비율 `share` 가 0 이라 한 걸음도 안 나간다). 그러면 화면이 통째로 멈춘다
|
||||
# (2026-09-07 실사고 — 소단 한 건을 놓자 브라우저가 25분간 안 끝남). 직전 무릎 자리를
|
||||
# 들고 있다가 **같은 자리면 뒤집지 않고 한 걸음 나아간다**.
|
||||
last_knee_dist = float("-inf")
|
||||
steps = 0
|
||||
while dist < limit:
|
||||
steps += 1
|
||||
if steps > _MAX_STEPS:
|
||||
logger.warning(
|
||||
"절토 사면 걸음이 상한(%d)을 넘어 멈춥니다 — 거리 %.3fm, 경사비 %.3f. "
|
||||
"제자리 무릎이 남아 있을 수 있습니다.",
|
||||
_MAX_STEPS,
|
||||
dist,
|
||||
ratio,
|
||||
)
|
||||
break
|
||||
rise = _STEP_M / ratio
|
||||
slant = math.hypot(_STEP_M, rise)
|
||||
|
||||
@@ -119,12 +143,15 @@ def cut_profile_points(
|
||||
share = min(max(share, 0.0), 1.0)
|
||||
knee_dist = dist + _STEP_M * share
|
||||
knee_z = elevation + rise * share
|
||||
slant_since_berm += math.hypot(knee_dist - dist, knee_z - elevation)
|
||||
dist, elevation = knee_dist, knee_z
|
||||
points.append((dist, elevation)) # 무릎
|
||||
in_soil = not in_soil
|
||||
ratio = soil_cut_ratio if in_soil else cut_ratio
|
||||
continue
|
||||
# 앞으로 나아가는 무릎만 인정한다(위 ⚠ 참조). 같은 자리면 그냥 한 걸음 간다.
|
||||
if knee_dist > last_knee_dist + 1e-9:
|
||||
slant_since_berm += math.hypot(knee_dist - dist, knee_z - elevation)
|
||||
dist, elevation = knee_dist, knee_z
|
||||
points.append((dist, elevation)) # 무릎
|
||||
in_soil = not in_soil
|
||||
ratio = soil_cut_ratio if in_soil else cut_ratio
|
||||
last_knee_dist = knee_dist
|
||||
continue
|
||||
|
||||
dist, elevation = next_dist, next_z
|
||||
slant_since_berm += slant
|
||||
|
||||
@@ -25,6 +25,10 @@ export const BERM_DEFAULT_SLOPE_DEG = 0.0;
|
||||
/** 사면을 따라 걸어가는 보폭(m)과 최대 거리 — 파이썬 짝과 같은 값. */
|
||||
const STEP_M = 0.05;
|
||||
const MAX_REACH_M = 200.0;
|
||||
/** 걸음 수 상한 — 정상 경로의 최대는 200/0.05 = 4,000. 소단은 걸음 없이 거리를 더하므로
|
||||
* 여유를 크게 두고 10배로 잡는다. 넘으면 조용히 자르지 않고 콘솔에 알린다.
|
||||
* 짝: 파이썬 `_MAX_STEPS`. */
|
||||
const MAX_STEPS = (MAX_REACH_M / STEP_M) * 10;
|
||||
|
||||
/** 소단 제원 — 폭(m) · 간격(사면길이 m) · 안쪽 기울기(도). */
|
||||
export interface BermSpec {
|
||||
@@ -71,7 +75,21 @@ export function cutProfilePoints(
|
||||
let inSoil = rockBoundaryZ === null || elevation >= rockBoundaryZ(dist);
|
||||
let ratio = rockBoundaryZ !== null && inSoil ? soilCutRatio : cutRatio;
|
||||
|
||||
// ⚠ 제자리 무릎을 막는 자리 — 경계선 기울기가 **암 경사와 토사 경사 사이**면 「토사로
|
||||
// 바꾸면 경계 아래, 암으로 바꾸면 경계 위」가 되어 같은 자리에서 영원히 뒤집힌다(보간 비율
|
||||
// `share` 가 0 이라 한 걸음도 안 나간다). 그러면 화면이 통째로 멈춘다(2026-09-07 실사고 —
|
||||
// 소단 한 건을 놓자 브라우저가 25분간 안 끝남). 직전 무릎 자리를 들고 있다가 **같은 자리면
|
||||
// 뒤집지 않고 한 걸음 나아간다**. 짝: 파이썬 `cut_profile_points`.
|
||||
let lastKneeDist = Number.NEGATIVE_INFINITY;
|
||||
let steps = 0;
|
||||
while (dist < limit) {
|
||||
steps += 1;
|
||||
if (steps > MAX_STEPS) {
|
||||
console.warn(
|
||||
`절토 사면 걸음이 상한(${MAX_STEPS})을 넘어 멈춥니다 — 거리 ${dist.toFixed(3)}m, 경사비 ${ratio}.`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
const rise = STEP_M / ratio;
|
||||
const slant = Math.hypot(STEP_M, rise);
|
||||
|
||||
@@ -104,13 +122,17 @@ export function cutProfilePoints(
|
||||
share = Math.min(Math.max(share, 0), 1);
|
||||
const kneeDist = dist + STEP_M * share;
|
||||
const kneeZ = elevation + rise * share;
|
||||
slantSinceBerm += Math.hypot(kneeDist - dist, kneeZ - elevation);
|
||||
dist = kneeDist;
|
||||
elevation = kneeZ;
|
||||
points.push([dist, elevation]); // 무릎
|
||||
inSoil = !inSoil;
|
||||
ratio = inSoil ? soilCutRatio : cutRatio;
|
||||
continue;
|
||||
// 앞으로 나아가는 무릎만 인정한다(위 ⚠ 참조). 같은 자리면 그냥 한 걸음 간다.
|
||||
if (kneeDist > lastKneeDist + 1e-9) {
|
||||
slantSinceBerm += Math.hypot(kneeDist - dist, kneeZ - elevation);
|
||||
dist = kneeDist;
|
||||
elevation = kneeZ;
|
||||
points.push([dist, elevation]); // 무릎
|
||||
inSoil = !inSoil;
|
||||
ratio = inSoil ? soilCutRatio : cutRatio;
|
||||
lastKneeDist = kneeDist;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user