- 부품 상자 common_util_structure_parts.json(갈래마다 부품 · 읽는 칸 · 자리꼴 · 정본 · 42종 처음값) · 표 읽기 common_util_structure_catalog.ts / .py 짝(정의 반영 ⊕ 처음값 ⊕ 부품 끔) · 거울 시험 test_322_1_catalog_mirror - 목록: pickGroups 군 차례 = 구조물군 열쇠 · 페이지 거르기 listPick(B05 both · B06 both + b06) · HIDDEN_PICK_GROUPS · HIDDEN_PICK_TYPES · pickable 걷음(옛 대피 셋 asRoad 그대로) - 측점: structureStationMarks · pipeStationMarks · is_station_planting_type · resolve_extra_stations = 표 횡단도(anchor · span3 · none) - 카드 이름: cardTitle / card_title 한 공식(빈칸 = 그 종류 정의 기본값) · 표시 이름이 정의 관종도 앎 - 형상: 벽 = shape wall(형태 = 형상칸.form · 물빼기 = 형상칸.weep) · 포장 = shape pavement(「사용 중」 거르기 없앰) · 맹암거 · 사토 · 관 지점 시설 세트 = shape - 3D 몸체 = 그림 자료 주인 종류의 표 3D 부품 · 알약 띠 = 표 자리꼴 구간(관 지점 시설은 알약만) - ★1 노출형 · 개거 횡단도 none · ★2 쇄석 화면에서도 포장층 · ★3 기슭막이 표 자리꼴 interval(정의 파일 그대로) · ★4 코드 숨김 10종 list hidden · ★5 관 빈칸 D800 → 정의 기본 D1000 - 옛 시험: 도움 파일 로더 27개가 표 모듈 공용 한 벌(helper_322_catalog_module) 씀 · 기대값 고침(272_1 · b05_extra_stations · roundtrip · 314 · 52_7 · walls 거울 · wall_drain_pipe) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
187 lines
6.8 KiB
JavaScript
187 lines
6.8 KiB
JavaScript
/* PLAN 219-1 단위검증 헬퍼 — 횡단 보기 방향키가 관을 몰래 옮기던 흠.
|
|
* 실제 TS(`B05_Profile_UI_Profile_Panel_Tools` · `B06_Section_UI_Cross_Panel_Base`)를 그 자리에서
|
|
* 트랜스파일해 Node 로 돌림 · 결과 = 표준출력 JSON 한 벌. 짝 = `test_219_1_arrow_owner.py`.
|
|
* 창 · 문서 듣개는 흉내(누름 = 문서 잡기 단계 · 키 = 문서 듣개(횡단 조정창) 뒤 창 듣개(종단도) 차례).
|
|
*/
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1)
|
|
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
const cache = new Map();
|
|
|
|
const listeners = { window: {}, document: {} };
|
|
const on = (where) => (type, fn) => (listeners[where][type] ??= new Set()).add(fn);
|
|
const off = (where) => (type, fn) => listeners[where][type]?.delete(fn);
|
|
const session = new Map();
|
|
globalThis.sessionStorage = {
|
|
getItem: (k) => session.get(k) ?? null,
|
|
setItem: (k, v) => session.set(k, String(v)),
|
|
removeItem: (k) => session.delete(k),
|
|
};
|
|
globalThis.window = {
|
|
addEventListener: on("window"),
|
|
removeEventListener: off("window"),
|
|
setTimeout: () => 0,
|
|
clearTimeout: () => undefined,
|
|
setInterval: () => 0,
|
|
clearInterval: () => undefined,
|
|
};
|
|
globalThis.document = { addEventListener: on("document"), removeEventListener: off("document") };
|
|
|
|
// 화면 계산 · 이력 · 도구줄은 이 흠과 무관 — 쓰는 꼴만 흉내
|
|
const calls = { move: [], edits: 0 };
|
|
const STUBS = {
|
|
"./B05_Profile_UI_Profile_Alignment": { adjustStation: () => (calls.edits++, {}) },
|
|
"./B05_Profile_UI_Profile_Edit": { HOLD_DELAY_MS: 500, HOLD_INTERVAL_MS: 100 },
|
|
"./B05_Profile_UI_Profile_History": {
|
|
createProfileHistory: () => ({
|
|
record: () => undefined,
|
|
pause: () => undefined,
|
|
resume: () => undefined,
|
|
undo: () => undefined,
|
|
redo: () => undefined,
|
|
canUndo: () => false,
|
|
canRedo: () => false,
|
|
}),
|
|
},
|
|
"./B05_Profile_UI_Profile_Tools": {
|
|
createProfileTools: () => ({ mode: () => "none", clear: () => undefined }),
|
|
},
|
|
"./B05_Profile_UI_Profile_Straighten": {},
|
|
// [자유지정](215-1) — 화면 설정(@config) 줄기를 끌고 옴 · 이 흠과 무관
|
|
"../A00_Common/design/design_profile_free": {
|
|
createFreeDraw: () => ({ key: () => false, hint: () => "" }),
|
|
},
|
|
"../A00_Common/design/design_profile_free_view": {
|
|
createFreeView: () => ({ mount: () => undefined, redraw: () => undefined }),
|
|
},
|
|
};
|
|
|
|
function loadTs(file) {
|
|
const full = path.resolve(file);
|
|
if (cache.has(full)) return cache.get(full);
|
|
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 localRequire = (request) =>
|
|
(CATALOG.isCatalogRequest(request) ? CATALOG : null) ??
|
|
STUBS[request] ??
|
|
(request.startsWith(".")
|
|
? loadTs(path.join(path.dirname(full), `${request}.ts`))
|
|
: require(request));
|
|
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
|
cache.set(full, box.exports);
|
|
return box.exports;
|
|
}
|
|
|
|
const tools = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_Profile_Panel_Tools.ts"));
|
|
const base = loadTs(path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Panel_Base.ts"));
|
|
|
|
// 엄상준 263.21 관 — 횡단 보기에서 그 관 카드가 골라져 종단도도 그 관 측점을 고른 상태
|
|
const pipe = { id: "p1", chainage_m: 263.21, origin: "pipe" };
|
|
tools.createPanelTools({
|
|
root: { isConnected: true },
|
|
base: () => ({}),
|
|
alignment: () => null,
|
|
edits: () => ({}),
|
|
applyEdits: () => undefined,
|
|
selectedStationId: () => "irregular:p1",
|
|
irregularStations: () => [pipe],
|
|
stationIdOf: (s) => `irregular:${s.id}`,
|
|
moveStation: (s, to) => calls.move.push([s.chainage_m, to]),
|
|
drainage: { pipeChainages: () => [], setPipeChainages: () => undefined },
|
|
restore: () => undefined,
|
|
restoreSaved: () => undefined,
|
|
canRestoreSaved: () => false,
|
|
refresh: () => undefined,
|
|
});
|
|
|
|
/** 누른 자리 — 종단도 안이면 closest 가 그 판을 줌(새 창 요소처럼 Element 가 아닌 것도). */
|
|
const spot = (inProfile) => ({
|
|
closest: (sel) => (inProfile && sel === ".b05-route-profile" ? {} : null),
|
|
});
|
|
const press = (target) => listeners.document.pointerdown?.forEach((fn) => fn({ target }));
|
|
|
|
/** 횡단 조정창 흉내 — 관 · 도수로 띠를 고른 동안 `swallowsArrows` 면 삼킴(Cross_Structure_Panel 배선과 같음). */
|
|
let side = "";
|
|
const crossPanel = (event) => {
|
|
if (base.swallowsArrows(side, event.key)) event.preventDefault();
|
|
};
|
|
listeners.document.keydown = new Set([crossPanel]);
|
|
|
|
function key(name) {
|
|
const event = {
|
|
key: name,
|
|
target: null,
|
|
repeat: false,
|
|
defaultPrevented: false,
|
|
preventDefault() {
|
|
this.defaultPrevented = true;
|
|
},
|
|
};
|
|
listeners.document.keydown.forEach((fn) => fn(event));
|
|
listeners.window.keydown?.forEach((fn) => fn(event));
|
|
listeners.window.keyup?.forEach((fn) => fn({ key: name }));
|
|
return event.defaultPrevented;
|
|
}
|
|
|
|
function run(steps) {
|
|
calls.move = [];
|
|
calls.edits = 0;
|
|
steps();
|
|
return { move: calls.move, edits: calls.edits };
|
|
}
|
|
|
|
const out = {
|
|
// 횡단 카드를 누른 뒤(조정창 없음) ← → = 안 옮김 — 옛 흠: 263.21 → 263.31
|
|
cardThenArrows: run(() => {
|
|
press(spot(false));
|
|
key("ArrowRight");
|
|
key("ArrowLeft");
|
|
}),
|
|
// 종단도를 누른 뒤 ← → = 지금처럼 0.1 m 이동
|
|
profileThenArrows: run(() => {
|
|
press(spot(true));
|
|
key("ArrowRight");
|
|
}),
|
|
// 새 창 종단도(다른 창 요소)도 종단도 안
|
|
popoutThenArrows: run(() => {
|
|
press(Object.create(null, { closest: { value: spot(true).closest } }));
|
|
key("ArrowLeft");
|
|
}),
|
|
// 횡단에서 도수로 띠 고름 · 카드 쪽 누름 = 방향키 넷 다 아무것도 안 옮김(계획고도)
|
|
chute: run(() => {
|
|
side = "chute";
|
|
press(spot(false));
|
|
["ArrowRight", "ArrowLeft", "ArrowUp", "ArrowDown"].forEach(key);
|
|
}),
|
|
pipeSide: run(() => {
|
|
side = "pipe";
|
|
press(spot(false));
|
|
key("ArrowRight");
|
|
key("ArrowUp");
|
|
}),
|
|
// 도수로 고른 채 종단도를 누르면 방향키는 종단도 몫
|
|
chuteThenProfile: run(() => {
|
|
side = "chute";
|
|
press(spot(true));
|
|
key("ArrowRight");
|
|
}),
|
|
// 조정창 있는 벽 고름 · 고름 없음 = 삼키지 않음(조정창 · 종단도 제 길)
|
|
swallow: {
|
|
wall: base.swallowsArrows("inlet", "ArrowRight"),
|
|
none: base.swallowsArrows("", "ArrowRight"),
|
|
letter: base.swallowsArrows("chute", "a"),
|
|
},
|
|
};
|
|
side = "";
|
|
out.cardUpArrow = run(() => {
|
|
press(spot(false));
|
|
key("ArrowUp"); // 고름 없는 카드 ▲ = 계획고(24-3b 그대로)
|
|
});
|
|
process.stdout.write(JSON.stringify(out));
|