Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYJJRCfHfdCGNhmXTjQKV7
85 lines
2.7 KiB
JavaScript
85 lines
2.7 KiB
JavaScript
/* 90-2 도수로 · 돌붙임 띠 헬퍼 — `appendChuteBand`(B06_Section_UI_Cross_Wall_Hatch.ts)를 그 자리에서
|
|
* 트랜스파일해 흉내 DOM 에 그림 · 결과 = 띠 꼭짓점(m) · 돌 수(표준출력 JSON). 짝 = `test_90_chute_band.py`.
|
|
*/
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
const cache = new Map();
|
|
function loadTs(file) {
|
|
const full = path.resolve(file);
|
|
if (cache.has(full)) return cache.get(full);
|
|
const js = ts.transpileModule(fs.readFileSync(full, "utf8"), {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
cache.set(full, box.exports);
|
|
const localRequire = (request) => loadTs(path.join(path.dirname(full), `${request}.ts`));
|
|
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
|
return box.exports;
|
|
}
|
|
|
|
class Node {
|
|
constructor(tag) {
|
|
this.tag = tag;
|
|
this.attrs = {};
|
|
this.children = [];
|
|
}
|
|
setAttribute(k, v) {
|
|
this.attrs[k] = String(v);
|
|
}
|
|
getAttribute(k) {
|
|
return this.attrs[k] ?? null;
|
|
}
|
|
append(...kids) {
|
|
this.children.push(...kids);
|
|
}
|
|
set textContent(v) {
|
|
this.text = v;
|
|
}
|
|
}
|
|
globalThis.document = { createElementNS: (_ns, tag) => new Node(tag) };
|
|
|
|
const { appendChuteBand } = loadTs(
|
|
path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Wall_Hatch.ts"),
|
|
);
|
|
const SCALE = 100;
|
|
const run = (line) => {
|
|
const layer = new Node("g");
|
|
appendChuteBand(
|
|
layer,
|
|
[line],
|
|
{ structure: "돌붙임", thickness_m: 0.3 },
|
|
(o) => o * SCALE,
|
|
(e) => -e * SCALE,
|
|
);
|
|
const band = layer.children.find((k) => k.tag === "polygon");
|
|
const group = layer.children.find((k) => k.tag === "g");
|
|
const points = band.attrs.points.split(" ").map((p) => {
|
|
const [px, py] = p.split(",").map(Number);
|
|
return { offset: px / SCALE, elevation: -py / SCALE };
|
|
});
|
|
return {
|
|
points,
|
|
stones: group.children.filter((k) => k.tag === "rect").length,
|
|
clipped: group.attrs["clip-path"]?.startsWith("url(#") ?? false,
|
|
title: band.children[0]?.text ?? "",
|
|
};
|
|
};
|
|
const out = {
|
|
// 좌측 유출(+ 바깥) · 성토 1:1.2 → 단 사이 수평 조각 → 다시 성토(흙막이 단 사이 포함)
|
|
left: run([
|
|
{ offset: 5, elevation: 10 },
|
|
{ offset: 7.4, elevation: 8 },
|
|
{ offset: 8.4, elevation: 8 },
|
|
{ offset: 10.8, elevation: 6 },
|
|
]),
|
|
// 우측 유출(− 바깥) · 곧은 성토 한 줄
|
|
right: run([
|
|
{ offset: -5, elevation: 10 },
|
|
{ offset: -8.6, elevation: 7 },
|
|
]),
|
|
};
|
|
process.stdout.write(JSON.stringify(out));
|