Files
Aislo/resources/tester/helper_498_2_grade_touch.cjs
T
eomsangdonandClaude Opus 5.5 b92fa06f34 fix(B05·B06): 노선 고친 [저장] 계획고 = 서버 옮김 위에 사용자 손댐만 (PLAN 498-2 · 요구 411~417)
- 브라우저 — 노선 바뀐 [저장]의 계획고 본문 = 묶음 계획고 대 저장분 옮김(같은 carryEdits · 이 초안 첫 기준)의 다른 열쇠 · 지운 열쇠만(`gradeTouch` · `routeGradeTouch`) · 못 가르면 옛 길(노선 0 칸 통째)
- 서버 — `alignment_cleared` 가 오면 관 · 구조물 · 추가 측점 차례 뒤 새 종단 편집(서버 옮김) 위에 손댐만 얹어 한 번 저장(`touched_alignment` · 얹기 실패도 되돌림 안)
- follow 옮김 측점 = stations 단계와 같은 추가 측점(`plus` 넘김)
- 재현: 꼬리 추가 측점 ▲ 뒤 앞 교점 끌기 [저장] = 990 m 편집 버림 · ▲ 잃음 · 최대 −0.50 m → 고친 뒤 버림 0 · 끌 때 = 저장 뒤
- 시험: test_498_2_grade_touch 새로 · helper_design_store_save 에 묶음 창구 가짜 하나(옛 길)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:51:29 +09:00

145 lines
4.8 KiB
JavaScript

/* 498-2 노선 바뀐 [저장] 계획고 손댐 — 진짜 TS(`logic/B05_Profile_Pipeline_Grade` follow · `gradeTouch`)를
* Node 로 돌림. 표준출력 JSON 한 벌.
*/
const path = require("path");
const { makeLoader } = require("./helper_ts_stub_loader.cjs");
const ROOT = path.join(__dirname, "..", "..");
function loadTs(file, stubs = {}) {
return makeLoader({
root: ROOT,
cache: false,
requireFor: (full) => (request) => {
if (stubs[request]) return stubs[request];
throw new Error(`stub 없음: ${request} (${file})`);
},
}).loadTs(path.join(ROOT, file));
}
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 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": loadTs("B05_Profile/logic/B05_Profile_Grade_Seat.ts", {
"./B05_Profile_UI_Profile_Alignment": alignment,
}),
"./B05_Profile_UI_Profile_Alignment": alignment,
"./B05_Profile_Grade_Carry": carry,
"./B05_Profile_Sections_Stations": { SECTION_STATION_INTERVAL_M: 20 },
"./B05_Profile_Grade_Ground": loadTs("B05_Profile/logic/B05_Profile_Grade_Ground.ts", {
"./B05_Profile_Profile_CurveLaw": curveLaw,
"./B05_Profile_UI_Profile_Alignment": alignment,
}),
});
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] };
};
/** 10 m 격자 + 끝 — 묶음 stations 단계 자리. */
const gridOf = (route) => {
const out = [];
for (let s = 0; s < route.lengthM - 1e-6; s += 10) out.push(Math.round(s * 1000) / 1000);
return [...out, Math.round(route.lengthM * 1000) / 1000];
};
const SAVED_LINE = [
[0, 0],
[100, 0],
[100, 100],
[100, 200],
];
const STEPS = [
[
[0, 0],
[95, 0],
[100, 100],
[100, 200],
],
[
[0, 0],
[90, 0],
[100, 100],
[100, 200],
],
];
const BASE = [0, 100, 200, 300].map((chainage_m) => ({ chainage_m, elevation_m: 500 }));
const SAVED = { station_offsets: { "250.000": 0.5 }, curve_radii: {} };
/** 저장 노선에서 노선 걸음 둘 — 걸음 사이 `between` 이 사용자 계획고 조작(묶음 입력 고침). */
function run(between, after) {
const stage = grade.createFollowStage({
savedLine: () => null,
stationsAt: (route) => gridOf(route),
});
const planline = { policy: { station_interval_m: 20 }, basePvi: BASE };
let now = SAVED;
let before = routeOf(SAVED_LINE);
let route = before;
STEPS.forEach((line, index) => {
route = routeOf(line);
const patch = stage({
inputs: { route: { nodes: [] }, pipes: null, structures: null, alignment: now },
out: { route },
before: { route: before, planline },
});
if (patch.alignment) now = patch.alignment;
if (index === 0 && between) now = between(now);
before = route;
});
if (after) now = after(now);
const reader = {
input: (key) => ({ route: { nodes: [] }, alignment: now })[key] ?? null,
output: (key) => (key === "route" ? route : null),
};
return { now, touch: stage.touch(SAVED, reader) };
}
const bump = (key, by) => (edits) => ({
...edits,
station_offsets: { ...edits.station_offsets, [key]: (edits.station_offsets[key] ?? 0) + by },
});
const drop = (edits) => {
const offsets = { ...edits.station_offsets };
delete offsets[Object.keys(offsets).sort()[0]];
return { ...edits, station_offsets: offsets };
};
process.stdout.write(
JSON.stringify({
plain: run(null, null),
lateBump: run(null, bump("60.000", 0.3)),
midBump: run(bump("40.000", 0.2), null),
removed: run(null, drop),
noFirst: grade.createFollowStage({ savedLine: () => null }).touch(SAVED, {
input: () => null,
output: () => null,
}),
pure: grade.gradeTouch(
{ station_offsets: { "10.000": 1, "20.000": 2.5 }, curve_radii: { "30.000": 80 } },
{ station_offsets: { "10.000": 1, "20.000": 2, "40.000": 3 }, curve_radii: {} },
),
}),
);