/* 225-1 관 정보 글 자리 · 관보호공 0.1 m 이동 · 관 길이 표시 반올림 헬퍼 — 짝 = `test_225_1_pipe_round.py`. * 입력 = 인자(반올림 거울 값 JSON 배열) · B07 픽스처 배관 측점(139-6 과 같은 자료). */ 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 B06 = (name) => loadTs(path.join(ROOT, "B06_Section", `${name}.ts`)); const { computeCulvertLayout } = B06("B06_Section_UI_Cross_Culvert_Geom"); const { cutLength } = B06("logic/B06_Section_UI_Cross_Culvert_Solve"); const { pipeLengthText, pipeLengthWhole } = B06("logic/B06_Section_UI_Cross_Culvert_Const"); const { appendPipeInfoText } = B06("B06_Section_UI_Cross_Culvert_Types"); const { structurePanelDeps } = B06("B06_Section_UI_Cross_View_Structure"); // ① 글 자리 — 노폭 · 노견 라벨(x 200 · y 180) 바로 위 줄 · 둘이면 한 줄 더 위 · 라벨 없으면 fallback 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); appendPipeInfoText(plot, "관매설 Φ600m/m, L=8 m (7.6)", null); appendPipeInfoText(plot, "세월교 L8m", [10, 10]); const infos = svg.querySelectorAll(".b06-chart__pipe-info").map((e) => ({ x: Number(e.getAttribute("x")), y: Number(e.getAttribute("y")), text: e.textContent, inSvg: e.parentNode === svg, })); const bare = new El("svg"); appendPipeInfoText(bare, "관", null); appendPipeInfoText(bare, "세월교", [10, 20]); const bareInfos = bare.querySelectorAll(".b06-chart__pipe-info").map((e) => ({ x: Number(e.getAttribute("x")), y: Number(e.getAttribute("y")), text: e.textContent, })); // ② 0.1 m 이동 — 걸음 분류 · 유출 벽을 0.1 m 씩 밀면 관 길이가 벽 자리를 따라 0.1 m 로 바뀜 const fine = (key, hiddenPipe, fordGuard) => structurePanelDeps({ hiddenPipe: () => hiddenPipe, fordGuard: () => fordGuard, isLinked: false, }).fineMove(key); const steps = { pipeOutlet: fine("outlet", false, false), pipeInlet: fine("inlet", false, false), fordGuard: fine("outlet", true, true), hiddenOwn: fine("outlet", true, false), }; 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); const r3 = (v) => Math.round(v * 1000) / 1000; // ◀/▶ 한 번 = x 0.1 + d 빈 칸은 지금 자리로 묶음(옛 기본 d 로 튀지 않게) const patches = []; const nudgeDeps = structurePanelDeps({ adjustOf: () => ({ x: 0, d: null, h: null, m: null }), appliedD: () => new Map([["outlet", 0.4]]), outwardOf: () => 1, adjustChainage: () => 0, revetOffset: { update: (_c, key, patch) => patches.push({ key, ...patch }) }, }); nudgeDeps.nudge("outlet", 0.1); nudgeDeps.nudge("extra0", 0.1); const base = computeCulvertLayout(section, section.samples); const d0 = base.revetShift.outlet.d; const moved = [0, 0.1, 0.2, 0.3].map((x) => { const layout = computeCulvertLayout(section, section.samples, { outlet: { x, d: x ? d0 : null, h: null, m: null }, }); return { x, lengthM: layout.pipe.lengthM, cut: r3(cutLength(layout.pipeCorners)) }; }); // ③ · ④ 표시 · 반올림 거울 const values = JSON.parse(process.argv[2] ?? "[]"); process.stdout.write( JSON.stringify({ infos, bareInfos, steps, patches, moved, text: values.map((v) => pipeLengthText(v)), whole: values.map((v) => pipeLengthWhole(v)), }), );