Files
Aislo/B05_Profile/logic/B05_Profile_Structure_Follow.ts
T
eomsangdonandClaude Opus 5.5 ccccb9f527 fix(B05 구조물 · 관 자리): 등고선 좌표 따라감 · 관 짝 id · B06 관 열쇠 pipe (410 · 411-B · 411-D)
- 좌표 낡음 판정 — 좌표가 그 누가거리를 안 낳으면(마커 · 방향키 · 폼으로 누가거리만 옮김) 노선 위 자리로 다시 적음 · TS placeOnLine ↔ 파이썬 fill_structure_coordinates 짝(보낸 좌표가 누가거리를 낳으면 살림)
- 따라감 단계 — 좌표 없는 관 초안은 앞 따라감 좌표 · site 간격 넘김
- 배수 편집기 — 노선 초안 follow 가 다시 매긴 관 자리를 id 짝으로 받음(시설 속성 · 고름 함께) · 초안 버림 = 저장분 다시 읽음
- 관 짝 차례 → id — 관 알림에 고정 id · followPipeReports · shownPipes(movePipeIds 한 번에 옮김) · followedMarks 같은 자리 먼저
- B06 측점 열쇠 — 서버 관 id 도 pipe: (계산 묶음과 같은 갈래)
- 시험 test_410_follow_xy · test_411_b_pipe_id_pairing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
2026-10-18 09:26:18 +09:00

70 lines
3.1 KiB
TypeScript

/* =============================================================================
* B05_Profile_Structure_Follow.ts
* 노선 초안 동안 종단 알약 자리(PLAN 346-2 덧 3) — 계산 묶음 follow 가 좌표로 따라간 관 · 구조물의 새
* 누가거리를 알약 목록에 얹음. 구조물 = structure_id 로 짝 · 관 알약 id = 세션 id(`pipeIdAt` · 자리 장부)라
* 묶음 관 id(서버 고정 id)와 다르면 같은 자리(장부가 따라간 자리 = 그 관 · 411-B) → 남은 것끼리 누가거리 차례.
*
* 순수 — 화면 · fetch · 세션 없음.
* ========================================================================== */
interface Placed {
chainage_m?: number | null;
start_m?: number | null;
end_m?: number | null;
}
interface Mark extends Placed {
structure_id?: string | null;
}
interface Moved extends Placed {
id?: string | null;
structure_id?: string | null;
}
/** 같은 관으로 볼 자리 차이(m) — `PIPE_MATCH_M`(design_selection) 같은 값. */
const SAME_PIPE_M = 0.51;
const list = (value: unknown): Moved[] => (Array.isArray(value) ? (value as Moved[]) : []);
const at = (item: Placed): number => item.chainage_m ?? item.start_m ?? 0;
/** 알약 목록 → 따라간 자리를 얹은 사본 · 짝 없는 알약은 그대로. `structures` · `pipes` = 묶음 입력(follow 뒤). */
export function followedMarks<T extends Mark>(
marks: ReadonlyArray<T>,
structures: unknown,
pipes: unknown,
): T[] {
const byId = new Map<string, Moved>();
for (const item of list(structures)) if (item.structure_id) byId.set(item.structure_id, item);
const movedPipes = list(pipes).filter((pipe) => typeof pipe.chainage_m === "number");
for (const pipe of movedPipes) if (pipe.id) byId.set(pipe.id, pipe);
const loose = marks
.filter((mark) => mark.structure_id?.startsWith("pipe-") && !byId.has(mark.structure_id))
.sort((a, b) => at(a) - at(b));
const ids = new Set(marks.map((mark) => mark.structure_id));
let free = movedPipes.filter((pipe) => !ids.has(pipe.id)).sort((a, b) => at(a) - at(b));
const paired = new Map<T, Moved>();
for (const mark of loose) {
const same = free.find((pipe) => Math.abs(at(pipe) - at(mark)) < SAME_PIPE_M);
if (!same) continue;
paired.set(mark, same);
free = free.filter((pipe) => pipe !== same);
}
// ponytail: 남은 것은 차례 짝 — 알약이 옛 자리인 채 노선 고쳐 관 차례가 뒤바뀌면 틀림
const rest = loose.filter((mark) => !paired.has(mark));
if (rest.length === free.length) rest.forEach((mark, index) => paired.set(mark, free[index]));
return marks.map((mark) => {
const moved = (mark.structure_id ? byId.get(mark.structure_id) : undefined) ?? paired.get(mark);
if (!moved) return mark;
const shift = at(moved) - at(mark);
const move = (value: number | null | undefined, next: number | null | undefined) =>
value == null ? value : (next ?? value + shift);
return {
...mark,
chainage_m: move(mark.chainage_m, moved.chainage_m),
start_m: move(mark.start_m, moved.start_m),
end_m: move(mark.end_m, moved.end_m),
};
});
}