/* 234-1 · 272 구조물 기준 측점 = 정수 m · 옛 소수 자동 맞춤(단추 없음 · 167). * 진짜 `common_util_structure_station` · `_Structures_Snap` · `_Structures_Fields` · `_Drainage_Pipes` · * `followedStation` 을 Node 로 돌림. 입력은 argv[2] JSON(파이썬과 같은 값) · 표준출력 JSON 한 벌. */ const fs = require("fs"); const path = require("path"); const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); const ROOT = path.join(__dirname, "..", ".."); function loadTs(file, stubs = {}) { const js = ts.transpileModule(fs.readFileSync(path.join(ROOT, file), "utf8"), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, }).outputText; const box = { exports: {} }; const localRequire = (request) => { if (stubs[request]) return stubs[request]; throw new Error(`stub 없음: ${request} (${file})`); }; new Function("exports", "module", "require", js)(box.exports, box, localRequire); return box.exports; } /* 가짜 DOM — 폼 칸(input · label · span)만. */ function fakeElement(tag) { const listeners = {}; const classes = new Set(); return { tagName: tag, value: "", className: "", style: {}, children: [], classList: { toggle: (name, on) => (on ? classes.add(name) : classes.delete(name)), add: (name) => classes.add(name), remove: (name) => classes.delete(name), contains: (name) => classes.has(name), }, append(...nodes) { this.children.push(...nodes); }, addEventListener(type, fn) { (listeners[type] ??= []).push(fn); }, fire(type) { (listeners[type] ?? []).forEach((fn) => fn({ type })); }, focus() {}, }; } global.document = { createElement: fakeElement }; const input = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); // 심는 측점 = 구조물 표(322-1) — 공용 표 모듈(실제 정의로 세움) const catalog = require("./helper_322_catalog_module.cjs"); const station = loadTs("common_util/common_util_structure_station.ts", { "./common_util_structure_catalog": catalog, }); const STATIONS = input.stations; const out = {}; // ① 맞춤 · 걸음 — 파이썬과 같은 값(거울) out.snap = input.samples.map((value) => station.snapStructureStation(value, STATIONS)); out.step = input.steps.map(([from, dir]) => station.stepStructureStation(from, dir, STATIONS)); out.snapTwice = input.samples.every( (value) => station.snapStructureStation(station.snapStructureStation(value, STATIONS), STATIONS) === station.snapStructureStation(value, STATIONS), ); // 일반 측점 등록 — 구조물 측점(irregular)은 뺌 · 등록값이 기본으로 쓰임 station.setGeneralStations( STATIONS.map((value) => ({ chainage_m: value, kind: "regular" })).concat([ { chainage_m: 80.17, kind: "irregular" }, ]), ); out.general = [...station.generalStations()]; out.defaultSnap = station.snapStructureStation(80.4); // ② 좌측 폼 칸 — 친 값 80.17 → 80 · 안 고친 옛 값은 그대로 const fields = loadTs("B05_Profile/B05_Profile_UI_Structures_Fields.ts", { "./logic/B05_Profile_Util_Station": loadTs("B05_Profile/logic/B05_Profile_Util_Station.ts"), "../B06_Section/logic/B06_Section_UI_Cross_Culvert_Basin": { DEFAULT_BASIN_SHAPE: {} }, "../common_util/common_util_culvert_sets": { fillPipeHeights: () => {} }, "../common_util/common_util_structure_station": station, }); const anchor = fields.stationFields("기준 측점", () => 20); out.remainderStep = anchor.remainder.step; anchor.station.value = "4"; anchor.remainder.value = "0.17"; out.typed = anchor.read(20, true); anchor.remainder.value = "0.6"; out.typedNear = anchor.read(20, true); anchor.remainder.value = "1"; out.typedOne = anchor.read(20, true); anchor.write(80.17, 20); out.untouched = anchor.read(20, true); // ③ 구조물 옮김 · 옛 소수 자동 맞춤 셈 · 실행 const toasts = []; let confirmText = null; const snap = loadTs("B05_Profile/B05_Profile_UI_Structures_Snap.ts", { "@ui/ui_template_elements": { showToast: (message, kind) => toasts.push({ message, kind }), showConfirmDialog: async (message) => ((confirmText = message), true), }, "../common_util/common_util_structure_station": station, "../A00_Common/design/design_selection": { PIPE_MATCH_M: 0.51 }, "./B05_Profile_Api_Structures": { structureAnchorM: (entry) => entry.chainage_m ?? entry.start_m ?? 0, }, }); out.movedPoint = snap.movedStructure({ placement: "point", chainage_m: 127.86 }, 127.86); out.movedWall = snap.movedStructure( { placement: "interval", chainage_m: 80.17, start_m: 75.17, end_m: 95.67 }, 80.17, ); out.movedDrag = snap.movedStructure( { placement: "interval", chainage_m: 50, start_m: 50, end_m: 62 }, 133.4, ); const structures = input.structures.map((entry) => ({ ...entry })); const pipes = input.pipes.map((chainage_m) => ({ chainage_m, facility: "pipe", options: { pipe_kind: "흄관" }, summary_item: null, group_items: null, })); const plan = snap.snapPlan(structures, pipes); out.plan = { structures: plan.structures.map(({ index, next }) => ({ index, next })), pipes: plan.pipes.map(({ from, to }) => [from, to]), skipped: plan.skipped, }; const moves = []; let emits = 0; let closed = 0; Promise.resolve( snap.snapOldStations({ structures, pipes: () => pipes, emit: () => (emits += 1), movePipe: (from, to, attributes) => moves.push({ from, to, attributes }), closeForm: () => (closed += 1), }), ) .then(async (count) => { out.run = { count, emits, closed, moves, structures, confirmText, toasts: [...toasts] }; // 두 번째 = 옮길 것 없음(관은 위 moves 로 옮겨졌다고 보고 목록을 맞춤) moves.forEach(({ from, to }) => { const hit = pipes.find((pipe) => pipe.chainage_m === from); if (hit) hit.chainage_m = to; }); toasts.length = 0; const again = { structures, pipes: () => pipes, emit() {}, movePipe() {}, closeForm() {} }; out.second = { count: snap.snapOldStations(again), toasts: [...toasts] }; // ④ 계획고 편집 따라감 — 한꺼번에 여럿 옮김(사라진 둘 · 새 둘) const pivot = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Pivot.ts"); const alignment = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Alignment.ts", { "./B05_Profile_UI_Profile_Pivot": pivot, }); const gone = [ { id: "pipe-80.17", chainage_m: 80.17 }, { id: "pipe-263.31", chainage_m: 263.31 }, ]; const born = [ { id: "pipe-80.00", chainage_m: 80 }, { id: "pipe-263.00", chainage_m: 263 }, ]; out.follow = gone.map((entry) => { const old = alignment.followedStation(entry, gone, born); const next = old ?? snap.followSnapped(entry, born); return { old: old?.chainage_m ?? null, next: next?.chainage_m ?? null }; }); // ⑤ 배수유역도 관 지점 — 옮김 · 우클릭 추가 = 정수 m const pipesEditor = loadTs("B05_Profile/B05_Profile_UI_Drainage_Pipes.ts", { "../A00_Common/touch_bridge": { pickPx: (px) => px }, // 기기대응 4-4 — 마우스 반경 그대로 "@ui/ui_template_palette": { themeColor: () => "#000" }, "../common_util/common_util_structure_station": station, }); const editor = pipesEditor.createPipeEditor(() => {}); editor.setContext(null, [ { x: 0, y: 0, chainage_m: 0 }, { x: 300, y: 0, chainage_m: 300 }, ]); editor.setPipes([{ chainage_m: 80.17, reason: "stream" }]); editor.moveTo(0, 141.37); editor.addAtChainage(200.6); out.editor = editor.pipes().map((pipe) => pipe.chainage_m); process.stdout.write(JSON.stringify(out)); }) .catch((error) => { process.stderr.write(String(error && error.stack ? error.stack : error)); process.exit(1); });