diff --git a/B06_Section/logic/B06_Section_UI_Cross_Fit.ts b/B06_Section/logic/B06_Section_UI_Cross_Fit.ts index d30b011af..5560101a3 100644 --- a/B06_Section/logic/B06_Section_UI_Cross_Fit.ts +++ b/B06_Section/logic/B06_Section_UI_Cross_Fit.ts @@ -72,6 +72,8 @@ export function toePoints( /** 멈춤 탐색 간격(m) — 사면 이동 걸음(0.1 m)보다 촘촘하게. */ const STOP_SCAN_M = 0.01; +/** 노견 끝 = 원지반 잣대(m) — 멈춤 자리(`extendStop`) · 「절·성토 없음」(`edgeOnGround`) 한 벌(370-4). */ +const ON_GROUND_M = 0.005; /** * 사면 이동 멈춤(370-2) — 그 쪽 노견 확장 `now` → `next` 사이에서 옮긴 노견 끝이 원지반과 딱 만나는 @@ -99,7 +101,8 @@ export function extendStop( return groundAt(o) - plane(o); }; let previous = gapAt(now); - if (Math.abs(previous) < 1e-4) return next; + // 이미 멈춤 자리(0.001 m 반올림이 교차 앞에 떨어져도) — 같은 교차를 다시 찾지 않고 넘어감(370-4) + if (Math.abs(previous) <= ON_GROUND_M) return next; const steps = Math.max(1, Math.ceil(Math.abs(next - now) / STOP_SCAN_M)); for (let i = 1; i <= steps; i += 1) { const a = now + ((next - now) * (i - 1)) / steps; @@ -121,7 +124,7 @@ export function edgeOnGround( const on = (side: "left" | "right") => !!groundAt && !!edges && - Math.abs(groundAt(edges[side].offset_m) - edges[side].elevation_m) <= 0.005; + Math.abs(groundAt(edges[side].offset_m) - edges[side].elevation_m) <= ON_GROUND_M; return { left: on("left"), right: on("right") }; } diff --git a/resources/tester/helper_370_2_toe_stop.cjs b/resources/tester/helper_370_2_toe_stop.cjs index c62046e96..2b121d995 100644 --- a/resources/tester/helper_370_2_toe_stop.cjs +++ b/resources/tester/helper_370_2_toe_stop.cjs @@ -225,6 +225,21 @@ out.stop = { ), leftNoGround: fit.extendStop([], base.road_edges, "left", 0, 0.1), }; +// 370-4 — 0.001 m 반올림이 교차 **앞**에 떨어진 멈춤(54+0 0.936 꼴) → 다음 걸음은 넘어감(같은 교차에 다시 안 섬) +{ + const [ga, gb] = crossing.map((e) => gapAt(base, e)); + const exact = crossing[0] + ((crossing[1] - crossing[0]) * ga) / (ga - gb); + let before = Math.floor(exact * 1000) / 1000; + if (before === exact) before -= 0.001; + const next = Math.round((before + 0.1) * 1000) / 1000; + out.before = { + exact, + before, + gapMm: Math.abs(gapAt(base, before)) * 1000, + next, + got: fit.extendStop(samples, designOf(before).road_edges, "right", before, next), + }; +} // ② 교차점 손잡이 — 쪽마다 원 · 늘 보임 · 키 toe:left|right · 고르면 ◀▶ 가 멈춤 자리로 보냄 const x = (o) => 300 - o * 20; diff --git a/resources/tester/test_370_2_toe_stop.py b/resources/tester/test_370_2_toe_stop.py index 42e9f4e94..fe501353e 100644 --- a/resources/tester/test_370_2_toe_stop.py +++ b/resources/tester/test_370_2_toe_stop.py @@ -52,6 +52,14 @@ def test_노견_끝이_원지반을_지나면_그_자리에_한_번_섬(out) -> assert stop["leftNoGround"] == 0.1 # 지반 없으면 그대로 +def test_반올림이_교차_앞이어도_다음_걸음은_넘어감(out) -> None: + """370-4 — 멈춤값(0.001 m 반올림)이 참 교차 앞이면 남은 틈 > 1e-4 · 그래도 ±5 mm 안 = 멈춤 자리.""" + got = out["before"] + assert got["before"] < got["exact"] # 교차 앞 + assert 0.1 < got["gapMm"] <= 5 # 옛 잣대(0.1 mm) 밖 · 새 잣대(5 mm) 안 + assert abs(got["got"] - got["next"]) < 1e-9 # 0.936 → 1.036 꼴(제자리 아님) + + def test_교차점_손잡이_쪽마다_늘_보임(out) -> None: toe = out["toe"] assert toe["count"] == len(toe["sides"]) == 2