- 규칙: 구조물 · 관 기준 측점 = 정수 m 사사오입(일반 측점 1 m 붙기 뺌) · 측점 심는 자리 TS 짝(plantedChainages ↔ resolve_extra_stations) · 같은 m 면 구조물 측점 하나(격자 자리 grid) · 떠나면 일반 측점(oneStationList ↔ _merge_irregular_into_longitudinal) - 서버: B05 [저장] · B06 저장 · [측점 만들기]가 목록 전체 다시 맞춤(빈 목록도 · 옛 글 · 횡단 파일 글) · 계획선 측점 줄 다시 셈 · 정본에 없는 횡단 행은 0.5 m 안 새 측점으로 옮기고 나머지 지움 · 옛 소수 측점 꼭짓점 · 편집은 정수 자리로 · 맞춤 실패는 저장을 안 막음 - 화면: 종단 그래프 · 3D · 표 · ▲▼ 한 목록(초안 반영) · C군 구간형 · 독립 기슭막이 시작 · 끝 측점 · B06 카드는 초안에 없는 옛 구조물 측점 숨김 · 고름은 같은 m 구조물 측점 먼저 · 폼 거리 칸 정수 표기 · C군 계산 구간 · 포장 나눔 경계 정수 - 옛 자료: [정수 맞춤] 단추 없앰 · 종단 보기 열 때 한 번 자동 정수 · 알림 한 줄 · [저장]하면 남음 - 시험: test_272_1 · test_272_2 새로 · 234_1 · 238_1 · b05_extra_stations_supplier · b06_patch_station · b05_station_roundtrip · b05_b06_save_roundtrip 맞춤 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LLCk2y5xE8YGVgjbTntWsY
194 lines
7.3 KiB
JavaScript
194 lines
7.3 KiB
JavaScript
/* 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"));
|
|
const station = loadTs("common_util/common_util_structure_station.ts");
|
|
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", {
|
|
"./B05_Profile_Util_Station": loadTs("B05_Profile/B05_Profile_Util_Station.ts"),
|
|
"../B06_Section/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 alignment = loadTs("B05_Profile/B05_Profile_UI_Profile_Alignment.ts");
|
|
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", {
|
|
"@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);
|
|
});
|