Files
Aislo/resources/tester/helper_514_1_mark_remove.cjs
T

120 lines
4.2 KiB
JavaScript

/* 514-1 알약 · 평면 마커 우클릭 「구조물 삭제」(요구 328-1) — `design_pipe_ids.removeMarkOwner` ·
* `_RouteEdit_StructureMenu` 를 Node 로 돌려 JSON 한 줄(`test_514_1_mark_remove.py` 가 판정).
* 관 장부 · 메뉴 판 · 마커 집기는 가짜 · 주인 가르기 · 평면 항목 짓기는 진짜.
* node helper_514_1_mark_remove.cjs
*/
const path = require("path");
const { makeLoader } = require("./helper_ts_stub_loader.cjs");
const ROOT = path.join(__dirname, "..", "..");
const B05 = path.join(ROOT, "B05_Profile");
const opens = [];
const calls = [];
const ui = () => ({
createMapContextMenu: () => ({
element: { parentElement: null },
open: (x, y, items) => opens.push({ x, y, items }),
close() {},
contains: () => false,
}),
});
// 평면 마커 = 측점선 없는 대피로 20 m(화면 40 px)
const MARK = { id: "evac-1", chainageM: 20 };
const STUBS = {
"../b_page_state": {
readState: () => ({ next: 4, ids: [{ id: "pipe-s3", at: 55 }] }),
writeState() {},
},
"./design_selection": {
PIPE_MATCH_M: 0.51,
isNear: () => false,
nearestWithin: () => null,
readSelection: () => null,
selectDesign() {},
},
common_util_pick_tag: { setPipeTagSource() {} },
"../A00_Common/touch_bridge": { pickPx: (v) => v },
"./B05_Profile_UI_IrregularStations": { irregularStationId: (id) => `irr:${id}` },
"./B05_Profile_Api_Structures": { structureAnchorM: (s) => s.chainage_m ?? 0 },
"../A00_Common/design/design_selection": { PIPE_MATCH_M: 0.51 },
"./B05_Profile_UI_RouteEdit_StructureMarks": {
planMarkAt: (_c, _l, toScreen, px, py) => {
const [x, y] = toScreen([MARK.chainageM, 0]);
const distance = Math.hypot(x - px, y - py);
return distance <= 10 ? { mark: MARK, distance } : null;
},
},
};
const { loadTs } = makeLoader({ root: ROOT, stubs: STUBS, ui });
const ids = loadTs(path.join(ROOT, "A00_Common", "design", "design_pipe_ids.ts"));
ids.usePipeIds("p1");
const shared = loadTs(path.join(B05, "B05_Profile_UI_Profile_Structures.ts"));
const plan = loadTs(path.join(B05, "B05_Profile_UI_RouteEdit_StructureMenu.ts"));
const out = {};
// 알약 주인 가르기 — 관 세션 id · 옛 자리 이름 관 = 관 지점 · 그 밖 = 구조물 id
const remove = {
pipe: (m) => calls.push(["pipe", m]),
structure: (id) => calls.push(["structure", id]),
};
["pipe-s3", "pipe-12.50", "evac-1", "cross-2"].forEach((id) => ids.removeMarkOwner(id, remove));
out.owners = calls.splice(0);
function fakeElement() {
const listeners = {};
return {
isConnected: true,
addEventListener: (type, fn) => (listeners[type] = listeners[type] || []).push(fn),
getBoundingClientRect: () => ({ left: 0, top: 0 }),
append(child) {
child.parentElement = this;
},
parentElement: null,
fire(type, clientX, clientY) {
(listeners[type] || []).forEach((fn) =>
fn({ clientX, clientY, target: null, preventDefault() {} }),
);
},
};
}
// 종단 그래프 = 재료(관 측점 40 m 하나)
const host = fakeElement();
shared.mountStructureMenu(host, {
stations: [{ id: "pipe-1", chainage_m: 40, origin: "pipe" }],
x: (m) => m * 2,
chainageAt: (px) => px / 2,
maxChainageM: 100,
onRemove: (station) => calls.push(["remove", station.id]),
onRemoveMark: (id) => calls.push(["removeMark", id]),
onAddPipe: () => {},
structureGroups: () => [{ label: "A군", types: [{ type: { name: "배수관", type_id: "pipe" } }] }],
onAddStructureType: () => {},
});
const canvas = fakeElement();
canvas.parentElement = fakeElement();
const line = [
[0, 0],
[100, 0],
];
plan.bindPlanStructureMenu(
{
canvas,
toScreen: (v) => [v[0] * 2, v[1] * 2],
unrotate: (x, y) => [x, y],
state: () => ({ plannedLine: line, planned: line }),
},
() => -1,
);
const planAt = (x, y) => {
canvas.fire("contextmenu", x, y);
const items = opens.at(-1).items;
return items.map((item) => (Array.isArray(item) ? item[0] : item.label));
};
out.planMark = planAt(41, 3); // 대피로 마커
opens.at(-1).items[0][1]();
out.planPipe = planAt(81, 2); // 관 측점선 = 측점 먼저
opens.at(-1).items[0][1]();
out.planEmpty = planAt(140, 2); // 빈 자리 = 추가 메뉴
out.calls = calls;
process.stdout.write(JSON.stringify(out));