- 유입 · 유출 구조 「흙막이」면 배관 흙막이와 같은 세트(관 숨김 · body)를 같은 측점 culvert 자리에 함께 얹음(파이썬 _guard_set ↔ TS guardSet 짝 · 거울 시험) - 자동 자리 = 벽 전면이 관(세월교) · 본체(박스) 끝 · 좌우 x 는 그 자리 기준(본체를 늘리면 벽도 따라감) · 상하 d 는 배관과 같음 - 연동 옆 측점 · 3D 연동 해제 자리는 본체 기준 x 를 절대값으로 넘겨 받음 · 3D 만료 해시에 흙막이 값 포함 - 설계선 트림 = 벽 선 쪽은 흙막이 · 안 선 쪽은 본체 · 벽 선 쪽 본체 성토선은 걷음(두 줄 방지) · 관 연장은 안 실음 - 좌측 폼 → 캐시: 세트 다시 짓기 · 높이 · 형태 · 추가 흙막이 상자 수 = 유출 다단 수 - [연동][경사] 회색 풀림(옆 측점에 벽이 서면 연동 · 구간 있으면 경사) — 배관과 같은 규칙 - 시험: test_73_4_body_guard(벽 전면 = 본체 끝 · 박스 +1 m 따라감 · 연동 옆 측점 벽 · 서버 Node 면적 = 화면) · 거울 시험 흙막이 칸 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYJJRCfHfdCGNhmXTjQKV7
120 lines
4.6 KiB
JavaScript
120 lines
4.6 KiB
JavaScript
/* 73-4 세월교 · 박스 흙막이 횡단 기하 헬퍼 — 화면 TS(`B06_Section_Structure_Layouts` · `_Cross_Body_Guard`)를
|
|
* 그 자리에서 트랜스파일해 Node 로 돌림. 입력 = 픽스처(검증_sub7 상세 일부) · 결과 = 표준출력 JSON 한 벌.
|
|
* 짝 = `test_73_4_body_guard.py`.
|
|
*/
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
const cache = new Map();
|
|
// 화면 전용 모듈(토스트 · 글) — 기하 계산엔 안 쓰임
|
|
const STUBS = {
|
|
"@ui/ui_template_toast": { showToast: () => undefined },
|
|
};
|
|
|
|
function loadTs(file) {
|
|
const full = path.resolve(file);
|
|
if (cache.has(full)) return cache.get(full);
|
|
if (full.endsWith(".json")) {
|
|
const data = JSON.parse(fs.readFileSync(full, "utf8"));
|
|
cache.set(full, data);
|
|
return data;
|
|
}
|
|
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 localRequire = (request) => {
|
|
if (STUBS[request]) return STUBS[request];
|
|
const alias = { "@util/": "common_util", "@ui/": "ui_template", "@config/": "config" };
|
|
for (const [prefix, dir] of Object.entries(alias))
|
|
if (request.startsWith(prefix))
|
|
return loadTs(resolve(path.join(ROOT, dir, request.slice(prefix.length))));
|
|
return loadTs(resolve(path.join(path.dirname(full), request)));
|
|
};
|
|
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
|
cache.set(full, box.exports);
|
|
return box.exports;
|
|
}
|
|
const resolve = (base) =>
|
|
[base, `${base}.ts`, path.join(base, "index.ts")].find(
|
|
(p) => fs.existsSync(p) && fs.statSync(p).isFile(),
|
|
) ?? `${base}.ts`;
|
|
|
|
const B06 = (f) => path.join(ROOT, "B06_Section", f);
|
|
const { computeStoredLayouts, structureAreaRows, trimOfLayouts } = loadTs(
|
|
B06("B06_Section_Structure_Layouts.ts"),
|
|
);
|
|
const { outletPipeEnd } = loadTs(B06("B06_Section_UI_Cross_Culvert_Tiers.ts"));
|
|
const { bodyGuardBase } = loadTs(B06("B06_Section_UI_Cross_Body_Guard.ts"));
|
|
|
|
const fixture = JSON.parse(
|
|
fs.readFileSync(path.join(__dirname, "fixtures", "b06_73_4_body_guard_sections.json"), "utf8"),
|
|
);
|
|
const sections = fixture.cross_sections;
|
|
const at = (c) => sections.find((s) => Math.abs(s.chainage_m - c) < 0.6);
|
|
const round = (v) => Math.round(v * 1000) / 1000;
|
|
|
|
/** 벽마다 전면(관 끝 자리) ↔ 본체 끝 차이(m) · 바닥 기준 ↔ 본체 끝 표고 차이. */
|
|
function fronts(section) {
|
|
const layouts = computeStoredLayouts(section, sections);
|
|
const base = bodyGuardBase(section, section.samples);
|
|
const walls = layouts.culvert?.walls ?? [];
|
|
return {
|
|
walls: walls.map((w) => {
|
|
const end = base.ends[w.role];
|
|
const front = outletPipeEnd(w);
|
|
return {
|
|
role: w.role,
|
|
form: w.form,
|
|
dOffset: round(front.offset - end.offset),
|
|
dElevation: round(front.elevation - end.elevation),
|
|
front: round(front.offset),
|
|
};
|
|
}),
|
|
bodyX: layouts.culvert?.bodyX ?? null,
|
|
applied: layouts.culvert?.revetShift
|
|
? { inlet: layouts.culvert.revetShift.inlet.x, outlet: layouts.culvert.revetShift.outlet.x }
|
|
: null,
|
|
trimSame:
|
|
JSON.stringify(trimOfLayouts(layouts)) === JSON.stringify(layouts.culvert?.designTrim),
|
|
pipeLength: null,
|
|
};
|
|
}
|
|
|
|
const out = {};
|
|
const box = at(352.14);
|
|
const ford = at(173.09);
|
|
out.box = fronts(box);
|
|
out.ford = fronts(ford);
|
|
|
|
// 박스를 좌우로 1 m 늘리면 벽 전면도 1 m 따라 나감(연동 = 본체 기준 자리)
|
|
const moved = JSON.parse(JSON.stringify(box));
|
|
moved.design.box_adjust = { left: { lengthM: 1, riseM: 0 }, right: { lengthM: 1, riseM: 0 } };
|
|
const swapped = sections.map((s) => (s === box ? moved : s));
|
|
const movedLayouts = computeStoredLayouts(moved, swapped);
|
|
const base = bodyGuardBase(moved, moved.samples);
|
|
out.boxMoved = movedLayouts.culvert.walls.map((w) => ({
|
|
role: w.role,
|
|
dOffset: round(outletPipeEnd(w).offset - base.ends[w.role].offset),
|
|
front: round(outletPipeEnd(w).offset),
|
|
}));
|
|
|
|
// 연동 옆 측점(340 · 360) — 소유 측점 벽이 섬
|
|
out.linked = [340, 360].map((c) => {
|
|
const s = at(c);
|
|
return { chainage: c, walls: computeStoredLayouts(s, sections)?.culvert?.walls.length ?? 0 };
|
|
});
|
|
|
|
// 면적 행 — 서버 Node 진입점과 같은 함수(`structureAreaRows`)
|
|
out.areas = structureAreaRows(sections).filter((r) =>
|
|
[173.087, 352.14].some((c) => Math.abs(r.chainage_m - c) < 0.6),
|
|
);
|
|
process.stdout.write(JSON.stringify(out));
|