- 관 길이 = 유출 벽 자리에서 셈(0.1 m) · 정수 올림 연장 · 길이 맞춤 이동(_Culvert_Fit) 없앰 - 관보호공 ◀▶▲▼ · 끌기 0.1 m 걸음(세월교 흙막이만 1 m) · ◀▶ 때 d 빈 칸은 지금 자리로 묶음 - 관 · 세월교 정보 글 = 노폭 · 노견 라벨 바로 위 줄 · 카드 · 좌측 패널 「정수 m (소수 첫째)」 - 구조물 집계(M02_Table_Fill) 관연장 = 사사오입 정수 · TS pipeLengthWhole 짝 · 새 시험 test_225_1 - 정수 맞춤 없앰에 맞춰 139_6 · 186_1 · 108 · 79 기대값 고침 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JywHxQuWPk6UYXTebyrBoU
155 lines
5.7 KiB
JavaScript
155 lines
5.7 KiB
JavaScript
/* 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("B06_Section_UI_Cross_Culvert_Solve");
|
|
const { pipeLengthText, pipeLengthWhole } = B06("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)),
|
|
}),
|
|
);
|