/* ============================================================================= * 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( marks: ReadonlyArray, structures: unknown, pipes: unknown, ): T[] { const byId = new Map(); 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(); 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), }; }); }