/* PLAN 323-4 형상 얹기 한 벌 헬퍼 — 진짜 `common_util_structure_attach.ts`(attachShapes) · 3D 지문 · * 벽 한 번(`B05_Profile_UI_Corridor_Structures_Hash.ts`)을 그 자리에서 트랜스파일해 Node 로 돌림 · 결과 = 표준출력 JSON. * 입력 = argv[2] JSON `{definition, types, cases: {이름: {sections, pipes, structures}}}`. 짝 = `test_323_4_attach_mirror.py`. */ const fs = require("fs"); const path = require("path"); const ROOT = path.join(__dirname, "..", ".."); const ts = require(path.join(ROOT, "config", "node_modules", "typescript")); const cache = new Map(); function load(file) { const full = path.resolve(file); if (cache.has(full)) return cache.get(full); if (full.endsWith(".json")) return JSON.parse(fs.readFileSync(full, "utf8")); const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020, esModuleInterop: true, }, }).outputText; const box = { exports: {} }; cache.set(full, box.exports); const need = (spec) => { const base = path.join(path.dirname(full), spec); const hit = [`${base}.ts`, base.replace(/\.js$/, ".ts"), base].find( (p) => fs.existsSync(p) && fs.statSync(p).isFile(), ); if (!hit) throw new Error(`모름: ${spec} (${full})`); return load(hit); }; new Function("exports", "module", "require", js)(box.exports, box, need); return box.exports; } const input = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); const catalog = load(path.join(ROOT, "common_util", "common_util_structure_catalog.ts")); const attach = load(path.join(ROOT, "common_util", "common_util_structure_attach.ts")); const hash = load( path.join(ROOT, "B05_Profile", "logic", "B05_Profile_UI_Corridor_Structures_Hash.ts"), ); catalog.useCatalogDefinition({ types: input.types, ...input.definition }); const out = { cases: {} }; for (const [name, { sections, pipes, structures }] of Object.entries(input.cases)) { attach.attachShapes(sections, pipes, structures, input.types); out.cases[name] = sections; } // 3D 벽 한 번 — 옹벽 93 ~ 113(기준 101)이 덮는 측점 넷 중 기준 측점만 · 스윕 = 시작 ~ 끝 const wall = { structure_id: "w1", start_m: 93, anchor_m: 101, end_m: 113 }; const covered = [93, 100, 101, 113].map((at) => ({ chainage_m: at, revetment: { ...wall } })); out.sweep = covered.map((section) => hash.wallSweepAt(section, covered)); // 3D 지문 — 손 해시가 빠뜨린 칸(벽 기초 · 포장 · 맹암거)도 바뀌면 다름 · 구조물 없는 측점 = 빈 목록 const base = { chainage_m: 0, revetment: { ...wall, foundation: null } }; const fp = (section) => JSON.stringify(hash.structureHashParts(section)); out.fingerprint = { same: fp(base) === fp({ chainage_m: 0, revetment: { ...wall, foundation: null } }), foundation: fp(base) !== fp({ ...base, revetment: { ...wall, foundation: "기초유" } }), pavement: fp(base) !== fp({ ...base, pavement: [{ type_id: "pavement_concrete" }] }), underdrain: fp(base) !== fp({ ...base, underdrain: { height_m: 0.5 } }), adjust: fp(base) !== fp({ ...base, design: { revet_adjust: { own: { x: 1 } } } }), plain: hash.structureHashParts({ chainage_m: 0 }).length === 0, }; process.stdout.write(JSON.stringify(out));