- 새 `B05_Profile_Engine_RouteInitial_Follow.merged_follow` — 예정노선 폴리라인 → 큰 R(widen) → 꺾임만 곡선 → 같은 쪽 합치기(merge_same_side · 노선 횡단 토공 +10 % 멈춤) → 곡선 MC 일반측점 맞춤 · 잼 = 토공 최소와 같은 Guard(지형 · 계류 격자) · 같은 입력 기억 - 한 함수 — 체인 처음 · /route/plan · /route/replan(`initial_land` · 초기선 낡을 때만 지형 읽음) · /route/follow(`follow_land`) · 추천 「예상노선 추적」(follow_line · 묶음 격자 재사용) - 지형 못 읽으면 옛 길(순수 기하) · 기존 확정 노선 · 초기선 · 저장본은 안 바꿈(다음 체인 · 끌 때부터) - 화면 — 따라가기 뒤 지금 추천 = 예상노선 추적(「토공 최소」 꼬리표 남던 흠) · 걸음 사진에 실려 되돌리기 짝 - 엔진 합치기 잣대 셈(cross_total · cross_delta)은 새 파일 한 벌로 옮김(엔진 줄 줄어듦) - 시험: test_758_1 새 지킴 · test_383_1 은 체인 초기선도 같은 지형을 넘기게 고침(추천 추적 = 곡선 최적이라 순수 기하와 달라짐) · test_626_3 은 onFollow 새 꼴로 글 대조 고침 - 검증_sub1: 곡선 25 → 16 · 측점 16/16 · 예정노선과 최대 10.6 m · 끌 때 1.4~1.9 s Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
105 lines
4.4 KiB
TypeScript
105 lines
4.4 KiB
TypeScript
/* =============================================================================
|
|
* B05_Profile_UI_RouteEdit_Initial.ts
|
|
* 계획노선 편집의 **지금 추천 이음**(계획서 20 · 23-2 · 626-1) — [저장]에 실을 지금 추천 · 편집 걸음 짝.
|
|
*
|
|
* 추천 고르기(옛 좌측 「노선 추천」 칸 · 모달 · [적용] = `as_initial`)는 걷음(626-1 · 요구 530 · 결정 D1 · D8) —
|
|
* 추천은 평면 도구줄 미리보기(`_RecPreview`)만 · 초기값 = 예정노선 추적(자동설계 체인 · [초기화] 그대로).
|
|
* 지금 추천 = 서버가 적어 둔 것(`current_initial` · 없으면 예정노선 추적) · 옛 캐시(`routeedit-initial`)는
|
|
* 서버와 같을 때만 남음 · [저장]은 그 값 그대로 실음(서버 `initial_choice.json`).
|
|
* ========================================================================== */
|
|
|
|
import type {
|
|
RouteInitialCriteria,
|
|
RouteInitialLogic,
|
|
RouteInitialWeights,
|
|
} from "./B05_Profile_Api_RouteInitial";
|
|
import type { RouteEditSnapshot } from "./B05_Profile_UI_RouteEdit_History";
|
|
import {
|
|
choiceWithProfile,
|
|
readInitialChoice,
|
|
sameBase,
|
|
serverBase,
|
|
writeInitialChoice,
|
|
type InitialBase,
|
|
type InitialChoice,
|
|
} from "./B05_Profile_UI_RouteEdit_Initial_Model";
|
|
|
|
export interface InitialPanelParams {
|
|
projectId: string;
|
|
}
|
|
|
|
export interface InitialPanel {
|
|
/** 노선을 읽었다 — 창을 연 편집값 · 서버가 적어 둔 현재 초기값(`current_initial`). */
|
|
ready: (opening: RouteEditSnapshot, server?: unknown) => void;
|
|
/** [저장]에 실어 보낼 지금 추천. */
|
|
current: () => {
|
|
mode: RouteInitialLogic;
|
|
weights: RouteInitialWeights | null;
|
|
criteria: RouteInitialCriteria | null;
|
|
with_profile: boolean;
|
|
};
|
|
/** 지금 노선이 종단을 반영하는가 — 지금 추천의 「종단 반영」 값(421 · 옛 기록이면 갈래로).
|
|
* 종단 띠 · [확인]이 함께 본다(추천을 손으로 고쳤어도 그 값대로). */
|
|
withProfile: () => boolean;
|
|
/** 지금 추천 — 편집 걸음(사진)에 실어 둔다. 되돌리기 · 다시하기가 `restoreTag` 로 되살린다. */
|
|
tag: () => InitialChoice | null;
|
|
restoreTag: (tag: unknown) => void;
|
|
/** 예정노선 따라가기(758-1) 걸음의 꼬리표 — 지금 추천 = 「예상노선 추적」(그 선 · 종단 반영 값 그대로). */
|
|
followed: (snapshot: RouteEditSnapshot) => InitialChoice;
|
|
/** [취소] — 이 창에서 바꾼 추천을 창을 열 때 것으로 되돌린다. */
|
|
cancel: () => void;
|
|
/** [확인] 뒤 결과 캐시를 비웠다 — 추천은 새 노선의 출발점이라 다시 적는다. */
|
|
keep: () => void;
|
|
}
|
|
|
|
export function createInitialPanel(params: InitialPanelParams): InitialPanel {
|
|
const { projectId } = params;
|
|
|
|
/** 창을 열 때 캐시에 있던 추천 — [취소]가 돌아갈 자리. */
|
|
let openingChoice = readInitialChoice(projectId);
|
|
let choice: InitialChoice | null = openingChoice;
|
|
/** 노드 없이 이름만 아는 현재 추천 — 서버가 적어 둔 것(없으면 예상노선 추적). */
|
|
let base: InitialBase = { logic: "follow", weights: null, criteria: null };
|
|
|
|
return {
|
|
ready(_snapshot, server) {
|
|
// 「현재 추천」은 서버가 [확인] 때 적어 둔 것이 정본이다 — 캐시가 다르면(확인 없이 닫음)
|
|
// 캐시를 버린다.
|
|
if (server) {
|
|
base = serverBase(server);
|
|
if (choice && !sameBase(choice, base)) choice = null;
|
|
openingChoice = choice;
|
|
writeInitialChoice(projectId, choice);
|
|
}
|
|
},
|
|
current: () => {
|
|
const now = choice ?? base;
|
|
const criteria = choice ? choice.criteriaOverrides : base.criteria;
|
|
return {
|
|
mode: now.logic,
|
|
weights: now.weights,
|
|
criteria,
|
|
with_profile: choiceWithProfile(now),
|
|
};
|
|
},
|
|
withProfile: () => choiceWithProfile(choice ?? base),
|
|
tag: () => choice,
|
|
restoreTag(tag) {
|
|
if (tag === undefined) return; // 추천을 안 실은 옛 걸음 — 그대로 둔다.
|
|
choice = tag as InitialChoice | null;
|
|
writeInitialChoice(projectId, choice);
|
|
},
|
|
followed: (snapshot) => ({
|
|
logic: "follow",
|
|
weights: null,
|
|
snapshot,
|
|
metrics: null,
|
|
criteria: null,
|
|
criteriaOverrides: null,
|
|
withProfile: choiceWithProfile(choice ?? base),
|
|
}),
|
|
cancel: () => writeInitialChoice(projectId, openingChoice),
|
|
keep: () => writeInitialChoice(projectId, choice),
|
|
};
|
|
}
|