fix(458 B06 갈아끼움): 떠난 측점 옛 자리에 새 측점이 서면 새 카드 번호 · 초안 사본 표시 안 들고 감 · 노선 밖 관은 시종점 카드 이름 안 가져감

- 구간 끝 · 관 측점이 격자 자리를 떠나면 그 자리 새 일반 측점과 번호 겹침(station_000000080000 둘) — 옛 자리에 다른 측점이 서면 새 번호 · 아니면 옛 번호(433-4 MC)
- 갈아끼움이 preview 표시를 들고 가면 다음 측점 맞춤이 그 측점을 버림(시점 측점 사라짐 실측) — 지움
- 시험: test_458 카드 번호 · preview 둘 더

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-18 22:56:52 +09:00
co-authored by Claude Opus 5.5
parent 37a738aff4
commit 0dad7f792e
4 changed files with 37 additions and 4 deletions
+5 -2
View File
@@ -136,9 +136,12 @@ export async function withDraftShapes(
const inputs = await statusOf(projectId, pipesDraft, structuresDraft);
if (!inputs) return;
const sections = detail.cross_sections as unknown as Array<Record<string, unknown>>;
warnMisses(projectId, attachShapes(sections, ...inputs));
const misses = attachShapes(sections, ...inputs);
warnMisses(projectId, misses);
if (swapped) {
const [pipes, structures, types] = inputs;
const [all, structures, types] = inputs;
// 노선 밖 관은 시종점 카드 이름도 안 가져감
const pipes = all.filter((pipe) => !misses.outside.includes(pipe.chainage_m));
const planned = planStations(pipes, structures, types, registryDefaults(types));
for (const section of [...detail.cross_sections, ...(detail.longitudinal?.stations ?? [])]) {
const hit = planned.find((station) => near(station.chainage_m, section.chainage_m));
+13 -2
View File
@@ -11,7 +11,8 @@
* · 0.5 m 넘게 옮긴 관 · 구조물 측점 = 기본 설계 + 사용자 칸(만진 칸 · 지반 등 입력 `ROW_CARRY_KEYS`) — 서버가
* 그 행을 지우고 기본 설계에 [저장] patch 를 얹음(`sync_cross_section_rows` · `compute_default_designs` · 428) ·
* patch 가 그 칸을 모두 실음(`crossPatchesFromCache` · 445 H2).
* · 격자 아닌 자리 = 앞 횡단의 `grid` 표시를 지움 · 카드 이름은 형상 다시 얹기가 계획 제목으로(458).
* · 격자 아닌 자리 = 앞 횡단의 `grid` 표시 · 초안 사본 표시(`preview`)를 지움 · 카드 이름은 형상 다시 얹기가
* 계획 제목으로(458).
* · 빠진 측점 = 목록에서 뺌(세션 고침은 열쇠로 남음 · 노선이 바뀐 때만 「노선 밖 고침」 · 부르는 쪽).
* · 옛 상세는 그대로 둠 — 새 객체를 돌려줌(쥔 쪽이 갈아끼움).
*
@@ -215,8 +216,16 @@ export function swapCrossSections(
kind === "irregular" &&
!!intervalM &&
Math.abs(station.chainageM / intervalM - Math.round(station.chainageM / intervalM)) < 1e-6;
// 옮긴 측점의 옛 자리에 다른 측점이 섬(격자 자리를 떠난 구간 끝 등) = 새 번호 — 옛 번호를 들고 가면 그 자리 새
// 측점 번호(`stationIdOf`)와 겹침(458) · 안 겹치면 옛 번호(MC 이동 · 433-4)
const vacated =
!!prior &&
Math.abs(prior.chainage_m - station.chainageM) >= 0.01 &&
stations.some(
(other) => other !== station && Math.abs(other.chainageM - prior.chainage_m) < 0.0005,
);
const place = {
station_id: prior?.station_id ?? stationIdOf(station.chainageM),
station_id: prior && !vacated ? prior.station_id : stationIdOf(station.chainageM),
chainage_m: station.chainageM,
label: formatStation(station.chainageM),
kind,
@@ -247,6 +256,8 @@ export function swapCrossSections(
...(reseed ? { design: reseeded(prior.design, uphill) } : {}),
};
if (!onGrid) delete next.grid; // 격자 자리를 떠난 시설 측점 — 옛 숨은 일반 측점 표시를 안 들고 감(458)
// 묶음 측점 = 정본 목록 — 초안 맞춤의 「구조물 없는 사본」 표시를 안 들고 감(들고 가면 다음 맞춤이 그 측점을 버림)
delete next.preview;
return next;
});
const removed = detail.cross_sections
@@ -113,6 +113,7 @@ function run(L1, L2, pipes, structures) {
const S1 = SS.buildStations(L1, [], pipes, structures, { types, intervalM: INTERVAL });
const saved = { longitudinal: { stations: [], length_m: 0 }, cross_sections: sectionsFrom(S1) };
AT.attachShapes(saved.cross_sections, pipes, structures, types);
saved.cross_sections[0].preview = true; // 초안 맞춤의 구조물 없는 사본 표시(갈아끼움이 안 들고 감)
const pipesF = follow.projectPipePoints(pipes, xy(L2));
const structuresF = follow
.followStructures(structures, xy(L2), undefined, INTERVAL)
@@ -132,6 +133,8 @@ function run(L1, L2, pipes, structures) {
const stale = swap.detail.cross_sections.map((s) => ({ ...s }));
const staleMisses = AT.attachShapes(stale, pipes, structures, types);
return {
ids: swap.detail.cross_sections.map((s) => [s.chainage_m, s.station_id]),
preview: swap.detail.cross_sections.filter((s) => s.preview).length,
pipes: pipesF.map((p) => [p.id, p.chainage_m]),
now: view(now),
misses,
+16
View File
@@ -4,6 +4,8 @@
- 시점 당김(재현 A 꼴): 관 · 세월교 그림 = 제 측점 하나씩(따라간 누가거리) · 격자를 떠난 관 측점 `grid` 지움.
- 가게 초안 없는 채(저장분 현황) 얹어도 옛 자리 세월교가 이웃 일반 측점에 안 붙음(상한 0.51 m · 측점 없음).
- 시점 밖으로 밀린 관(재현 D 꼴) = 시점 측점에 안 얹고 「노선 밖」.
- 카드 번호 안 겹침 — 격자 자리(80 m)를 떠난 옹벽 끝 측점은 새 번호(그 자리 새 일반 측점과 따로) ·
초안 사본 표시(`preview`)는 안 들고 감.
Node 헬퍼 `helper_458_swap_attach.cjs` 가 진짜 모듈(따라감 · 측점 · 갈아끼움 · 형상 얹기)을 부름.
"""
@@ -64,3 +66,17 @@ def test_시점_밖_관은_안_얹고_노선_밖(out):
assert outside["misses"] == {"outside": [0], "far": []}
assert _shaped(outside["now"]) == {100: {"ford": 100}}
assert {s["chainage_m"]: s["grid"] for s in outside["now"]}[20] is True # 격자 자리 옹벽 측점
def test_카드_번호_안_겹침_떠난_끝_측점은_새_번호(out):
ids = dict(out["shift"]["ids"])
assert len(set(ids.values())) == len(ids)
assert ids[87] == "station_000000087000" # 옹벽 끝 80 → 87
assert ids[80] == "station_000000080000" # 그 자리 새 일반 측점
assert ids[47] == "station_000000047000" # 관도 격자 40 을 떠남
assert ids[40] == "station_000000040000"
assert ids[157] == "station_000000150000" # 옛 자리가 빈 세월교 측점 = 옛 번호 그대로
def test_초안_사본_표시_안_들고_감(out):
assert out["shift"]["preview"] == 0 and out["outside"]["preview"] == 0