- 헬퍼 동작 그대로(풀이 함수 본문은 옮겨 담음) · 제품 코드 안 고침 - 확인: 이 묶음 헬퍼를 쓰는 시험 236 개 전 230 통과 → 후 230 통과 (시험별 junit 대조 · 어긋남 0) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
114 lines
3.4 KiB
JavaScript
114 lines
3.4 KiB
JavaScript
/* 371-2 관 글 기울기 헬퍼 — 짝 = `test_371_2_pipe_slope_text.py`.
|
|
* B07 픽스처 배관 측점에 `appendCulvertOverlay` 를 가짜 SVG 로 그려 관 글 · 툴팁을 읽음.
|
|
*/
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const { makeLoader } = require("./helper_ts_stub_loader.cjs");
|
|
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
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) };
|
|
|
|
const { loadTs } = makeLoader({
|
|
root: ROOT,
|
|
requireFor: (full) => {
|
|
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)));
|
|
};
|
|
return req;
|
|
},
|
|
});
|
|
|
|
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),
|
|
}),
|
|
);
|