Files
Aislo/resources/tester/test_toe_extrapolate.mjs
T
eomsangdonandClaude Opus 5 0ef32b5279 chore(tester): 시험을 resources/tester/ 로 옮김 — 창끼리 건너가게
⚠ **뿌리** — `tmp/` 는 창 사이에 안 건너감(실측 확인: 상대 창이 놓은 `tmp/_sync_probe.txt`
가 시간을 두고 두 번 봐도 안 보임). 그래서 **정본(등록부 스키마)만 건너가고 그것을 읽는
시험은 안 건너가** 오늘 두 번, 같은 시험이 **연 창은 통과·받은 창은 실패**가 됐음.

- `tmp/tests/*` 를 `resources/tester/` 로 **복사**(127 파일). 내용은 **한 줄도 안 고침**
- `tmp/tests` 는 **남겨 둠** — 되돌릴 자리(사용자 지시)
- 실행: `./venv/Scripts/python.exe -m pytest resources/tester/ -q`
  옮기기 전과 **같은 수**: 617 통과 / 22 건너뜀 / 실패 0

⇒ 이제 시험·예외·까닭이 **정본과 함께** 움직임. 오늘 세운 「예외는 정본 스키마에」와 짝임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 17:12:30 +09:00

64 lines
3.3 KiB
JavaScript

// 교차점 탐색·외삽(B06_Section_UI_Cross_Fit) 자체검증 — 로직 복제.
// 사면이 5m를 넘어도 실제 지반 교차점을 짚어야 한다(2026-08-23 사용자 보고).
// 실행: node tmp/tests/test_toe_extrapolate.mjs
import assert from "node:assert/strict";
const TREND_SPAN_M = 2;
const MAX = 60;
function extrapolateMeet(designAt, groundAt, limit, startOffset) {
const outward = Math.sign(limit - startOffset) || 1;
const back = limit - outward * TREND_SPAN_M;
const diffAtLimit = designAt(limit) - groundAt(limit);
const diffAtBack = designAt(back) - groundAt(back);
const closingRate = (Math.abs(diffAtBack) - Math.abs(diffAtLimit)) / TREND_SPAN_M;
if (!(closingRate > 1e-6)) return limit;
const extra = Math.abs(diffAtLimit) / closingRate;
if (!Number.isFinite(extra)) return limit;
return limit + outward * Math.min(extra, MAX);
}
// 성토 사면 1:1.5(바깥으로 갈수록 0.667m/m 하강), 지반 평탄 → 실제 교차점 = 노견+15m.
const edge = 3;
const designAt = (o) => 10 - ((o - edge) * 2) / 3;
const groundAt = () => 0;
// 샘플 끝 15m에서 아직 안 만남(차이 2m) → 외삽이 실제 교차점 18m를 짚어야 한다.
const meet = extrapolateMeet(designAt, groundAt, 15, edge);
assert.ok(Math.abs(meet - 18) < 1e-6, `외삽 어긋남: ${meet}`);
// 반대쪽(음의 offset)도 부호가 맞아야 한다.
const designLeft = (o) => 10 - ((-edge - o) * 2) / 3;
const meetLeft = extrapolateMeet(designLeft, groundAt, -15, -edge);
assert.ok(Math.abs(meetLeft + 18) < 1e-6, `좌측 외삽 어긋남: ${meetLeft}`);
// 벌어지기만 하는 지형(지반이 사면보다 더 빨리 내려감)은 넓히지 않는다.
const divergingGround = (o) => -Math.abs(o) * 2;
assert.equal(extrapolateMeet(designAt, divergingGround, 15, edge), 15);
// 표시 반폭 = 올림(교차점) + 1m — 18m 교차점이면 19m.
assert.equal(Math.min(Math.ceil(Math.abs(meet) + 1), MAX), 19);
console.log("ok");
// ── 실제 교차점 탐색(meetOffset) — 성토 사면 5m 규칙에 잘리면 안 된다.
const SCAN_STEP_M = 0.1;
const MEET_TOLERANCE_M = 0.02;
function meetOffset(designAt, groundAt, startOffset, limitOffset, outward) {
const span = Math.abs(limitOffset - startOffset);
if (!(span > 0)) return startOffset;
const steps = Math.ceil(span / SCAN_STEP_M);
let previousDiff = designAt(startOffset) - groundAt(startOffset);
for (let i = 1; i <= steps; i += 1) {
const o = startOffset + outward * Math.min(SCAN_STEP_M * i, span);
const diff = designAt(o) - groundAt(o);
if (Math.abs(diff) <= MEET_TOLERANCE_M) return o;
if (previousDiff !== 0 && Math.sign(diff) !== Math.sign(previousDiff)) return o;
previousDiff = diff;
}
return extrapolateMeet(designAt, groundAt, limitOffset, outward);
}
// 사면 길이 약 18m(1:1.5, 높이 10m) — 5m 컷오프면 노견+약 4.2m에서 멈춘다.
const meetFar = meetOffset(designAt, groundAt, edge, 25, 1);
assert.ok(Math.abs(meetFar - 18) < 0.15, `교차점 탐색 어긋남: ${meetFar}`);
assert.ok(meetFar > edge + 5, "5m 사면 규칙에 잘렸다");
// 설계선이 교차 전에 끝나면(15m까지만) 외삽으로 같은 자리를 짚는다.
const meetClipped = meetOffset(designAt, groundAt, edge, 15, 1);
assert.ok(Math.abs(meetClipped - 18) < 1e-6, `외삽 어긋남: ${meetClipped}`);
console.log("ok(fit)");