- config 기본 L 20 · 초기값 직선 문턱 500 m ↔ TS `logic/B05_Profile_Profile_CurveLaw.ts` 상수 짝 - 초기값(1차 지반 종단 · 추천 [적용])만 정책 `no_curve_keys` 로 얼림 · 손 편집 · R 이면 기본 곡선 - 법정 종단곡선 최소 R · L 미달 = 경고 글(파이썬 · TS 같은 글) + 종단 요약줄 「종단곡선 미달」 - 노선 [저장]은 옛 정책(L · 열쇠) 이어받음 · 바뀐 자리 새 꼭짓점만 새 지반 열쇠(`carry_policy` ↔ `carryNoCurveKeys`) - 시험 test_432_1 + 헬퍼 스텁 넷 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWh5wNNycMkbwMpACz1rte
132 lines
5.2 KiB
JavaScript
132 lines
5.2 KiB
JavaScript
/* 400 계획고 이월 거울 — 진짜 TS(`logic/B05_Profile_Grade_Carry` · `logic/B05_Profile_Pipeline_Grade` follow)를
|
|
* 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;
|
|
}
|
|
|
|
const straightJudge = loadTs("B05_Profile/logic/B05_Profile_Profile_Straight.ts");
|
|
const curveLaw = loadTs("B05_Profile/logic/B05_Profile_Profile_CurveLaw.ts");
|
|
const pivot = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Pivot.ts", {
|
|
"./B05_Profile_Profile_Straight": straightJudge,
|
|
});
|
|
const alignment = loadTs("B05_Profile/logic/B05_Profile_UI_Profile_Alignment.ts", {
|
|
"./B05_Profile_UI_Profile_Pivot": pivot,
|
|
"./B05_Profile_Profile_Straight": straightJudge,
|
|
"./B05_Profile_Profile_CurveLaw": curveLaw,
|
|
});
|
|
const carry = loadTs("B05_Profile/logic/B05_Profile_Grade_Carry.ts", {
|
|
"./B05_Profile_UI_Profile_Alignment": alignment,
|
|
});
|
|
const ground = loadTs("B05_Profile/logic/B05_Profile_Grade_Ground.ts", {
|
|
"./B05_Profile_Profile_CurveLaw": curveLaw,
|
|
"./B05_Profile_UI_Profile_Alignment": alignment,
|
|
});
|
|
const grade = loadTs("B05_Profile/logic/B05_Profile_Pipeline_Grade.ts", {
|
|
"./B05_Profile_Profile_CurveLaw": curveLaw,
|
|
"../../common_util/common_util_route_follow": {},
|
|
"./B05_Profile_UI_Profile_Alignment": alignment,
|
|
"./B05_Profile_Grade_Carry": carry,
|
|
"./B05_Profile_Sections_Stations": { SECTION_STATION_INTERVAL_M: 20 },
|
|
"./B05_Profile_Grade_Ground": ground,
|
|
});
|
|
|
|
const curve = loadTs("B05_Profile/logic/B05_Profile_UI_RouteEdit_Curve.ts");
|
|
const edits = loadTs("B05_Profile/logic/B05_Profile_UI_RouteEdit_Edits.ts", {
|
|
"./B05_Profile_UI_RouteEdit_Curve": curve,
|
|
});
|
|
const frame = loadTs("B05_Profile/logic/B05_Profile_UI_RouteEdit_Frame.ts");
|
|
const fit = loadTs("B05_Profile/logic/B05_Profile_RouteEdit_StationFit.ts", {
|
|
"./B05_Profile_UI_RouteEdit_Curve": curve,
|
|
"./B05_Profile_UI_RouteEdit_Edits": edits,
|
|
"./B05_Profile_UI_RouteEdit_Frame": frame,
|
|
});
|
|
|
|
/** 측점 스냅 켠 노선 — 저장 노선(맞춤) · 교점 하나 옮긴 뒤 맞춤(편집기와 같은 `fitCurveStations`). */
|
|
function snapFit(item) {
|
|
const n = item.nodes.length;
|
|
const cols = (planned) => ({
|
|
planned,
|
|
curveOn: Array(n).fill(true),
|
|
curveRadius: Array(n).fill(null),
|
|
curveLock: Array(n).fill(null),
|
|
curveArc: Array(n).fill(null),
|
|
apexLock: Array(n).fill(false),
|
|
});
|
|
const line = (planned) =>
|
|
curve.buildEditedPolyline(
|
|
planned,
|
|
Array(n).fill(true),
|
|
Array(n).fill(null),
|
|
item.limits.minRadiusM,
|
|
).vertices;
|
|
const old = fit.fitCurveStations(cols(item.nodes), item.limits, 1);
|
|
const moved = old.map((p, i) => (i === item.index ? item.to : p));
|
|
const now = fit.fitCurveStations(cols(moved), item.limits, Math.max(1, item.index - 1));
|
|
return { old_nodes: old, new_nodes: now, old_line: line(old), new_line: line(now) };
|
|
}
|
|
|
|
const input = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
|
const routeOf = (line) => {
|
|
const chainage = [0];
|
|
for (let i = 1; i < line.length; i += 1)
|
|
chainage.push(
|
|
chainage[i - 1] + Math.hypot(line[i][0] - line[i - 1][0], line[i][1] - line[i - 1][1]),
|
|
);
|
|
return { line, curves: [], chainage, lengthM: chainage[chainage.length - 1] };
|
|
};
|
|
|
|
/** follow 단계를 노선 걸음 차례대로 — 각 걸음 뒤 편집(묶음이 입력에 넣는 것과 같음). */
|
|
function walk(item) {
|
|
let stations = [];
|
|
const stage = grade.createFollowStage({ savedLine: () => null, stationsAt: () => stations });
|
|
const planline = { policy: item.policy, basePvi: item.base_pvi };
|
|
let alignmentNow = item.edits;
|
|
let before = routeOf(item.ref_line);
|
|
const out = [];
|
|
for (const step of item.steps) {
|
|
stations = step.stations;
|
|
const now = routeOf(step.line);
|
|
const patch = stage({
|
|
inputs: { route: { nodes: [] }, pipes: null, structures: null, alignment: alignmentNow },
|
|
out: { route: now },
|
|
before: { route: before, planline },
|
|
});
|
|
if (patch.alignment) alignmentNow = patch.alignment;
|
|
out.push(alignmentNow);
|
|
before = now;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
const result = {
|
|
match: input.match.map((item) => {
|
|
const fn = carry.lineMatcher(item.old_xy, item.new_xy, item.tol);
|
|
return item.queries.map((t) => fn(t));
|
|
}),
|
|
base: input.base.map((item) =>
|
|
carry.carryBase(item.old_xy, item.new_xy, item.old_base, item.ground_pvi),
|
|
),
|
|
edits: input.edits.map((item) =>
|
|
carry.carryEdits(item.old_xy, item.new_xy, item.old_base, item.edits, item.stations, item.half),
|
|
),
|
|
follow: input.follow.map(walk),
|
|
snap: (input.snap ?? []).map(snapFit),
|
|
};
|
|
process.stdout.write(JSON.stringify(result));
|