- 암 경계 · 암 절토 경사 저장소(`B06_Section_UI_Page_Persist`) = 세션 칸이 주인 — 읽을 때마다 측점 열쇠 칸(`readCells`) · 고칠 때 지금 칸을 읽어 그 측점만 바꿔 씀(`putCell`) · 메모리 맵 · load · 갈아끼움 다시 읽기(keyedStores · reloadKeyedStores) 걷음 · ↶ 뒤 다음 조작이 되돌림을 덮던 흠(N5) 고침 - 저장 재료 = `offsets()` · `ratios()`(새 Map) · ↶ ↷ 가 rockb · cutslope 칸을 바꾸면 조정창과 같은 onRestore 로 카드 · 면적 다시 - B05 상단측 메모리 맵(uphillOverrides · load/saveUphillOverrides) 걷음 — 읽는 곳 없이 저장 뒤 비우기만 하던 사본 · 값 주인 = 세션 칸 `uphill` · 상단측 차례 = 기본 조각 그대로 - 노선 바뀐 [저장] 성공 뒤(페이지 다시 세운 뒤) 옛 노선 번호의 노선 범위 초안(`clearRouteDrafts`) · `route-draft-hold` 비움 · 넘김 규칙 그대로 · 같은 번호면 노선 칸 안 지움 · 실패면 그대로 - 시험 test_472_5r_rock_owner 7(옛 코드 빨강 5) · 옛 단언 고침(176_3 · 271 · 443 상단측 조각 · 344_3 저장소 load) - 위키 암선 · 암 경사 · ↶ ↷ 규칙 · 노선 저장 뒤 정리 줄 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
205 lines
7.3 KiB
JavaScript
205 lines
7.3 KiB
JavaScript
/* 344-3 · 346-2 2파 헬퍼 — vite ssr 로 묶은 진짜 모듈(helper_344_3_entry.ts)을 부르고 결과를 JSON 한 벌로.
|
|
* node helper_344_3_station_keys.mjs <묶음.mjs> <입력.json>
|
|
* 입력 = { standard: 파이썬 config 표준단면, policy: 종단 정책 }.
|
|
*/
|
|
import { readFileSync } from "node:fs";
|
|
import { pathToFileURL } from "node:url";
|
|
|
|
const store = () => {
|
|
const m = new Map();
|
|
return {
|
|
getItem: (k) => (m.has(k) ? m.get(k) : null),
|
|
setItem: (k, v) => m.set(k, String(v)),
|
|
removeItem: (k) => m.delete(k),
|
|
key: (i) => [...m.keys()][i] ?? null,
|
|
get length() {
|
|
return m.size;
|
|
},
|
|
clear: () => m.clear(),
|
|
};
|
|
};
|
|
globalThis.window = globalThis;
|
|
globalThis.sessionStorage = store();
|
|
globalThis.localStorage = store();
|
|
const any = new Proxy(function () {}, {
|
|
get: (_t, k) => (k === Symbol.toPrimitive ? () => 0 : any),
|
|
apply: () => any,
|
|
construct: () => any,
|
|
});
|
|
globalThis.document = any;
|
|
globalThis.requestAnimationFrame = (run) => setTimeout(run, 0);
|
|
|
|
const [bundlePath, inputPath] = process.argv.slice(2);
|
|
const m = await import(pathToFileURL(bundlePath).href);
|
|
const input = JSON.parse(readFileSync(inputPath, "utf8"));
|
|
const P = "p344";
|
|
const R = 7;
|
|
const R2 = 8;
|
|
const raw = (name, route) => m.readState(name, P, route) ?? {};
|
|
const keysOf = (name, route) =>
|
|
Object.keys(raw(name, route))
|
|
.filter((k) => !k.startsWith("@")) // 「@at」 · 「@seat」 = 쓴 때 자리 칸(475-3)
|
|
.sort();
|
|
const section = (chainage_m, kind, extra = {}) => ({
|
|
station_id: `s${chainage_m}`,
|
|
chainage_m,
|
|
kind,
|
|
label: String(chainage_m),
|
|
samples: [],
|
|
...extra,
|
|
});
|
|
const detailOf = (sections) => ({
|
|
longitudinal: {
|
|
stations: sections,
|
|
profile_alignment: { policy: { station_interval_m: 20 } },
|
|
},
|
|
cross_sections: sections,
|
|
});
|
|
const choice = (ground) => ({
|
|
ground_type: ground,
|
|
section_mode: "right_cut",
|
|
ditch_side: "right",
|
|
ditch_type: "l_type",
|
|
paved: true,
|
|
two_stage_slope: true,
|
|
ditch_choice: false,
|
|
});
|
|
const out = {};
|
|
|
|
/* 1. 옛 초안(누가거리 열쇠) → 측점 열쇠로 옮김 · 지금 측점에 없는 것은 남겨 둠 */
|
|
const oldRoute = detailOf([
|
|
section(0, "bp"),
|
|
section(20, "regular"),
|
|
section(40, "regular"),
|
|
section(45.054, "irregular", { structure: "파형강관 D800", culvert: {} }),
|
|
section(52.5, "irregular", { structure: "대피로" }),
|
|
section(60, "regular"),
|
|
section(70, "ep"),
|
|
]);
|
|
m.useStructureIds(P, [{ structure_id: "st-1", chainage_m: 52.5 }]);
|
|
m.useStationKeys(P, R, oldRoute);
|
|
m.writeState(
|
|
"crossdesign",
|
|
{
|
|
"20.00": choice("soil"),
|
|
45.05: choice("blasting_rock"),
|
|
"52.50": choice("soil"),
|
|
"60.00": choice("soil"),
|
|
"99.00": choice("soil"),
|
|
},
|
|
P,
|
|
R,
|
|
);
|
|
out.legacyRead = [...m.crossDesignChoices(P, R).keys()].sort((a, b) => a - b);
|
|
out.legacyKeys = keysOf("crossdesign", R);
|
|
|
|
/* 2. 쓰기 = 측점 열쇠 · 누가거리로 다시 읽음 · 암 경계 저장소도 같은 열쇠 */
|
|
m.writeCrossDesignChoice(P, R, 40, choice("ripping_rock"));
|
|
m.writeShoulderExtend(P, R, 40, "left", 0.5);
|
|
out.writeKey = keysOf("crossdesign", R).includes("grid:2");
|
|
out.shoulderKeys = keysOf("shoulderext", R);
|
|
const rock = m.createRockBoundaryStore({
|
|
sessionKey: () => m.stateKey("rockb", P, R),
|
|
detail: () => oldRoute,
|
|
refreshCard: () => undefined,
|
|
recompute: () => undefined,
|
|
});
|
|
rock.control.adjust(20, -0.3);
|
|
out.rockKeys = keysOf("rockb", R);
|
|
out.rockLoaded = Object.fromEntries(rock.offsets());
|
|
out.rockSession = m.readRockBoundarySession(P, R);
|
|
|
|
/* 3. 구조물 id 를 아직 모를 때(첫 열기) — 쓴 때 자리(@at)로 그 측점 고침을 읽음 */
|
|
m.useStructureIds(P, []);
|
|
m.useStationKeys(P, R, oldRoute);
|
|
out.hintKeyNow = m.keyAt(P, R, 52.5);
|
|
out.hintRead = m.crossDesignChoices(P, R).has(52.5);
|
|
|
|
/* 4. 노선이 바뀜 — 측점 열쇠로 넘김 · 넘김 = 카드 선택 넷 · 암 경계 · 노견 확장(417) · 사라진 측점 = 노선 밖 */
|
|
m.writeState("pipe-ids", { next: 2, ids: [{ id: "pipe-s1", at: 47 }] }, P); // 관이 47 m 로 옮김
|
|
m.useStructureIds(P, [{ structure_id: "st-1", chainage_m: 55 }]);
|
|
m.useStationKeys(
|
|
P,
|
|
R2,
|
|
detailOf([
|
|
section(0, "bp"),
|
|
section(20, "regular"),
|
|
section(40, "regular"),
|
|
section(47, "irregular", { structure: "파형강관 D800", culvert: {} }),
|
|
section(55, "irregular", { structure: "대피로" }),
|
|
section(58, "ep"),
|
|
]),
|
|
);
|
|
// 옛 노선 목록(st-1 은 52.5 · 관 장부는 옛 자리 기준으로 이미 열쇠가 섬)
|
|
out.outside = m.carryStationDrafts(P, R, R2);
|
|
out.carriedKeys = keysOf("crossdesign", R2);
|
|
out.carriedChoice = raw("crossdesign", R2)["grid:1"];
|
|
out.carriedRead = [...m.crossDesignChoices(P, R2).keys()].sort((a, b) => a - b);
|
|
out.carriedRock = m.readRockBoundarySession(P, R2);
|
|
out.carriedShoulder = keysOf("shoulderext", R2);
|
|
|
|
/* 5. cross 단계 — 가짜 입력 · 같은 입력 0 · 측점 하나 고침 = 1 · 범위 밖 재사용 */
|
|
const grid = [0, 20, 40, 60, 80, 100];
|
|
const groundAt = (c) => 100 + 0.05 * c - 3 * Math.sin(c / 17);
|
|
const base = {
|
|
policy: input.policy,
|
|
onlyExplicitCurves: false,
|
|
basePvi: grid.map((c, i) => ({ chainage_m: c, elevation_m: groundAt(c) + (i % 2 ? 0.8 : -0.6) })),
|
|
chainage: Array.from({ length: 101 }, (_, i) => i),
|
|
ground: Array.from({ length: 101 }, (_, i) => groundAt(i)),
|
|
stations: grid.map((c) => ({ station_id: `s${c}`, chainage_m: c })),
|
|
};
|
|
const profile = m.buildAlignment(base, m.emptyEdits());
|
|
const side = (c, sign) =>
|
|
Array.from({ length: 24 }, (_, i) => ({
|
|
offsetM: (i + 1) * 0.5,
|
|
z: groundAt(c) + sign * 0.3 * (i + 1) * 0.5,
|
|
}));
|
|
const ground = {
|
|
stations: grid.map((c, i) => ({
|
|
key: i === 0 ? "end:BP" : i === grid.length - 1 ? "end:EP" : `grid:${i}`,
|
|
chainageM: c,
|
|
x: c,
|
|
y: 0,
|
|
azimuthDeg: 90,
|
|
left: [0, 1],
|
|
kind: i === 0 || i === grid.length - 1 ? "end" : "grid",
|
|
id: null,
|
|
ground: { centerZ: groundAt(c), left: side(c, 1), right: side(c, -1) },
|
|
planRadiusM: null,
|
|
curveOuterSide: null,
|
|
curveWideningM: null,
|
|
})),
|
|
longitudinal: { chainageM: [], z: [] },
|
|
};
|
|
const samples = m.groundSamples(ground.stations[1]);
|
|
out.samplesSorted = samples.every((s, i) => i === 0 || s.offset_m > samples[i - 1].offset_m);
|
|
out.samplesCount = samples.length;
|
|
const { stage, counts } = m.createCrossStage();
|
|
const ctxOf = (cross, range, prev, prof = profile) => ({
|
|
generation: 1,
|
|
range,
|
|
signal: new AbortController().signal,
|
|
inputs: { route: null, alignment: null, cross, pipes: null, structures: null },
|
|
out: { profile: prof, ground },
|
|
before: {},
|
|
prev,
|
|
});
|
|
const crossInput = { standard: input.standard, byKey: { "grid:2": { groundType: "soil" } } };
|
|
const first = stage(ctxOf(crossInput, null, null));
|
|
out.crossFirst = { ...counts(), keys: Object.keys(first.byKey).length };
|
|
out.crossHasArea = Object.values(first.byKey).every((r) => Number.isFinite(r.cutAreaM2));
|
|
stage(ctxOf(crossInput, null, first));
|
|
out.crossSame = counts();
|
|
const touched = { ...crossInput, byKey: { ...crossInput.byKey, "grid:3": { groundType: "soil" } } };
|
|
const third = stage(
|
|
ctxOf(touched, { sources: ["cross"], fromM: 60, toM: 60, keys: ["grid:3"] }, first),
|
|
);
|
|
out.crossOne = counts();
|
|
out.crossOneChanged = third.byKey["grid:3"].detail.ground_type;
|
|
const edited = m.buildAlignment(base, { station_offsets: { "40.000": 0.4 }, curve_radii: {} });
|
|
stage(ctxOf(touched, { sources: ["grade"], fromM: 20, toM: 60, keys: [] }, third, edited));
|
|
out.crossGrade = counts();
|
|
|
|
process.stdout.write(JSON.stringify(out));
|