From 0dad7f792e35afbc2a4eb2b142395504ab295233 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 18 Oct 2026 22:56:52 +0900 Subject: [PATCH] =?UTF-8?q?fix(458=20B06=20=EA=B0=88=EC=95=84=EB=81=BC?= =?UTF-8?q?=EC=9B=80):=20=EB=96=A0=EB=82=9C=20=EC=B8=A1=EC=A0=90=20?= =?UTF-8?q?=EC=98=9B=20=EC=9E=90=EB=A6=AC=EC=97=90=20=EC=83=88=20=EC=B8=A1?= =?UTF-8?q?=EC=A0=90=EC=9D=B4=20=EC=84=9C=EB=A9=B4=20=EC=83=88=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EB=B2=88=ED=98=B8=20=C2=B7=20=EC=B4=88=EC=95=88=20?= =?UTF-8?q?=EC=82=AC=EB=B3=B8=20=ED=91=9C=EC=8B=9C=20=EC=95=88=20=EB=93=A4?= =?UTF-8?q?=EA=B3=A0=20=EA=B0=90=20=C2=B7=20=EB=85=B8=EC=84=A0=20=EB=B0=96?= =?UTF-8?q?=20=EA=B4=80=EC=9D=80=20=EC=8B=9C=EC=A2=85=EC=A0=90=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EC=95=88=20=EA=B0=80=EC=A0=B8?= =?UTF-8?q?=EA=B0=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 구간 끝 · 관 측점이 격자 자리를 떠나면 그 자리 새 일반 측점과 번호 겹침(station_000000080000 둘) — 옛 자리에 다른 측점이 서면 새 번호 · 아니면 옛 번호(433-4 MC) - 갈아끼움이 preview 표시를 들고 가면 다음 측점 맞춤이 그 측점을 버림(시점 측점 사라짐 실측) — 지움 - 시험: test_458 카드 번호 · preview 둘 더 Co-Authored-By: Claude Opus 5.5 --- B06_Section/B06_Section_Section_Drafts.ts | 7 +++++-- B06_Section/logic/B06_Section_Station_Swap.ts | 15 +++++++++++++-- resources/tester/helper_458_swap_attach.cjs | 3 +++ resources/tester/test_458_swap_attach.py | 16 ++++++++++++++++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/B06_Section/B06_Section_Section_Drafts.ts b/B06_Section/B06_Section_Section_Drafts.ts index 22dad9da0..d0d9fc6f3 100644 --- a/B06_Section/B06_Section_Section_Drafts.ts +++ b/B06_Section/B06_Section_Section_Drafts.ts @@ -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>; - 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)); diff --git a/B06_Section/logic/B06_Section_Station_Swap.ts b/B06_Section/logic/B06_Section_Station_Swap.ts index 0fffb9258..26bffa0c3 100644 --- a/B06_Section/logic/B06_Section_Station_Swap.ts +++ b/B06_Section/logic/B06_Section_Station_Swap.ts @@ -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 diff --git a/resources/tester/helper_458_swap_attach.cjs b/resources/tester/helper_458_swap_attach.cjs index 4f6921462..887902e35 100644 --- a/resources/tester/helper_458_swap_attach.cjs +++ b/resources/tester/helper_458_swap_attach.cjs @@ -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, diff --git a/resources/tester/test_458_swap_attach.py b/resources/tester/test_458_swap_attach.py index 5ec3b1fcc..a946a7a45 100644 --- a/resources/tester/test_458_swap_attach.py +++ b/resources/tester/test_458_swap_attach.py @@ -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