/* 371-2 관 글 기울기 헬퍼 — 짝 = `test_371_2_pipe_slope_text.py`. * B07 픽스처 배관 측점에 `appendCulvertOverlay` 를 가짜 SVG 로 그려 관 글 · 툴팁을 읽음. */ 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(); const noop = new Proxy({}, { get: () => () => "" }); /** 아주 작은 SVG 가짜 — 글 자리 판정에 쓰는 것만(class 고르기 · 속성 · 부모). */ class El { constructor(tag) { this.tag = tag; this.attrs = {}; this.children = []; this.parentNode = null; this.ownerSVGElement = null; this.textContent = ""; } setAttribute(k, v) { this.attrs[k] = String(v); } getAttribute(k) { return this.attrs[k] ?? null; } appendChild(c) { c.parentNode = this; c.ownerSVGElement = this.tag === "svg" ? this : this.ownerSVGElement; this.children.push(c); return c; } append(...cs) { cs.forEach((c) => this.appendChild(c)); } addEventListener() {} all() { return this.children.flatMap((c) => [c, ...c.all()]); } querySelectorAll(sel) { return this.all().filter((e) => (e.attrs.class ?? "").split(" ").includes(sel.slice(1))); } querySelector(sel) { return this.querySelectorAll(sel)[0] ?? null; } } globalThis.document = { addEventListener: () => undefined, createElementNS: (_ns, t) => new El(t) }; function loadTs(file) { const full = path.resolve(file); if (cache.has(full)) return cache.get(full); if (full.endsWith(".json")) return JSON.parse(fs.readFileSync(full, "utf8")); 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 resolve = (base) => fs.existsSync(base) && fs.statSync(base).isFile() ? base : `${base}.ts`; const req = (r) => { if (r.startsWith("@ui/") || r.startsWith("@config/")) return noop; if (r.startsWith("@util/")) return loadTs(resolve(path.join(ROOT, "common_util", r.slice(6)))); return loadTs(resolve(path.join(path.dirname(full), r))); }; new Function("exports", "module", "require", js)(box.exports, box, req); cache.set(full, box.exports); return box.exports; } const { computeCulvertLayout } = loadTs( path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Culvert_Geom.ts"), ); const { appendCulvertOverlay } = loadTs( path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Culvert.ts"), ); const sections = JSON.parse( fs.readFileSync(path.join(__dirname, "fixtures", "b07_cross_overlay_sections.json"), "utf8"), ).cross_sections; const section = sections.find((s) => s.culvert && !s.culvert.hidden_pipe); /** 노폭 라벨이 있는 B06 카드 꼴 svg 에 그려 관 글 · 관 툴팁을 돌려줌. */ function draw(layout, hidePipe) { const svg = new El("svg"); const road = new El("text"); road.setAttribute("class", "b06-chart__carriageway-label"); road.setAttribute("x", 200); road.setAttribute("y", 180); svg.appendChild(road); const plot = new El("g"); svg.appendChild(plot); appendCulvertOverlay( plot, layout, (o) => o, (z) => z, undefined, hidePipe, false, ); const titles = svg .all() .filter((e) => e.tag === "title") .map((e) => e.textContent); return { infos: svg.querySelectorAll(".b06-chart__pipe-info").map((e) => e.textContent), pipeTips: titles.filter((t) => t.startsWith("관매설")), }; } const layout = computeCulvertLayout(section, section.samples); const reverse = { ...layout, pipe: { ...layout.pipe, slopePct: -0.43 } }; process.stdout.write( JSON.stringify({ slopePct: layout.pipe.slopePct, lengthM: layout.pipe.lengthM, pipe: draw(layout, false), reverse: draw(reverse, false), hidden: draw(layout, true), }), );