Files
Aislo/B05_Profile/B05_Profile_UI_RouteEdit_Initial.ts
T
eomsangdonandClaude Opus 5.5 c187f30e8e feat(758-1): 예정노선 따라가기에도 토공 최소 곡선 최적 — 큰 R · 같은 쪽 곡선 합치기 · 직접 잼 측점 맞춤
- 새 `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>
2026-11-03 08:38:17 +09:00

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),
};
}