mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-01 12:18:51 +00:00
v1.59.0-beta.10: unify device visuals and wall refinements
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
# Единая индикация состояния и активности устройств
|
||||
|
||||
Статус: **утверждено владельцем и реализовано локально 2026-08-05**.
|
||||
|
||||
## Цель
|
||||
|
||||
Заменить четыре пересекающиеся механики — цвет подложки, пользовательскую
|
||||
пульсацию, motion/presence-эффекты и движение штор — одной семантической
|
||||
системой. Визуал должен отвечать на три разных вопроса:
|
||||
|
||||
1. Доступно ли устройство.
|
||||
2. В каком устойчивом состоянии оно находится.
|
||||
3. Что оно делает прямо сейчас или что только что произошло.
|
||||
|
||||
## Режимы отображения
|
||||
|
||||
В UI остаются:
|
||||
|
||||
1. **Значок** — иконка, морфинг и статусная подложка без обычных эффектов
|
||||
активности.
|
||||
2. **Значок + активность** — то же плюс семантический эффект активности.
|
||||
3. **Значение вместо значка** — числовая плашка со статусным цветом; обычная
|
||||
активность не рисуется.
|
||||
|
||||
Опция **«Только пульсация» удаляется**. Legacy `display: ripple` читается как
|
||||
`icon_ripple`, а при следующем сохранении конфигурации переписывается в
|
||||
`icon_ripple`. Backend временно продолжает принимать старое значение.
|
||||
|
||||
## Модель визуального состояния
|
||||
|
||||
Для маркера вычисляется один результат:
|
||||
|
||||
```ts
|
||||
availability: 'available' | 'unavailable'
|
||||
status: 'neutral' | 'open' | 'working' | 'alarm'
|
||||
activity: 'none' | 'event' | 'presence' | 'transition' | 'running'
|
||||
```
|
||||
|
||||
- `availability` отвечает за приглушение недоступного устройства;
|
||||
- `status` управляет подложкой и морфингом;
|
||||
- `activity` управляет кольцом или волнами;
|
||||
- тревога имеет высший приоритет;
|
||||
- недоступность подавляет обычную активность.
|
||||
|
||||
## Визуальный язык
|
||||
|
||||
| Смысл | Подложка | Эффект в «Значок + активность» |
|
||||
|---|---|---|
|
||||
| Доступно, простаивает | нейтральная | нет |
|
||||
| Выполняет основную функцию | жёлтая | медленное дыхание |
|
||||
| Открыто / разблокировано | оранжевая | только событие/переход, не постоянный эффект |
|
||||
| Разовое срабатывание | текущая статусная | три расходящиеся волны, 3,3 с |
|
||||
| Присутствие | нейтральная | спокойное статичное кольцо |
|
||||
| Механическое движение | текущая статусная | дыхание до конца движения |
|
||||
| Недоступно | приглушённая | нет |
|
||||
| Тревога (протечка, дым, газ, CO, safety/tamper/problem, сирена, сработавшая охрана) | красная | быстрая красная пульсация при любом display |
|
||||
|
||||
`prefers-reduced-motion` заменяет анимацию статичным кольцом.
|
||||
|
||||
## Значение жёлтой подложки
|
||||
|
||||
Жёлтый означает только **фактическое выполнение основной функции**:
|
||||
|
||||
- `light/switch/fan/humidifier = on`;
|
||||
- climate: `hvac_action = heating/cooling/drying/fan`, но не просто выбранный
|
||||
режим `heat/cool/auto`;
|
||||
- media player: `playing`;
|
||||
- vacuum: `cleaning` (и работающий механизм при `returning`);
|
||||
- бытовая техника: `running/washing/rinsing/spinning/drying/heating/cooking`;
|
||||
- маркер с `controls`: работает хотя бы одна управляемая цель.
|
||||
|
||||
Не являются работой: открытая дверь, разблокированный замок, открытые шторы,
|
||||
presence/motion, `enabled`, `home`, `unknown`, `unavailable`, climate `idle`.
|
||||
|
||||
В glow-режиме у реального источника света жёлтая подложка может быть скрыта:
|
||||
его устойчивым индикатором служит световое пятно. Эффект активности остаётся.
|
||||
|
||||
## Семантика активности
|
||||
|
||||
### Event, 3,3 секунды
|
||||
|
||||
- motion: только засвидетельствованный `off -> on`, не весь cooldown;
|
||||
- vibration/shock/sound;
|
||||
- открытие двери/окна (`off -> on`), но не всё время открытого состояния;
|
||||
- button/event;
|
||||
- успешный запуск script/scene/automation без устойчивого состояния.
|
||||
|
||||
Первое состояние после загрузки и восстановление из `unknown/unavailable` не
|
||||
создают ложного события. Повторный детект перезапускает окно и анимацию.
|
||||
|
||||
### Presence
|
||||
|
||||
`occupancy/presence = on` даёт статичное кольцо до исчезновения присутствия.
|
||||
|
||||
### Transition
|
||||
|
||||
- cover: `opening/closing`;
|
||||
- lock: `locking/unlocking`;
|
||||
- valve: `opening/closing`;
|
||||
- vacuum: `returning`.
|
||||
|
||||
Если промежуточного состояния нет, прямой конечный переход
|
||||
`closed <-> open` / `locked <-> unlocked` показывает transition 3,3 секунды.
|
||||
|
||||
### Running
|
||||
|
||||
Пока устройство фактически работает, его подложка жёлтая, а в режиме
|
||||
«Значок + активность» вокруг неё медленно дышит кольцо.
|
||||
|
||||
## Источник состояния
|
||||
|
||||
Приоритет:
|
||||
|
||||
1. Явный специализированный источник (в будущей расширенной настройке).
|
||||
2. Cover, когда tap action явно задан как «Открыть/закрыть».
|
||||
3. `controls` — агрегированное состояние всех управляемых целей.
|
||||
4. Функциональная сущность устройства / включённый light.
|
||||
5. Primary entity.
|
||||
|
||||
Критические сущности устройства проверяются независимо и не могут быть
|
||||
скрыты менее важным primary state.
|
||||
|
||||
## Приоритет вывода
|
||||
|
||||
1. alarm;
|
||||
2. unavailable;
|
||||
3. event;
|
||||
4. transition;
|
||||
5. presence;
|
||||
6. running;
|
||||
7. open;
|
||||
8. neutral.
|
||||
|
||||
## Инварианты
|
||||
|
||||
- Одна функция вычисляет подложку и activity; они не смотрят на разные
|
||||
сущности.
|
||||
- Обычная активность появляется только в `icon_ripple`.
|
||||
- Alarm отображается при любом display и не зависит от пользовательского цвета.
|
||||
- Hidden/ghost не получает live-чисел, морфинга, подложки или активности.
|
||||
- Static `houseplan-space-card` остаётся статической и не рисует live effects.
|
||||
- Пользовательские цвет и размер применяются к обычной активности, но не к
|
||||
красной тревоге.
|
||||
|
||||
## Реализация
|
||||
|
||||
- Чистая классификация сущностей, агрегация и распознавание переходов:
|
||||
`src/device-visual.ts`.
|
||||
- Выбор эффективных источников маркера, runtime-окно 3,3 с, миграция legacy
|
||||
display и рендер: `src/houseplan-card.ts`.
|
||||
- Общий слой колец и reduced-motion варианты: `src/styles.ts`.
|
||||
- UI оставляет три режима; backend сохраняет `ripple` только как входное
|
||||
legacy-значение до завершения миграционного периода.
|
||||
- Unit-спецификация классификатора подготовлена в
|
||||
`test/device-visual.test.mjs`; браузерные smokes переведены на новые классы.
|
||||
@@ -0,0 +1,465 @@
|
||||
# Свободные цепочки стен и разная толщина при рисовании — ТЗ
|
||||
|
||||
Статус: **согласовано и отложено** (2026-08-05).
|
||||
|
||||
> Реализацию не начинать до отдельного прямого запроса владельца проекта.
|
||||
> Этот документ фиксирует требования и принятые продуктовые решения для
|
||||
> будущей реализации.
|
||||
|
||||
## 1. Цель
|
||||
|
||||
Добавить возможность:
|
||||
|
||||
1. Рисовать одну цепочку стен, задавая отдельную толщину каждому следующему
|
||||
сегменту.
|
||||
2. Оставлять на плане незамкнутые стены, не принадлежащие комнате или контуру.
|
||||
3. Переключаться на другие инструменты без удаления уже нарисованной
|
||||
незамкнутой цепочки.
|
||||
4. После возвращения в инструмент или перезагрузки продолжать сохранённую
|
||||
цепочку с одного из её концов.
|
||||
5. Замыкать цепочку с выбором результата:
|
||||
- создать комнату;
|
||||
- оставить замкнутую цепочку самостоятельными стенами.
|
||||
|
||||
## 2. Текущее состояние и архитектурная причина изменения
|
||||
|
||||
Сейчас комната является единственным первичным источником геометрии стен:
|
||||
|
||||
- контур хранится в `rooms[].poly`;
|
||||
- стены вычисляются из рёбер комнат;
|
||||
- `walls[]` хранит толщину рёбер, но не самостоятельную геометрию;
|
||||
- незаконченный `_path` является временным состоянием редактора;
|
||||
- смена инструмента очищает `_path`;
|
||||
- сервер удаляет старое legacy-поле `segments`;
|
||||
- статическая карточка, проёмы, привязки и расчёт видимой области получают
|
||||
геометрию стен преимущественно из комнат.
|
||||
|
||||
Поэтому функция должна вводить новую сохраняемую сущность. Простое сохранение
|
||||
текущего `_path` не решает рендер, восстановление после перезагрузки,
|
||||
посегментную толщину и интеграцию с остальными инструментами.
|
||||
|
||||
## 3. Термины
|
||||
|
||||
- **Свободная цепочка стен** — упорядоченная открытая или замкнутая ломаная,
|
||||
которая не принадлежит комнате.
|
||||
- **Сегмент** — отрезок между двумя соседними вершинами; у замкнутой цепочки
|
||||
последний сегмент соединяет последнюю вершину с первой.
|
||||
- **Активная цепочка** — цепочка, которую пользователь сейчас продолжает.
|
||||
- **Линейная стена** — существующий вариант стены без объёмного тела.
|
||||
- **Толстая стена** — стена с положительной толщиной в сантиметрах.
|
||||
- **Виртуальный участок** — существующий `open_span`. Отсутствие толщины само
|
||||
по себе не превращает стену в виртуальную.
|
||||
|
||||
## 4. Модель данных
|
||||
|
||||
В конфигурацию пространства добавить необязательное поле `wall_paths`:
|
||||
|
||||
```ts
|
||||
interface WallPathCfg {
|
||||
id: string;
|
||||
points: Array<[number, number]>;
|
||||
closed?: boolean;
|
||||
segments: Array<{
|
||||
cm?: number;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
Пример открытой цепочки:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "wp_01",
|
||||
"points": [[0.10, 0.20], [0.40, 0.20], [0.40, 0.55]],
|
||||
"segments": [{ "cm": 15 }, { "cm": 25 }]
|
||||
}
|
||||
```
|
||||
|
||||
Пример замкнутой самостоятельной цепочки:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "wp_02",
|
||||
"closed": true,
|
||||
"points": [[0.10, 0.20], [0.40, 0.20], [0.40, 0.55]],
|
||||
"segments": [{ "cm": 15 }, { "cm": 20 }, { "cm": 25 }]
|
||||
}
|
||||
```
|
||||
|
||||
Инварианты:
|
||||
|
||||
- открытая цепочка: `points.length >= 2` и
|
||||
`segments.length === points.length - 1`;
|
||||
- замкнутая цепочка: минимум три пригодные вершины и
|
||||
`segments.length === points.length`;
|
||||
- у замкнутой цепочки последний сегмент идёт от последней вершины к первой;
|
||||
- первая точка не дублируется в конце массива;
|
||||
- `cm` отсутствует для линейной стены без объёмного тела;
|
||||
- отсутствие `cm` не означает `open_span`;
|
||||
- соседние точки не совпадают;
|
||||
- `id` уникален в пределах пространства;
|
||||
- координаты нормализованы по тем же правилам, что и `rooms[].poly`;
|
||||
- старое корневое поле `segments` не возвращается и продолжает считаться
|
||||
legacy.
|
||||
|
||||
Толщина хранится внутри `wall_paths`, потому что существующий `walls[]`
|
||||
привязан к геометрии комнат и удаляет осиротевшие ключи при нормализации.
|
||||
|
||||
## 5. Рисование и посегментная толщина
|
||||
|
||||
### 5.1 Начало цепочки
|
||||
|
||||
1. Пользователь выбирает инструмент «Стены».
|
||||
2. Первый клик создаёт временную начальную точку.
|
||||
3. Второй клик создаёт первый сегмент и постоянную запись в `wall_paths`.
|
||||
4. Одиночная точка без сегмента не сохраняется и исчезает при смене
|
||||
инструмента или перезагрузке.
|
||||
|
||||
### 5.2 Значение поля толщины
|
||||
|
||||
Поле толщины означает **толщину следующего сегмента**:
|
||||
|
||||
- значение фиксируется при клике второй точки сегмента;
|
||||
- изменение поля не меняет уже созданные сегменты;
|
||||
- live-preview использует текущее значение;
|
||||
- пустое значение создаёт линейную стену;
|
||||
- конфиг всегда хранит сантиметры независимо от выбранной системы единиц;
|
||||
- допустимая толщина — 1–100 см;
|
||||
- нечисловое значение и значение вне диапазона показывают ошибку и не дают
|
||||
создать сегмент.
|
||||
|
||||
Пример: пользователь может последовательно создать сегменты 15 см, 25 см,
|
||||
линейный сегмент и снова 15 см в пределах одной цепочки.
|
||||
|
||||
## 6. Сохранение при смене инструмента
|
||||
|
||||
Как только создан первый сегмент:
|
||||
|
||||
- цепочка становится частью конфигурации пространства;
|
||||
- каждый следующий законченный сегмент сразу добавляется в локальную
|
||||
конфигурацию;
|
||||
- запись на сервер может оставаться debounce-записью;
|
||||
- перед сменой этажа, режима, пространства или размонтированием карточки
|
||||
ожидающая запись принудительно завершается;
|
||||
- переключение на другой инструмент прекращает активное рисование, но не
|
||||
удаляет цепочку;
|
||||
- цепочка остаётся видимой во всех предусмотренных режимах.
|
||||
|
||||
Активное состояние редактора не требуется хранить на сервере.
|
||||
|
||||
## 7. Продолжение цепочки
|
||||
|
||||
Принятое поведение:
|
||||
|
||||
- в той же сессии после возвращения в инструмент «Стены» автоматически
|
||||
продолжается последняя активная цепочка;
|
||||
- после перезагрузки пользователь выбирает конечную точку кликом;
|
||||
- конечные точки доступных цепочек подсвечиваются при наведении;
|
||||
- клик по последней точке продолжает цепочку в текущем направлении;
|
||||
- клик по первой точке разворачивает порядок точек и сегментных параметров,
|
||||
после чего продолжает цепочку с этого конца;
|
||||
- при продолжении поле толщины принимает значение крайнего сегмента выбранного
|
||||
конца, после чего пользователь может его изменить;
|
||||
- из середины сегмента и из внутренней вершины нельзя создавать ответвление.
|
||||
|
||||
### 7.1 Соединение двух цепочек
|
||||
|
||||
Разрешается соединение **конец с концом**:
|
||||
|
||||
- при необходимости одна или обе цепочки разворачиваются;
|
||||
- точки и параметры сегментов объединяются без потери соответствия;
|
||||
- одна исходная запись сохраняет `id`, вторая удаляется;
|
||||
- нулевой соединительный сегмент не создаётся;
|
||||
- соединение через середину сегмента не поддерживается;
|
||||
- если соединение приводит к замыканию, открывается диалог завершения,
|
||||
описанный ниже.
|
||||
|
||||
## 8. Замыкание цепочки
|
||||
|
||||
При клике по начальной точке активной цепочки с минимум тремя пригодными
|
||||
вершинами:
|
||||
|
||||
1. Проверяется замыкающий сегмент.
|
||||
2. Проверяются нулевая длина, самопересечения и недопустимые наложения.
|
||||
3. Открывается существующий диалог создания комнаты.
|
||||
4. Внизу диалога добавляется второе действие
|
||||
**«Оставить замкнутыми стенами»**.
|
||||
|
||||
До выбора результата исходная открытая цепочка остаётся в конфигурации, а
|
||||
замыкающий сегмент хранится как ожидающее локальное изменение. Это исключает
|
||||
потерю стен при отмене диалога или ошибке сохранения.
|
||||
|
||||
### 8.1 Основное действие: создать комнату
|
||||
|
||||
При обычном сохранении комнаты одной транзакцией:
|
||||
|
||||
- создаётся `room.poly`;
|
||||
- свободная цепочка удаляется из `wall_paths`;
|
||||
- толщина каждого сегмента переносится в существующую модель `walls[]`;
|
||||
- замыкающий сегмент получает толщину, выбранную перед замыканием;
|
||||
- если новый контур разделяет физическую стену с существующей комнатой,
|
||||
побеждает толщина уже существующей стены.
|
||||
|
||||
Одна физическая стена не должна иметь две конкурирующие толщины.
|
||||
|
||||
### 8.2 Второе действие: оставить замкнутыми стенами
|
||||
|
||||
При выборе «Оставить замкнутыми стенами»:
|
||||
|
||||
- комната не создаётся;
|
||||
- заливка, название и площадь не появляются;
|
||||
- замыкающий сегмент добавляется в `segments`;
|
||||
- цепочка сохраняется с `closed: true`;
|
||||
- активное рисование этой цепочки завершается.
|
||||
|
||||
Для продолжения замкнутой цепочки сначала потребуется удалить один из её
|
||||
сегментов. Перемещение вершин в первой версии не предусмотрено.
|
||||
|
||||
### 8.3 Отмена и ошибки
|
||||
|
||||
- отмена диалога возвращает пользователя к открытой сохранённой цепочке;
|
||||
- ошибка записи не удаляет исходный `wall_path`;
|
||||
- удаление `wall_path` и добавление комнаты выполняются атомарно в одном
|
||||
изменении локальной конфигурации.
|
||||
|
||||
## 9. Рендер
|
||||
|
||||
Свободные стены отображаются:
|
||||
|
||||
- в редакторе плана — всегда;
|
||||
- в остальных редакторах — по действующим правилам отображения границ;
|
||||
- в режиме просмотра — при включённом `show_borders`;
|
||||
- в основной и статической карточке.
|
||||
|
||||
### 9.1 Толстые стены
|
||||
|
||||
Для сегментов с толщиной необходимо:
|
||||
|
||||
- строить тело относительно центральной линии;
|
||||
- объединять тела свободных и комнатных стен перед отрисовкой;
|
||||
- использовать существующие заливку и штриховку стен;
|
||||
- корректно формировать L- и T-образные стыки;
|
||||
- объединять сегменты разной толщины без щелей;
|
||||
- ограничивать miter на острых углах и переходить к bevel;
|
||||
- завершать действительно свободный конец ровным поперечным срезом;
|
||||
- прятать окончание линейного сегмента под телом примыкающей толстой стены.
|
||||
|
||||
### 9.2 Виртуальные участки
|
||||
|
||||
Действующее правило сохраняется:
|
||||
|
||||
- в режиме просмотра виртуальный пунктир рисуется под телом стены;
|
||||
- в редакторах пунктир рисуется поверх тела полностью от центральной линии;
|
||||
- свободная линейная стена не является виртуальным участком.
|
||||
|
||||
### 9.3 Границы содержимого
|
||||
|
||||
`wall_paths` участвуют в расчёте видимой области с учётом половины толщины.
|
||||
План, состоящий только из свободных стен, должен корректно центрироваться и
|
||||
масштабироваться.
|
||||
|
||||
## 10. Влияние на комнаты, площадь и свет
|
||||
|
||||
До преобразования в комнату свободная цепочка:
|
||||
|
||||
- не создаёт заливку пола;
|
||||
- не создаёт площадь и подпись м²;
|
||||
- не создаёт название комнаты;
|
||||
- не связывается с HA area;
|
||||
- не изменяет внутренний контур существующей комнаты;
|
||||
- не вычитает площадь из комнаты;
|
||||
- не разделяет комнату на световые зоны;
|
||||
- не участвует в распространении света между комнатами;
|
||||
- не создаёт солнечные лучи или дверной тоннель.
|
||||
|
||||
Свободная стена является отображаемой физической геометрией, но не
|
||||
топологической границей помещения.
|
||||
|
||||
Автоматическое распознавание комнат из произвольного графа пересекающихся стен
|
||||
не входит в задачу. Комната создаётся только явным замыканием цепочки и выбором
|
||||
основного действия в диалоге.
|
||||
|
||||
## 11. Работа инструментов
|
||||
|
||||
### 11.1 Толщина стены
|
||||
|
||||
Инструмент позволяет выбрать сегмент свободной цепочки и изменить только его
|
||||
толщину. Действие «Применить ко всей цепочке» можно добавить позднее как
|
||||
необязательное улучшение.
|
||||
|
||||
### 11.2 Удаление
|
||||
|
||||
- единственный сегмент: удалить всю цепочку;
|
||||
- крайний сегмент: укоротить цепочку;
|
||||
- средний сегмент: разбить цепочку на две независимые цепочки;
|
||||
- одна часть сохраняет старый `id`, вторая получает новый;
|
||||
- часть без сегментов не сохраняется;
|
||||
- удаление сегмента замкнутой цепочки превращает её в открытую цепочку с
|
||||
`closed: false`.
|
||||
|
||||
### 11.3 Resize и перемещение вершин
|
||||
|
||||
Не входят в первую версию. Resize продолжает работать только с комнатами.
|
||||
Перемещение вершин свободных цепочек вынесено в следующий этап.
|
||||
|
||||
### 11.4 Merge и Split
|
||||
|
||||
Не считают свободные цепочки комнатами и не изменяют их автоматически.
|
||||
|
||||
### 11.5 Выравнивание
|
||||
|
||||
Глобальное выравнивание плана по сетке должно включать точки `wall_paths`, не
|
||||
нарушая соответствие сегментов и их толщин.
|
||||
|
||||
### 11.6 Привязка мебели
|
||||
|
||||
Свободные физические стены рекомендуется включить в привязку мебели, поскольку
|
||||
для пользователя они являются обычными стенами.
|
||||
|
||||
### 11.7 Двери и окна
|
||||
|
||||
Двери и окна устанавливаются **только в стены комнат**.
|
||||
|
||||
- свободные открытые и замкнутые цепочки не являются целью инструмента
|
||||
проёмов;
|
||||
- при наведении на них инструмент не показывает доступную привязку;
|
||||
- проёмы становятся доступны только после преобразования цепочки в комнату.
|
||||
|
||||
## 12. Undo, Escape и Reset
|
||||
|
||||
Рекомендуемая безопасная модель:
|
||||
|
||||
- `Ctrl+Z` или `Escape` удаляет последний сегмент, добавленный в текущей
|
||||
сессии редактирования;
|
||||
- при продолжении старой цепочки Undo не удаляет части, существовавшие до её
|
||||
выбора;
|
||||
- когда добавленных в сессии сегментов больше нет, следующее действие снимает
|
||||
активность с цепочки;
|
||||
- для новой цепочки отмена первого сегмента удаляет `wall_path`, оставляя
|
||||
временную стартовую точку;
|
||||
- следующая отмена удаляет стартовую точку.
|
||||
|
||||
Reset следует трактовать как «Отменить текущие изменения цепочки»:
|
||||
|
||||
- новая цепочка возвращается к состоянию до текущего рисования;
|
||||
- у продолженной цепочки удаляются только добавленные в этой сессии сегменты;
|
||||
- ранее сохранённая часть не удаляется;
|
||||
- полное удаление выполняется инструментом удаления.
|
||||
|
||||
## 13. Сохранение и конкурентные изменения
|
||||
|
||||
- каждый завершённый сегмент сразу попадает в локальную конфигурацию;
|
||||
- запись на сервер выполняется существующей сериализованной очередью;
|
||||
- ожидающий debounce принудительно завершается при уходе из контекста;
|
||||
- `activeWallPathId` является только состоянием редактора;
|
||||
- после перезагрузки продолжение начинается выбором конечной точки;
|
||||
- преобразование в комнату не должно сначала удалить цепочку и только потом
|
||||
пытаться сохранить комнату;
|
||||
- конфликты ревизий обрабатываются действующим механизмом конфигурации.
|
||||
|
||||
## 14. Серверная валидация и ограничения
|
||||
|
||||
Предлагаемые пределы:
|
||||
|
||||
- не более 500 цепочек на пространство;
|
||||
- не более 500 точек в одной цепочке;
|
||||
- не более 2000 свободных сегментов суммарно;
|
||||
- только конечные числовые координаты в допустимых границах холста;
|
||||
- толщина 1–100 см;
|
||||
- уникальные идентификаторы;
|
||||
- отсутствие совпадающих соседних точек;
|
||||
- точное соответствие количества сегментов типу цепочки;
|
||||
- минимум три пригодные вершины у замкнутой цепочки;
|
||||
- лишние или повреждённые поля отклоняются либо нормализуются единообразно.
|
||||
|
||||
Существующие конфигурации миграции не требуют: `wall_paths` является
|
||||
необязательным полем.
|
||||
|
||||
## 15. Геометрические и UX edge cases
|
||||
|
||||
Обязательно учесть:
|
||||
|
||||
- смену инструмента после единственной точки;
|
||||
- двойной клик в одной координате;
|
||||
- нулевой и очень короткий сегмент;
|
||||
- возврат в предыдущую точку;
|
||||
- попытку замкнуть цепочку с недостаточным количеством вершин;
|
||||
- самопересечение замыкающего или обычного сегмента;
|
||||
- прохождение свободной стены через существующую комнату;
|
||||
- полное или частичное совпадение с комнатной стеной;
|
||||
- полное или частичное совпадение двух свободных цепочек;
|
||||
- пересечение двух цепочек без общей вершины;
|
||||
- соединение двух цепочек с необходимостью развернуть обе;
|
||||
- попытку соединиться с серединой сегмента;
|
||||
- T-образный стык стен разной толщины;
|
||||
- переход толстая → линейная → толстая;
|
||||
- острый угол и ограничение miter;
|
||||
- удаление среднего сегмента открытой и замкнутой цепочки;
|
||||
- отмену диалога комнаты;
|
||||
- ошибку сервера при завершении комнаты;
|
||||
- изменение единиц измерения во время рисования;
|
||||
- координаты вне сетки;
|
||||
- достижение лимита точек или общего размера конфигурации;
|
||||
- план без комнат, содержащий только свободные стены;
|
||||
- скрытые границы в режиме просмотра;
|
||||
- конкурентное редактирование в другой вкладке.
|
||||
|
||||
## 16. Критерии готовности первой версии
|
||||
|
||||
1. Незамкнутая цепочка с хотя бы одним сегментом не исчезает при смене
|
||||
инструмента.
|
||||
2. Она сохраняется после перезагрузки карточки и Home Assistant.
|
||||
3. В той же сессии рисование продолжается автоматически.
|
||||
4. После перезагрузки цепочку можно продолжить с любого конца.
|
||||
5. Две цепочки можно соединить конец с концом.
|
||||
6. Ответвление из середины сегмента невозможно.
|
||||
7. Толщина задаётся отдельно каждому следующему сегменту.
|
||||
8. Сегменты разной толщины образуют корректное тело без щелей.
|
||||
9. При замыкании доступны «Создать комнату» и
|
||||
«Оставить замкнутыми стенами».
|
||||
10. Создание комнаты переносит толщины и атомарно удаляет свободную цепочку.
|
||||
11. При общей стене сохраняется ранее существовавшая толщина.
|
||||
12. Отмена диалога не удаляет свободную цепочку.
|
||||
13. Замкнутая самостоятельная цепочка сохраняется без комнаты, заливки и
|
||||
площади.
|
||||
14. Свободные стены видны в основной и статической карточке.
|
||||
15. Они учитываются при масштабировании и центрировании.
|
||||
16. Удаление среднего сегмента корректно разделяет цепочку.
|
||||
17. Двери и окна нельзя устанавливать в свободные стены.
|
||||
18. Старые конфигурации продолжают работать без миграции.
|
||||
|
||||
## 17. Не входит в первую версию
|
||||
|
||||
- перемещение вершин свободных стен;
|
||||
- Resize свободных цепочек;
|
||||
- ответвление из середины сегмента;
|
||||
- автоматическое распознавание комнат из сети стен;
|
||||
- установка дверей и окон в свободные стены;
|
||||
- участие свободных стен в комнатной топологии, площади и распространении
|
||||
света;
|
||||
- отдельные материалы, стили и свойства сторон свободной стены.
|
||||
|
||||
## 18. Предлагаемый порядок будущей реализации
|
||||
|
||||
1. Схема `wall_paths`, серверная валидация и чистые операции над цепочками.
|
||||
2. Посегментная толщина в инструменте «Стены».
|
||||
3. Сохранение, автоматическое продолжение и выбор конца после перезагрузки.
|
||||
4. Соединение цепочек конец с концом.
|
||||
5. Диалог замыкания и два варианта завершения.
|
||||
6. Геометрия тел и стыков стен разной толщины.
|
||||
7. Основная и статическая карточки, расчёт видимой области.
|
||||
8. Инструменты толщины, удаления, привязки и выравнивания.
|
||||
9. Документация и тестовый чек-лист.
|
||||
10. Прогон тестов только перед пре-релизом согласно принятому процессу.
|
||||
|
||||
## 19. Зафиксированные ответы владельца
|
||||
|
||||
| Вопрос | Решение |
|
||||
|---|---|
|
||||
| Что делать при замыкании? | Открывать диалог комнаты; внизу сразу добавить второе действие «Оставить замкнутыми стенами». |
|
||||
| Как продолжать цепочку? | В той же сессии продолжать автоматически; после перезагрузки выбирать конец кликом. |
|
||||
| Как соединять цепочки? | Соединять конец с концом; ответвление из середины сегмента не поддерживать. |
|
||||
| Разрешать двери и окна? | Нет, проёмы устанавливаются только в стены комнат. |
|
||||
| Нужны ли перемещение вершин и Resize? | Вынести в следующий этап; в первой версии оставить продолжение, изменение толщины и удаление сегментов. |
|
||||
| Какая толщина побеждает у общей стены? | Уже существующая физическая стена определяет толщину. |
|
||||
| Сохранять ли одиночную точку? | Нет, точка без сегмента остаётся временной. |
|
||||
Reference in New Issue
Block a user