- 배수유역 머리 [자동 제안] → 고름 창(⚙ 창 틀) ① 전체 자동(all) ② 지정 포함 추가(add) · 낡은 동안 끔 - [배관 배치 초기화] — 저장분 즉시 지움 없앰 · 관 목록 없이 셈(세류선 교차만) · 초안 - 셋 다 답이 오면 세션 초안 + 사진첩 한 걸음(400 ms 넘은 답도 제 걸음) - 제안 목록 [적용] — 관 자리 후보는 안 더함(점선 원 · 목록 그대로) · 관경 · 시설 제안 없으면 꺼짐 - computeDetailBasins(points | null, auto) · 글 = 툴팁 두 값 고침 · 새 글 끝에 새 키 - 시험 — 349 흐름에 자동 제안 · 347_2 · 298 · 312 고쳐 씀 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC34fsxxbweEG3RUjs3Mn
81 lines
3.6 KiB
TypeScript
81 lines
3.6 KiB
TypeScript
/* =============================================================================
|
|
* B05_Profile/logic/B05_Profile_Drainage_Suggest.ts
|
|
* 배수유역 제안(347-3) — 관경 · 시설 종류 · 관 자리 후보를 모으고 [적용] 때 관경 · 시설만 관 초안에 얹음.
|
|
* 순수 함수만(화면 = `B05_Profile_UI_Drainage_Suggest.ts`).
|
|
*
|
|
* - 관경 = 유역 추천이 배관이고 추천 관경이 지금 관경과 다를 때(「Ø→Ø 배관 제안」).
|
|
* - 시설 = 유역 추천이 BOX암거 · 세월교이고 지금 관이 그 종류가 아닐 때.
|
|
* - 관 자리 = 서버 `suggest_pipe_positions`(간격 보충 규칙 · 기존 관에서 먼 곳) — 보이기만 · 더하기는
|
|
* [자동 제안] ②(369 · 서버가 새 관에 등록부 기본값).
|
|
* - 자동 바꿈 없음 — 모으기만 하고 바꾸는 것은 `applySuggestions`([적용])뿐.
|
|
* ========================================================================== */
|
|
|
|
/** 관 자리가 같은 유역으로 볼 거리(m) — 화면 `PIPE_MATCH_M` 과 같은 값. */
|
|
const SAME_PIPE_M = 0.5;
|
|
|
|
interface PipeLike {
|
|
chainage_m: number;
|
|
source?: string;
|
|
facility?: string;
|
|
options?: Record<string, string | number>;
|
|
}
|
|
|
|
interface BasinLike {
|
|
chainage_m: number;
|
|
recommended_facility?: string;
|
|
recommended_diameter_mm?: number | null;
|
|
}
|
|
|
|
export type Suggestion =
|
|
| { kind: "diameter"; chainageM: number; from: number | null; to: number }
|
|
| { kind: "facility"; chainageM: number; from: string; to: string }
|
|
| { kind: "position"; chainageM: number };
|
|
|
|
const near = (a: number, b: number): boolean => Math.abs(a - b) < SAME_PIPE_M;
|
|
|
|
/** 응답(유역 · 관 자리 후보) + 지금 관 목록 → 제안 목록(관 자리 차례). */
|
|
export function collectSuggestions(
|
|
pipes: ReadonlyArray<PipeLike>,
|
|
basins: ReadonlyArray<BasinLike>,
|
|
candidates: ReadonlyArray<{ chainage_m: number }>,
|
|
): Suggestion[] {
|
|
const found: Suggestion[] = [];
|
|
basins.forEach((basin) => {
|
|
const pipe = pipes.find((item) => near(item.chainage_m, basin.chainage_m));
|
|
if (!pipe) return;
|
|
const facility = pipe.facility ?? "pipe";
|
|
const want = basin.recommended_facility ?? "pipe";
|
|
if (want !== "pipe" && facility !== want) {
|
|
found.push({ kind: "facility", chainageM: pipe.chainage_m, from: facility, to: want });
|
|
return;
|
|
}
|
|
const to = basin.recommended_diameter_mm;
|
|
const raw = pipe.options?.pipe_diameter_mm;
|
|
const from = raw === undefined || raw === "" ? null : Number(raw);
|
|
if (want === "pipe" && facility === "pipe" && to != null && from !== to)
|
|
found.push({ kind: "diameter", chainageM: pipe.chainage_m, from, to });
|
|
});
|
|
candidates
|
|
.filter((item) => !pipes.some((pipe) => near(pipe.chainage_m, item.chainage_m)))
|
|
.forEach((item) => found.push({ kind: "position", chainageM: item.chainage_m }));
|
|
return found.sort((a, b) => a.chainageM - b.chainageM);
|
|
}
|
|
|
|
/** [적용] — 제안을 관 초안에 한 번에(관경 · 시설 바꿈 · 관 자리는 안 더함 · 369). 입력 목록은 안 고침. */
|
|
export function applySuggestions<P extends PipeLike>(
|
|
pipes: ReadonlyArray<P>,
|
|
suggestions: ReadonlyArray<Suggestion>,
|
|
): P[] {
|
|
const next = pipes.map(
|
|
(pipe) => ({ ...pipe, options: pipe.options && { ...pipe.options } }) as P,
|
|
);
|
|
suggestions.forEach((item) => {
|
|
if (item.kind === "position") return;
|
|
const pipe = next.find((candidate) => near(candidate.chainage_m, item.chainageM));
|
|
if (!pipe) return;
|
|
if (item.kind === "facility") pipe.facility = item.to;
|
|
else pipe.options = { ...(pipe.options ?? {}), pipe_diameter_mm: item.to };
|
|
});
|
|
return next;
|
|
}
|