⚠ **뿌리** — `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>
86 lines
3.2 KiB
JavaScript
86 lines
3.2 KiB
JavaScript
/* B05 패치 스커트 단위검증 헬퍼 — TS를 그 자리에서 트랜스파일해 Node로 돌린다.
|
|
* (프론트에 JS 테스트 러너가 없어 pytest가 이 스크립트를 호출한다 — 2026-08-27) */
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
|
|
const source = fs.readFileSync(
|
|
path.join(__dirname, "..", "..", "B05_Profile", "B05_Profile_UI_Corridor_Skirt.ts"),
|
|
"utf8",
|
|
);
|
|
const js = ts.transpileModule(source, {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
const moduleBox = { exports: {} };
|
|
new Function("exports", "module", "require", js)(moduleBox.exports, moduleBox, require);
|
|
const { buildPatchSkirts } = moduleBox.exports;
|
|
|
|
/** 직선 노선 패치 리본 하나 — 바깥 열 z를 행마다 지정해 만든다. */
|
|
function ribbonOf(outerZ, options = {}) {
|
|
const cols = 3;
|
|
const rows = outerZ.length;
|
|
const positions = new Float64Array(rows * cols * 3);
|
|
for (let row = 0; row < rows; row += 1) {
|
|
for (let col = 0; col < cols; col += 1) {
|
|
const i = (row * cols + col) * 3;
|
|
positions[i] = row; // x = 종방향 1m 간격
|
|
positions[i + 1] = col; // y = 횡방향(마지막 열이 바깥)
|
|
positions[i + 2] = col === cols - 1 ? outerZ[row] : 100;
|
|
}
|
|
}
|
|
return {
|
|
kind: "fill",
|
|
side: "left",
|
|
colCount: cols,
|
|
chainages: outerZ.map((_z, row) => row),
|
|
positions,
|
|
patch: true,
|
|
...options,
|
|
};
|
|
}
|
|
|
|
const results = {};
|
|
|
|
// ① 뜬 모서리 — 바깥 끝이 지반(90)보다 0.5m 위: 마디마다 판 2장씩.
|
|
{
|
|
const ribbon = ribbonOf([90.5, 90.5, 90.5]);
|
|
const { walls, stats } = buildPatchSkirts([ribbon], () => 90);
|
|
results.floating = {
|
|
walls: walls.length,
|
|
triangles: stats.triangles,
|
|
hatched: walls.every((w) => w.uvs && w.uvs.length === (w.positions.length / 3) * 2),
|
|
marked: walls.every((w) => w.patchSkirt === true),
|
|
};
|
|
}
|
|
|
|
// ② 잠긴 모서리 — 바깥 끝이 지반 아래: 판 없음(지형이 덮는다).
|
|
{
|
|
const ribbon = ribbonOf([89.5, 89.5, 89.5]);
|
|
const { walls, stats } = buildPatchSkirts([ribbon], () => 90);
|
|
results.submerged = { walls: walls.length, skipped: stats.skippedSubmerged };
|
|
}
|
|
|
|
// ③ 구조물에서 끝나는 줄 — 소유 측점(가운데 행) 낙차 2m > 허용 0.5m: 리본 전체 제외.
|
|
{
|
|
const ribbon = ribbonOf([92, 92, 92]);
|
|
const { walls, stats } = buildPatchSkirts([ribbon], () => 90);
|
|
results.toeGate = { walls: walls.length, reaches: stats.detail[0].reachesGround };
|
|
}
|
|
|
|
// ④ 낙차 상한 — 게이트는 통과(가운데 0.2m)하되 한 마디가 3m 넘게 뜨면 그 마디만 건너뜀.
|
|
{
|
|
const ribbon = ribbonOf([90.2, 94, 90.2, 90.2]);
|
|
const { walls, stats } = buildPatchSkirts([ribbon], () => 90);
|
|
results.tall = { walls: walls.length, skippedTall: stats.skippedTall, tris: stats.triangles };
|
|
}
|
|
|
|
// ⑤ 비패치·절토 리본은 무시.
|
|
{
|
|
const plain = ribbonOf([90.5, 90.5], { patch: undefined });
|
|
const cut = ribbonOf([90.5, 90.5], { kind: "cut" });
|
|
const { walls } = buildPatchSkirts([plain, cut], () => 90);
|
|
results.ignored = { walls: walls.length };
|
|
}
|
|
|
|
process.stdout.write(JSON.stringify(results));
|