Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
143 lines
4.5 KiB
JavaScript
143 lines
4.5 KiB
JavaScript
/* 294-6 물넘이 연결 표기 헬퍼 — 진짜 TS 를 Node 로 돌려 표준출력 JSON 한 벌로.
|
|
* 옆 카드(물넘이 측점 ≠ 카드 측점) · 주인 카드에 물넘이포장을 그리고 연결 꼴 · 호버 글 · 고름 표식을 잼.
|
|
*/
|
|
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 load = (file) => {
|
|
if (cache.has(file)) return cache.get(file);
|
|
const js = ts.transpileModule(fs.readFileSync(file, "utf8"), {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
cache.set(file, box.exports);
|
|
const near = (m) => {
|
|
if (m.startsWith("@config/")) return load(path.join(ROOT, "config", `${m.slice(8)}.ts`));
|
|
if (m.startsWith(".")) return load(path.join(path.dirname(file), `${m}.ts`));
|
|
return new Proxy({}, { get: () => () => {} });
|
|
};
|
|
new Function("exports", "module", "require", js)(box.exports, box, near);
|
|
return box.exports;
|
|
};
|
|
|
|
// DOM 흉내 — 속성 · 자식 · classList · 글만
|
|
class Node {
|
|
constructor(tag) {
|
|
this.tag = tag;
|
|
this.attrs = {};
|
|
this.children = [];
|
|
this.dataset = {};
|
|
this.style = {};
|
|
this.textContent = "";
|
|
}
|
|
setAttribute(k, v) {
|
|
this.attrs[k] = String(v);
|
|
}
|
|
getAttribute(k) {
|
|
return this.attrs[k] ?? null;
|
|
}
|
|
hasAttribute(k) {
|
|
return k in this.attrs;
|
|
}
|
|
append(...kids) {
|
|
this.children.push(...kids);
|
|
}
|
|
get classList() {
|
|
const names = () => (this.attrs.class ?? "").split(" ").filter(Boolean);
|
|
const set = (list) => (this.attrs.class = list.join(" "));
|
|
return {
|
|
contains: (c) => names().includes(c),
|
|
add: (c) => names().includes(c) || set([...names(), c]),
|
|
toggle: (c, on) =>
|
|
set(on ? [...names().filter((n) => n !== c), c] : names().filter((n) => n !== c)),
|
|
};
|
|
}
|
|
querySelectorAll(selector) {
|
|
const attr = /^\[([\w-]+)\]$/.exec(selector)?.[1];
|
|
const all = this.children.flatMap((k) => [k, ...k.querySelectorAll(selector)]);
|
|
return all.filter((k) => attr && k.hasAttribute(attr));
|
|
}
|
|
}
|
|
globalThis.document = { createElementNS: (_ns, tag) => new Node(tag) };
|
|
|
|
const B06 = (f) => path.join(ROOT, "B06_Section", f);
|
|
const pave = load(B06("B06_Section_UI_Cross_Ford_Pavement.ts"));
|
|
const linked = load(B06("B06_Section_UI_Cross_Ford_Linked.ts"));
|
|
const shapePick = load(B06("B06_Section_UI_Cross_Shape_Pick.ts"));
|
|
|
|
const design = {
|
|
section_mode: "left_cut",
|
|
tilted: true,
|
|
cross_slope_pct: 5,
|
|
design_elevation_m: 100,
|
|
pavement_thickness_m: 0.2,
|
|
paved: true,
|
|
road_edges: {
|
|
left: { offset_m: 3, elevation_m: 100.15 },
|
|
right: { offset_m: -3, elevation_m: 99.85 },
|
|
},
|
|
carriageway_edges: {
|
|
left: { offset_m: 2.5, elevation_m: 100.125 },
|
|
right: { offset_m: -2.5, elevation_m: 99.875 },
|
|
},
|
|
design_line: [
|
|
{ offset_m: 6, elevation_m: 103 },
|
|
{ offset_m: 3, elevation_m: 100.15 },
|
|
{ offset_m: -3, elevation_m: 99.85 },
|
|
{ offset_m: -7, elevation_m: 97.2 },
|
|
],
|
|
};
|
|
// 검증 프로젝트 꼴 — 4+10(물넘이 · 폭 10) → 4+15 카드에도 포장 · 측점 간격 20
|
|
const spec = { chainage_m: 90, span_m: 10, depth_m: 0.4, slope_pct: 5 };
|
|
|
|
const draw = (chainage) => {
|
|
const svg = new Node("g");
|
|
const before = new Node("polyline"); // 앞서 그린 것(세월교 점선 등) — 연결 꼴 밖
|
|
before.setAttribute("class", "b06-chart__ford-deck-plan");
|
|
svg.append(before);
|
|
const from = svg.children.length;
|
|
pave.appendFordPavementOverlay(
|
|
svg,
|
|
spec,
|
|
design,
|
|
(o) => o,
|
|
(e) => e,
|
|
);
|
|
const marked = linked.markLinkedFordPavement(
|
|
svg,
|
|
from,
|
|
{ ford_pavement: spec, chainage_m: chainage },
|
|
20,
|
|
);
|
|
const shapes = svg.children
|
|
.slice(from)
|
|
.filter((k) =>
|
|
["b06-chart__ford-pavement", "b06-chart__ford-deck-plan"].some((c) =>
|
|
k.classList.contains(c),
|
|
),
|
|
);
|
|
shapePick.highlightShapes(svg, "pipe-90.00");
|
|
return {
|
|
marked,
|
|
classes: shapes.map((k) => k.attrs.class),
|
|
titles: shapes.map((k) =>
|
|
k.children.filter((c) => c.tag === "title").map((c) => c.textContent),
|
|
),
|
|
pick: shapes
|
|
.filter((k) => k.hasAttribute("data-structure-id"))
|
|
.map((k) => k.attrs["data-structure-id"]),
|
|
beforeClass: before.attrs.class,
|
|
};
|
|
};
|
|
|
|
process.stdout.write(
|
|
JSON.stringify({
|
|
neighbor: draw(95),
|
|
owner: draw(90),
|
|
noSpec: linked.markLinkedFordPavement(new Node("g"), 0, { chainage_m: 95 }, 20),
|
|
}),
|
|
);
|