Appearance
Use Case #5: Удаление промежуточной точки (Route Auto-Healing)
Исходное состояние: У пользователя есть тщательно проложенный маршрут из 5 точек: А(Старт) → Б → В → Г → Д(Финиш). Действие: Пользователь понимает, что заезд в точку В (дальнее кафе) ему не нужен. Он удаляет точку В (через список или меню на карте).
1. Интент (Намерение пользователя)
"Я хочу вырезать этот кусок пути. Пожалуйста, соедини предыдущую точку (Б) и следующую (Г) самым оптимальным маршрутом напрямую. При этом категорически запрещаю трогать участки А → Б и Г → Д — я потратил полчаса на их ручную настройку!"
2. Идеальная логика системы (Хирургическая изоляция)
Снова работает правило «Якорей»:
- Идентификация соседей: Система определяет соседей удаляемой точки: Б и Г. Они становятся жесткими якорями.
- Блокировка графа: Участки А → Б и Г → Д «замораживаются» в памяти.
- Локальный пересчет: Запрос на прокладку НОВОГО маршрута уходит только для сегмента Б → Г.
- Очистка мусора (Важнейший нюанс!): Если на участках Б → В или В → Г ранее были наставлены «мягкие» точки (shaping points), они должны каскадно удалиться вместе с точкой В. Если система их оставит, новый маршрут Б → Г всё равно будет делать нелепую петлю к невидимым координатам.
3. Визуальный отклик и интерфейс (UX Сращивания)
Мгновенное перестроение без анимации вызывает «слепоту к изменениям» (change blindness).
- Фаза Hover (Предотвращение ошибки): При наведении на «Удалить», точка В и прилегающие отрезки (Б → В и В → Г) подсвечиваются красным (или становятся полупрозрачными). Юзер видит "радиус поражения".
- Фаза "Призрачный след" (Ghost Track): По клику точка В исчезает. Старая петля (Б → В → Г) становится бледно-серой пунктирной линией ("фантомный след").
- Фаза Стягивания (Rubber-band): За доли секунды рассчитывается новый прямой путь Б → Г и отрисовывается плавной анимацией. Серые "призраки" плавно растворяются (Fade out).
- Динамика UI:
- Карточка удаленной точки схлопывается, нумерация пересчитывается.
- Дистанция откручивается назад (анимация счетчика).
- График высот плавно "сжимается", вырезая горб удаленного сегмента.
4. Антипаттерны (Как делать НЕ надо)
- ❌ Глобальный пересчет (Эффект домино): Самый страшный грех. Оптимизация всего трека от старта до финиша уничтожает часы ручной работы пользователя.
- ❌ Маршрут-франкенштейн (Разрыв): Оставление двух несвязанных кусков (А → Б и Г → Д). Маршрут всегда должен оставаться непрерывным графом.
- ❌ Удаление вне экрана без фокуса: Если удаление произошло через панель слева, а точка была далеко за экраном, система пересчитает всё "втихаря". Камера обязана сделать плавный "Pan & Zoom", чтобы показать юзеру новый сросшийся участок Б → Г.
5. Edge Cases (Граничные случаи)
- Удаление Старта (А) или Финиша (Д):
- Решение: Никакого пересчета и обращения к серверу. Просто мгновенно "отрезаем хвост/голову" из локальной памяти. Соседняя точка автоматически становится новым Стартом/Финишем.
- Удаление точки в тупике (Out & Back аппендикс):
- Юзер построил маршрут А → Б → В(тупик на горе) → Б(возврат) → Г. И удаляет В.
- Решение: Система вырезает весь этот аппендикс целиком, не пытаясь строить маршрут Б → Б, оставляя чистый транзит А → Б → Г.
- Географический барьер (Удаление "Моста"):
- Точки Б и Г на разных берегах реки, В — на мосту. Юзер удалил В. Ближайший другой мост в 100 км.
- Решение: Маршрут "срастается", но его длина увеличивается на 200 км. Жизненно важен Toast-уведомление (⚠️ Дистанция увеличена на 200 км из-за поиска новой переправы) и доступная кнопка Undo.
Эпики и Задачи (Epics & Tasks)
Декомпозиция Use Case #5 для разработки.
Epic 1: Изолированный пересчет и Очистка мусора (Core Auto-Healing)
- Task 1.1: Каскадное удаление (Garbage Collection). При удалении
Waypoint, функция-редьюсер вplannerStoreдолжна найти все промежуточные точки сtype: 'shaping', лежащие на удаляемых сегментах (Б → В и В → Г), и удалить их из массива вместе с точкой В. - Task 1.2: Изоляция сегментов. Функция запроса к API должна отправлять роутеру только пару координат
[Сосед 1, Сосед 2](Б и Г), не передавая весь остальной трек. Полученная новая геометрия вставляется между старыми, замороженными сегментами. - Task 1.3: Обработка Start/Finish & Out-and-back. Написать алгоритм определения граничных случаев:
index === 0(Старт) илиindex === length - 1(Финиш) -> только локальное удаление (splice).- Выявление тупиковых сегментов (
[x, y, x]) -> удаление сегмента целиком без вызова API.
Epic 2: Анимации сращивания (Ghost Track & Rubber-banding)
- Task 2.1: Hover-превью ("Радиус поражения"). При событии
onMouseEnterна кнопке корзины в списке точек, вызывать экшен, который подсвечивает (меняет цвет или добавляет свечение/opacity) на удаляемые слои геометрии в MapLibre. - Task 2.2: Ghost Track. При
onClick(удаление), перевести удаляемые сегменты геометрии в отдельный временный слой (серая пунктирная линия). - Task 2.3: Cross-fade анимация. После получения нового сросшегося сегмента Б → Г от API, анимированно отрисовать его, одновременно плавно уводя в прозрачность (
fade out) слой "Ghost Track".
Epic 3: Синхронизация UI (Smart Viewport & Metrics)
- Task 3.1: Умная камера (Pan & Zoom). Перед удалением вычислить Bounding Box нового сегмента Б → Г. Если он не виден на экране (т.к. юзер удалял из списка), инициировать
fitBoundsилиflyTo, чтобы сфокусировать внимание на месте сращивания. - Task 3.2: Анимация Height Profile. Обновить компонент
ElevationProfileтак, чтобы удаление куска данных происходило не рывком, а плавным "схлопыванием" графиков по оси X (через D3.js или Framer Motion). - Task 3.3: Географические барьеры (Alerts). Если новая дистанция (Б → Г) превышает сумму старых дистанций (Б → В + В → Г) более чем на заданный коэффициент (например, +200%), триггерить Toast "Внимание: значительное увеличение дистанции".