Files
Aislo/B05_Profile/B05_Profile_UI_RouteEdit_SnapStep.ts
T
eomsangdonandClaude Sonnet 5.5 1288e21e55 feat(756-1): 추천 선 진하게 · 스냅 한 아이콘 · 추천노선 여럿 고르기 · 켠 동안 등고선 띠 넓힘
- 예정노선 수정 켬에도 추천 선 불투명 그대로(Render)
- 스냅 + 스냅 간격 = 고르기 메뉴 하나(측점 No. · 10 m · 끔) · 도구줄 표 snap-step 줄 걷음
- 추천노선 = 여럿 고르기(공용 부품 multi · 줄 색 점 PickMenuItem.color · 끔 줄 없음) · 선 색 --draw-map-rec-1~4 · 예정노선 가이드 선은 켬끔 한 줄
- 켠 동안 등고선 띠 = LAS 는 자르지 않음 · 도엽은 계획선 + 예정노선으로 넓힘(캔버스 · GL · 도엽 다시 받기)
- 시험 624_2 · 634_2 · 626_1 · 636_2 · 390_4 · b05_routeedit_initial 을 새 꼴로 고침 · 새 시험 756_1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-02 22:58:28 +09:00

100 lines
4.9 KiB
TypeScript

/* =============================================================================
* B05_Profile_UI_RouteEdit_SnapStep.ts
* 노선 편집 스냅 고르기 하나(390-2 · 요구 242-11 · 589-4) — 측점 No. · 10 m · 끔(끔 = 스냅 꺼짐).
*
* 간격 = 10 m · 측점 No.(편집 창 측점 간격 · 기본 · 280-4) — 곡선 MC 를 그 간격 배수에 맞춤.
* 켬이면 저장 노선에도 같이(`station_snap_step` · 390-5 · 서버 측점 셈이 MC 측점 값 자릿수로 씀 · 옛 노선 = 측점 No.).
* 저장 노선이 스냅 켬이면 열 때 그 간격으로 고르기를 맞춤. 추천 · 서버 초기 노선은 측점 No. 그대로.
* 고르기 = 도구줄 고르기 메뉴 하나(634-3 · `chrome.snap`) · 처음 = 끔.
* 상태줄 글은 고른 간격 따라(390-4) — 측점 No. = 「일반측점에」 · 10 m = 「10 m 배수에」.
* 평면 눈금 = 일반측점(스냅이 옮기지 않음 · 280-4) + 곡선 측점(MC 에 측점 없을 때 · 252-2 · `ticks`).
* ========================================================================== */
import type { RouteDraft } from "../A00_Common/design/logic/design_pipeline_types";
import type { RouteEditChrome } from "./B05_Profile_UI_RouteEdit_Chrome";
import type { EditedCurve, Vertex } from "./logic/B05_Profile_UI_RouteEdit_Curve";
import { curveSpans, curveStations, stationsAlong } from "./logic/B05_Profile_UI_RouteEdit_Frame";
/** 고르기 값 — 옛 "0.1" · "1"(pref · 저장)은 측점 No. 로 읽음(280-4). */
const STEPS = ["10", "station"];
/** 저장 · 초안에 실리는 스냅 두 칸. */
export type SnapSetting = Pick<RouteDraft, "stationSnap" | "stationSnapStep">;
type SavedSnap = { station_snap?: boolean; station_snap_step?: number | null };
/** 스냅 뒤 상태줄 글 — `step` = 고르기 값("10" · "station"). */
export function snapFitText(step: string): string {
const where = step === "station" ? "일반측점" : `${step} m 배수`;
return `곡선 가운데(MC)를 ${where}에 맞췄습니다.`;
}
export type SnapStep = {
/** 지금 스냅 간격(m) — 측점 No. 면 측점 간격. */
(): number;
/** 스냅 켬인가(고르기가 끔이 아님). */
on: () => boolean;
/** 스냅 뒤 상태줄 글. */
text: () => string;
/** 저장 · 초안 두 칸 — 끔이면 간격 null. */
draft: () => SnapSetting;
/** 저장 노선을 읽음 — 켬이면 고르기를 그 간격으로 · 돌려줌 = 켬끔. */
load: (plan: SavedSnap) => boolean;
/** 읽은 저장 노선과 켬끔 · 간격이 다른가([확인] 보낼지). */
changed: () => boolean;
/** 편집 창 일반측점 — 끔이면 null · 켬이면 측점 간격 그대로(옮기지 않음 · 280-4). */
stations: (line: ReadonlyArray<Vertex>, curves?: ReadonlyArray<EditedCurve>) => number[] | null;
/** 평면 눈금 — `stations` + 곡선 측점(252-2) · 곡선 측점이 없으면 `stations` 그대로. */
ticks: (
line: ReadonlyArray<Vertex>,
curves: ReadonlyArray<EditedCurve>,
) => { stations: number[] | null; curveStations: number[] };
};
/** 고르기 배선 — 고름(켬 · 간격 · 끔) 때 `onChange()`. */
export function bindStationSnap(
chrome: Pick<RouteEditChrome, "snap">,
stationIntervalM: number,
onChange: () => void,
): SnapStep {
// 고르기 메뉴 하나(634-3) — 값 = "10" · "station" · 끔 null
const menu = chrome.snap;
const value = (): string => menu.value() ?? "station";
menu.onPick(() => onChange());
const on = () => menu.value() !== null;
const step = (): number | null => (value() === "station" ? null : Number(value()));
const draft = (): SnapSetting => ({ stationSnap: on(), stationSnapStep: on() ? step() : null });
let loaded: SnapSetting = { stationSnap: false, stationSnapStep: null };
const stations = (line: ReadonlyArray<Vertex>) =>
on() ? stationsAlong(line, stationIntervalM, false) : null;
const stepM = () => step() ?? stationIntervalM;
return Object.assign(stepM, {
on,
text: () => snapFitText(value()),
draft,
load: (plan: SavedSnap) => {
const snapOn = plan.station_snap === true;
const kept = String(plan.station_snap_step ?? "station");
menu.set(snapOn ? (STEPS.includes(kept) ? kept : "station") : null);
loaded = { stationSnap: snapOn, stationSnapStep: snapOn ? step() : null };
return snapOn;
},
changed: () => {
const now = draft();
return (
now.stationSnap !== loaded.stationSnap || now.stationSnapStep !== loaded.stationSnapStep
);
},
stations,
ticks: (line: ReadonlyArray<Vertex>, curves: ReadonlyArray<EditedCurve>) => {
const base = stationsAlong(line, stationIntervalM, false);
// 노선 길이 = 무한 — 호(BC~EC)는 늘 노선 안
const curve = curveStations(base, curveSpans(line, curves), Infinity, on() ? step() : null);
return {
stations: curve.length ? [...base, ...curve].sort((a, b) => a - b) : stations(line),
curveStations: curve,
};
},
});
}