Files
Aislo/resources/tester/test_cad_rect_harvest.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

69 lines
2.8 KiB
JavaScript

// 수확기 rect 변환(B07_DesignDetail_UI_Cad_Structures.rectPoints) 자체검증 — 로직 복제.
// 돌쌓기 돌·돌망태 칸은 rect + rotate(벽기울기) 로 그린다. 회전을 빠뜨리면 돌이 벽을
// 삐져나가 클립에 전부 잘려 나간다(2026-09-03 실측: rect 49개가 CAD 에 안 실렸다).
// 실행: node tmp/tests/test_cad_rect_harvest.mjs
import assert from "node:assert/strict";
const flip = (x, y) => [x, -y];
function rectPoints(attrs, transform) {
const { x, y, width, height } = attrs;
const corners = [
[x, y],
[x + width, y],
[x + width, y + height],
[x, y + height],
[x, y],
];
const rotate = /rotate\(\s*(-?[\d.]+)[\s,]+(-?[\d.]+)[\s,]+(-?[\d.]+)\s*\)/.exec(transform ?? "");
if (!rotate) return corners.map(([cx, cy]) => flip(cx, cy));
const angle = (Number(rotate[1]) * Math.PI) / 180;
const [ox, oy] = [Number(rotate[2]), Number(rotate[3])];
const cos = Math.cos(angle);
const sin = Math.sin(angle);
return corners.map(([cx, cy]) => {
const dx = cx - ox;
const dy = cy - oy;
return flip(ox + dx * cos - dy * sin, oy + dx * sin + dy * cos);
});
}
const near = (a, b) => Math.abs(a - b) < 1e-9;
// ① 회전이 없으면 네 모서리를 y만 뒤집어 닫힌 점열로 낸다.
{
const points = rectPoints({ x: 10, y: 20, width: 4, height: 6 }, null);
assert.equal(points.length, 5);
assert.deepEqual(points[0], [10, -20]);
assert.deepEqual(points[2], [14, -26]);
assert.deepEqual(points[0], points[4], "닫힌 점열이어야 한다");
}
// ② rotate(90 cx cy) — 중심 기준 90° 회전. 폭·높이가 맞바뀐다.
{
const points = rectPoints({ x: -1, y: -2, width: 2, height: 4 }, "rotate(90 0 0)");
const xs = points.map((p) => p[0]);
const ys = points.map((p) => p[1]);
assert.ok(near(Math.max(...xs) - Math.min(...xs), 4), "가로가 원래 세로가 된다");
assert.ok(near(Math.max(...ys) - Math.min(...ys), 2), "세로가 원래 가로가 된다");
}
// ③ 회전 중심은 제자리에 남는다 — 벽 기울기만큼 기울여도 돌 중심이 안 움직인다.
{
const attrs = { x: 8, y: 12, width: 4, height: 2 };
const center = [attrs.x + attrs.width / 2, attrs.y + attrs.height / 2];
const points = rectPoints(attrs, `rotate(-16.7 ${center[0]} ${center[1]})`);
const xs = points.map((p) => p[0]);
const ys = points.map((p) => p[1]);
assert.ok(near((Math.min(...xs) + Math.max(...xs)) / 2, center[0]));
assert.ok(near((Math.min(...ys) + Math.max(...ys)) / 2, -center[1]));
}
// ④ 소수·쉼표 구분자도 읽는다 — SVG가 `rotate(-16.7,120.5,88.25)` 로 쓰기도 한다.
{
const points = rectPoints({ x: 0, y: 0, width: 2, height: 2 }, "rotate(-16.7,1,1)");
assert.ok(!near(points[0][0], 0), "회전이 실제로 적용돼야 한다");
}
console.log("OK test_cad_rect_harvest.mjs — 4건");