Files
Aislo/resources/tester/helper_400_profile_carry.cjs
T
eomsangdonandClaude Opus 5.5 22bf1e9822 fix(459 노선 바뀜): [저장] 구조물 초안을 서버 따라감 위에 · 추가 측점 초안 동안 좌표 따라감 · 옮긴 시설 측점 427 꼭짓점 세움 · 관 따라감 선 = points
- 구조물: 노선 저장 뒤 rebaseStructureDraft(관 rebasePipeDraft 짝) — 따라가기만 한 것 = 서버 자리 · 사용자가 옮긴 것 = 초안
- 추가 측점: follow 단계가 좌표로 옮긴 정수 m 를 patch.plusStations 로(FOLLOW_KEYS · stations 단계 입력값) · followPlusDraft(가게 쓰기 · 초안 버림 되돌림 · page 부름은 sub7) · follow 만 쓴 초안은 [저장]이 PUT 안 함
- 427: 초안 planline · 서버 _carry_profile 이 바뀐 구간 관 · 구조물 측점(+ 초안 중 사용자가 옮긴 것)에 seatBornNodes 로 다시 세움(route_moved_stations · seat_route_moved 짝)
- 서버 관 따라감 = 구조물과 같은 points(사용 범위 자름) · 좌표 없는 옛 관은 옛 노선 자리로 먼저 · 따라감 실패 = follow_failed 알림
- 시험 test_459_route_seat_mirror(거울 · 임시 storage · _place_pipes) · 헬퍼 400 · 449 · design_store_save 가짜 창구

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:50:43 +09:00

140 lines
5.5 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 seat = loadTs("B05_Profile/logic/B05_Profile_Grade_Seat.ts", {
"./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": {},
"../../common_util/common_util_structure_station": {
PLUS_OWNER_PREFIX: "plus-",
SAME_STATION_M: 0.005,
},
"./B05_Profile_Grade_Seat": seat,
"./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));