Files
houseplan-card/docs/superpowers/specs/2026-08-05-free-wall-paths-design.md
T

67 KiB
Raw Blame History

Незавершённые контуры, перегородки и колонны — ТЗ

Статус: реализовано локально, ожидает пре-релизной проверки (2026-08-07; три раунда ревью закрыты, владелец дал прямую команду начать реализацию).

Модели, редактор, рендер, площадь и световая физика реализованы. Тестовый прогон по принятому процессу выполняется только перед пре-релизом.

1. Цель

Добавить в редактор плана три связанные, но разные сущности:

  1. Незавершённый контур комнаты — сохраняемая ломаная, которую можно продолжить и позднее замкнуть в комнату.
  2. Перегородка — одна самостоятельная физическая стена, не принадлежащая контуру комнаты.
  3. Колонна — самостоятельное квадратное или круглое физическое препятствие.

Перегородки и колонны должны:

  • выглядеть как обычные стены и бесшовно примыкать к ним;
  • вычитаться из чистой площади пересекаемых комнат;
  • блокировать Glow;
  • блокировать солнечные лучи из окон;
  • перемещаться по плану с обязательной привязкой к сетке;
  • редактироваться двойным кликом в режиме выбора.

При этом они не создают комнаты, HA area, названия или отдельные контейнеры устройств и не выполняют неявный Split комнаты.

2. Принятое разделение моделей

Ранее самостоятельные стены и незавершённый контур предполагалось хранить одной цепочкой wall_paths. После упрощения UX их следует разделить:

  • room_drafts хранит только незавершённые контуры комнат;
  • partitions хранит самостоятельные стены строго по одному сегменту;
  • wall_columns хранит колонны.

Причины разделения:

  • перегородка создаётся и выбирается как один законченный объект;
  • ей не нужны продолжение цепочки, разворот, соединение концов и удаление среднего сегмента;
  • несколько примыкающих перегородок могут визуально образовать одну стену, но остаются независимо перемещаемыми объектами;
  • вырожденный сегмент нулевой длины нельзя безопасно использовать для колонны;
  • незавершённый контур комнаты сохраняет ранее согласованную механику продолжения и замыкания, не усложняя инструмент перегородок.

Это разделение существует только в данных и редактировании. Рендер, площадь и свет используют общее производное тело физических препятствий.

3. Термины

  • Незавершённый контур — открытая сохраняемая ломаная будущей комнаты.
  • Перегородка — самостоятельный отрезок стены с положительной толщиной.
  • Колонна — квадратное или круглое физическое препятствие с центром на сетке.
  • Комнатная стена — стена, производная от ребра room.poly.
  • Физическое препятствие — объёмное тело комнатной стены, сохранённого сегмента незавершённого контура, перегородки или колонны.
  • Комнатная топология — комнаты, их контуры, HA area, названия, настройки и связи через проёмы.
  • Визуальный union — объединение тел для бесшовной отрисовки.
  • Оптический union — объединение непрозрачных тел после корректного вычитания комнатных проёмов.

4. Модель данных

В конфигурацию пространства добавить три необязательных массива:

interface RoomDraftCfg {
  id: string;
  points: Array<[number, number]>;
  segments: Array<{
    cm: number;
  }>;
}

interface PartitionCfg {
  id: string;
  a: [number, number];
  b: [number, number];
  cm: number;
}

type WallColumnCfg =
  | {
      id: string;
      shape: "square";
      center: [number, number];
      cm: number;
      angle?: number;
    }
  | {
      id: string;
      shape: "circle";
      center: [number, number];
      cm: number;
    };

interface SpaceCfg {
  room_drafts?: RoomDraftCfg[];
  partitions?: PartitionCfg[];
  wall_columns?: WallColumnCfg[];
}

Пример перегородки:

{
  "id": "pt_01",
  "a": [0.10, 0.20],
  "b": [0.40, 0.20],
  "cm": 15
}

Примеры колонн:

{
  "id": "wc_01",
  "shape": "square",
  "center": [0.50, 0.50],
  "cm": 30
}
{
  "id": "wc_02",
  "shape": "circle",
  "center": [0.70, 0.50],
  "cm": 30
}

Инварианты:

  • room_draft.points.length >= 2;
  • room_draft.segments.length === room_draft.points.length - 1;
  • partition.a !== partition.b;
  • перегородка всегда содержит ровно один сегмент;
  • толщина перегородки и каждого сегмента контура конечна и положительна;
  • квадратная колонна имеет конечный cm > 0;
  • круглая колонна имеет конечный cm > 0;
  • angle разрешён только квадратной колонне и канонизируется в диапазон [0, 90) по симметрии квадрата; эквивалентные углы, отличающиеся на 90°, хранятся одинаково;
  • центр колонны и все концы сегментов находятся на сетке;
  • все координаты нормализованы относительно квадратного холста NORM_W × NORM_W по тем же правилам, что room.poly; x и y не зависят от пропорций загруженной картинки-подложки;
  • размеры хранятся в сантиметрах независимо от системы единиц интерфейса;
  • идентификаторы уникальны среди всех объектов пространства;
  • поля другой формы колонны не сохраняются;
  • старое корневое поле segments не возвращается и остаётся legacy.

Рендерируемые интерактивные узлы получают стабильные хуки кастомизации: data-hp="partition|wall-column|room-draft", data-id и соответствующий data-kind. Хуки являются частью действующего контракта STYLING-HOOKS.md.

У колонны cm всегда означает внешний габарит: сторону квадрата или диаметр круга. Поэтому при смене формы числовое значение сохраняется без пересчёта: квадрат 30×30 см превращается в круг диаметром 30 см.

Колонна намеренно не хранится как PartitionCfg с одинаковыми концами: нулевая длина ломает расчёт направления, snapping, перенос и построение поперечного среза стены.

5. Панель редактора плана

В редактор плана добавить две отдельные кнопки:

  • «Перегородка»;
  • «Колонна».

Они располагаются рядом с остальными инструментами геометрии плана. Иконка и подпись должны однозначно отличать их от «Контура комнаты» и обычного декора.

На панели не добавляются новые отдельные настройки формы или размера для этих двух инструментов. Уже существующее поле «Толщина» используется в момент рисования:

  • для перегородки — как толщина её тела;
  • для следующего сегмента незавершённого контура — как толщина его тела;
  • для квадратной колонны — как длина стороны.

Поле принимает общий диапазон 1–150 см, но завершение жеста валидируется по активному инструменту:

  • контур комнаты, перегородка и существующий инструмент изменения толщины комнатной стены — 1–100 см;
  • колонна — 1–150 см.

При переключении с колонны со значением 101–150 см на перегородку значение не обрезается и не заменяется молча. Preview показывает невалидное состояние, завершающий клик не создаёт объект, а рядом с полем отображается допустимый диапазон активного инструмента.

Форма колонны при создании всегда квадратная. Последующее изменение толщины, формы и угла выполняется через свойства объекта.

Активный инструмент сохраняется после создания объекта: можно последовательно поставить несколько перегородок или колонн без повторного выбора кнопки.

6. Создание перегородки

Перегородка рисуется как обычный прямой сегмент стены:

  1. Первый клик задаёт начало и создаёт только временный preview.
  2. Движение курсора показывает направление, длину и тело с начальной толщиной.
  3. Второй клик задаёт конец и создаёт одну запись partitions.
  4. После сохранения текущий жест завершается. Следующий клик начинает новую независимую перегородку.

Обязательные правила:

  • оба конца привязаны к сетке;
  • толщина фиксируется из значения поля «Толщина» в момент завершающего клика;
  • Shift фиксирует направление к ближайшему углу, кратному 45°;
  • длина показывается существующим форматтером метров либо футов;
  • нулевая длина не создаёт объект;
  • Escape до второго клика отменяет только временный preview;
  • одна перегородка никогда автоматически не превращается в цепочку;
  • последовательные перегородки не объединяются в данных автоматически.

Snapping конца перегородки разрешён:

  • к сетке;
  • к концу и центральной линии комнатной стены;
  • к концу и центральной линии другой перегородки;
  • к центру колонны.

Привязка к геометрической границе круглой колонны не предлагается: такая точка почти всегда находится вне сетки. Чтобы получить бесшовное примыкание, перегородку можно провести к центру колонны; лишнее тело скроет визуальный union.

В отличие от старой модели цепочек, T-образное примыкание к середине стены разрешено. Оно не создаёт графовую вершину: бесшовность обеспечивается union физических тел.

7. Создание колонны

  1. Пользователь выбирает «Колонна».
  2. Preview следует за курсором, его центр привязан к сетке.
  3. Один клик создаёт wall_column.
  4. По умолчанию колонна квадратная.
  5. Сторона фиксируется из значения поля «Толщина» в момент клика.
  6. Следующий клик создаёт ещё одну независимую колонну.

Повторный клик в той же точке с тем же внешним габаритом не создаёт полный дубликат:

  • круг сравнивается без угла;
  • квадрат считается дублем только при одинаковом каноническом угле modulo 90°;
  • квадрат и круг с одинаковым центром и габаритом по-прежнему считаются конфликтующим полным наложением, чтобы не создавать полностью скрытый слой.

Квадраты одинакового размера и центра с разными каноническими углами имеют разные тела и разрешены. При отказе существующая колонна кратко подсвечивается, показывается нейтральный toast. Инструмент «Колонна» остаётся активным, поэтому следующий клик по свободной точке создаёт новую колонну; постоянный selection chrome в инструменте создания не показывается. Намеренное частичное перекрытие колонн с разными центрами или габаритами разрешено.

На этапе создания нет выбора формы, размера или угла. Квадрат создаётся с angle: 0, его стороны параллельны осям плана. Форму, размер и угол поворота пользователь меняет после установки.

8. Выбор, перенос и свойства

8.1 Режим «Выбрать»

Перегородки и колонны являются полноценными выбираемыми объектами редактора плана:

  • один клик выбирает объект;
  • выбранный объект получает существующее нейтральное выделение геометрии;
  • Delete удаляет только выбранный объект;
  • изменение входит в общий именованный command stack на 50 шагов;
  • Ctrl+Z/Ctrl+Shift+Z/Ctrl+Y работают по общим правилам редактора плана.

Удаление одной перегородки или колонны не требует подтверждения: это одна обратимая команда общего стека. Подтверждение используется только для удаления draft целиком или его сегмента, поскольку одно действие может удалить либо разделить значительную физическую геометрию.

Хром выделения зависит от типа объекта:

  • перегородка получает контур тела и маркер перемещения без угловых ручек Resize и без ручки поворота;
  • квадратная колонна получает рамку без масштабных ручек и одну верхнюю ручку поворота;
  • круглая колонна получает рамку без масштабных и поворотной ручек;
  • размер hit target не меньше действующего минимального размера touch-цели, даже если физическое тело толщиной 1 см визуально субпиксельное.

Поворот квадратной колонны работает с шагом 5°; Shift включает свободный угол, как в редакторе подложки. Числовое свойство остаётся точным альтернативным способом.

При наложении нескольких объектов приоритет hit-test должен позволять выбрать перегородку или колонну повторным кликом либо существующим механизмом перебора, не заставляя пользователя временно скрывать стены комнат.

При выборе перегородки или колонны в редакторе плана появляется компактная контекстная панель с двумя действиями:

  • «Свойства» — открывает тот же диалог, что double-click/double-tap;
  • «Удалить» — удаляет выбранный объект без подтверждения как одну обратимую команду.

Панель является обязательным touch-путём: ни свойства, ни удаление не должны требовать физической клавиатуры или скрытого жеста. После удаления панель закрывается, Undo остаётся доступен.

8.2 Перетаскивание

Оба типа объектов можно переносить по плану:

  • у перегородки переносится весь сегмент целиком;
  • длина, угол и толщина перегородки сохраняются;
  • к обеим конечным точкам применяется один и тот же сеточный delta, чтобы перенос не деформировал сегмент;
  • у колонны переносится центр;
  • перенос квадратной колонны сохраняет её угол поворота;
  • preview показывается во время drag;
  • сохранение выполняется один раз при отпускании указателя;
  • pointercancel и Escape возвращают исходную геометрию;
  • off-grid координаты никогда не сохраняются.

Drag начинается с pointerdown по телу выбранного или ещё не выбранного объекта либо по маркеру перемещения перегородки и перехода общего порога движения. До превышения порога жест остаётся обычным кликом выбора; повторный клик без движения участвует в переборе наложенных объектов. После превышения порога верхний подходящий объект выбирается при необходимости и переносится. Те же правила действуют для touch и не конфликтуют с double-tap.

Во время pointermove не пересчитываются boolean-union, чистая площадь, Glow и солнечные маски. Для preview используется кэшированный базовый рендер без выбранного объекта и отдельное перемещаемое тело поверх него; временный шов на стыке во время жеста допустим. Полный пересчёт и сохранение выполняются один раз после pointerup. pointercancel не инвалидирует производные кэши.

Перемещение отдельных концов перегородки не входит в первую версию: это Resize, а не перенос.

8.3 Двойной клик и свойства

Двойной клик в режиме «Выбрать» всегда открывает свойства объекта. На touch используются double-tap и явная кнопка «Свойства» в панели выбранного объекта; критичная функция не должна быть доступна только скрытым жестом.

Для перегородки свойства содержат:

  • толщину;
  • информационно — текущую длину в пользовательских единицах.

Для колонны свойства содержат:

  • «Сторона» для квадратной формы или «Диаметр» для круглой;
  • форму: квадрат или круг;
  • угол поворота для квадратной формы.

Квадратную колонну также можно поворачивать штатным жестом выбранного объекта. Поворот выполняется вокруг центра; центр остаётся привязанным к сетке. Для круглой формы угол отсутствует и не сохраняется.

Изменение формы сохраняет центр и внешний габарит cm: сторона квадрата становится диаметром круга и наоборот. После сохранения свойств визуал, площадь, Glow и солнечные лучи обновляются одной командой Undo/Redo.

9. Незавершённый контур комнаты

Ранее согласованная функция сохранения незаконченной комнаты остаётся, но не использует partitions:

  • «Контур комнаты» создаёт и продолжает room_draft;
  • первый постоянный сегмент сразу сохраняет draft;
  • смена инструмента не удаляет его;
  • в той же сессии продолжение возобновляется автоматически;
  • после перезагрузки пользователь выбирает конец кликом;
  • из середины сегмента draft нельзя создавать ответвление;
  • два draft можно соединить только конец с концом;
  • толщина задаётся отдельно каждому следующему сегменту;
  • одиночная точка без сегмента не сохраняется.

После перезагрузки конец выбирается через общий экранный pickTolerance, не меньший минимальной touch-цели. Допуск переводится из CSS-пикселей в координаты плана с учётом текущего zoom; пользователь не обязан попадать в математическую точку вершины.

Соединение двух draft конец с концом выполняется атомарно:

  • при необходимости порядок точек и соответствующий порядок segments одной или обеих ломаных разворачивается;
  • draft, который пользователь продолжал, сохраняет id, второй удаляется;
  • совпавшая конечная точка хранится один раз;
  • нулевой соединительный сегмент не создаётся;
  • толщины существующих сегментов не усредняются и не меняются;
  • если соединение замыкает контур, сразу запускается обычная проверка замыкания и диалог завершения.

9.1 Выбор, свойства и удаление draft

Сохранённый draft нельзя оставлять без средств обслуживания:

  • в режиме «Выбрать» клик по его сегменту выбирает draft;
  • выбранный draft подсвечивает всю ломаную и вершины небольшими неинтерактивными маркерами; рамки, Resize/rotate-ручек и drag-cursor нет;
  • draft целиком не перетаскивается в первой версии;
  • Delete/Backspace удаляет выбранный draft целиком после подтверждения;
  • double-click/double-tap по сегменту открывает свойства этого сегмента;
  • свойства позволяют изменить его толщину в диапазоне 1–100 см;
  • в свойствах есть отдельные подтверждаемые действия «Удалить сегмент» и «Удалить весь контур»;
  • удаление единственного сегмента удаляет draft;
  • удаление крайнего сегмента укорачивает draft;
  • удаление среднего сегмента разбивает draft на два: исходный id сохраняет часть с исходной первой точкой, вторая получает новый id;
  • часть без сегментов не сохраняется;
  • после перезагрузки доступны те же операции, они не зависят от сессионного Ctrl+Z.

Изменение толщины, удаление сегмента, удаление всего draft и образовавшийся split являются именованными командами общего стека и отменяются в текущей сессии. Независимость от сессионного Ctrl+Z означает только то, что после перезагрузки действия остаются снова доступными через UI, а не то, что они обходят command stack.

Инструменты удаления комнат не удаляют draft, перегородки или колонны кликом. Для всех новых объектов действует единое правило продукта: Delete удаляет только явно выбранный объект; посегментное удаление draft выполняется из его свойств.

При замыкании с минимум тремя пригодными рёбрами и без самопересечений открывается диалог комнаты с двумя действиями. Пригодное ребро:

  • имеет разные привязанные к сетке концы;
  • имеет длину не меньше одного шага сетки;
  • не дублирует другое ребро контура полностью;
  • вместе с остальными рёбрами образует простой замкнутый полигон с площадью больше действующего геометрического epsilon.

Должны существовать минимум три разные пригодные вершины.

  1. Создать комнату — атомарно создать room.poly, перенести толщины в walls[] и удалить room_draft.
  2. Оставить замкнутыми стенами — атомарно преобразовать каждое ребро draft в отдельную partition, удалить draft и не создавать комнату.

При общей стене комнаты сохраняется толщина уже существующей физической стены. Отмена диалога и ошибка сохранения не удаляют draft.

Сохранённые сегменты draft до преобразования визуально и физически считаются стенами: участвуют в union, площади и оптических препятствиях. Это не относится к единственной временной стартовой точке или незавершённому preview-сегменту.

Тело каждого draft-сегмента до замыкания строится тем же симметричным centreline ± 1/2 thickness примитивом, что и будущая комнатная стена. Операция «Создать комнату» повторно использует те же оси и толщины, поэтому сама по себе не должна визуально сдвигать стены. Измениться может только производный union в месте совпадения с уже существующей комнатной стеной, где по принятому правилу побеждает существующая толщина.

10. Визуальная геометрия

Для комнатных стен, сохранённых сегментов room_drafts, partitions и wall_columns строятся объёмные тела в общей системе координат.

Перед финальной отрисовкой тела объединяются. После union рисуются:

  • одна заливка стен;
  • единая глобальная штриховка;
  • только внешний контур результата.

Это обязательно для отсутствия:

  • швов между перегородкой и комнатной стеной;
  • торца горизонтальной стены внутри T-стыка;
  • двойной обводки между соседними перегородками;
  • артефактов при пересечении колонны со стеной;
  • щелей на стыках разной толщины.

Дополнительные правила:

  • тело перегородки строится симметрично относительно её центральной линии;
  • свободные концы имеют ровный поперечный срез;
  • L-, T- и X-стыки допускаются без изменения исходных объектов;
  • miter острых углов ограничивается с переходом к bevel;
  • квадратная колонна строится относительно центра со стороной заданного размера и сохранённым углом поворота;
  • круглая колонна строится относительно центра с заданным диаметром;
  • круг для boolean-операций аппроксимируется детерминированно с ошибкой площади не более 0,2% и с ограничением максимальной хорды/стрелы прогиба, достаточным, чтобы грани не читались в Glow на максимальном поддерживаемом зуме;
  • рендер, площадь и свет используют одну производную геометрию;
  • результат union не сохраняется в конфигурации и может кэшироваться только в runtime.

В редакторе плана перегородки и колонны показываются полностью. В редакторе подложки они приглушаются вместе с комнатными стенами, поскольку не относятся к редактируемому декоративному слою. В View их видимость подчиняется show_borders, а не скрытию декора. Статическая карточка использует тот же рендер.

Самостоятельные стены и колонны разрешены вне комнат. Они участвуют в content bounds, но не создают вокруг себя искусственную «бумагу» или пол: вне комнат видно только собственное тело стены/колонны поверх сцены. Маска бумаги по-прежнему определяется комнатной геометрией.

10.1 Кэширование производной геометрии

Производные данные разделяются минимум на следующие кэши:

  • тела комнатных стен и их проёмы;
  • тела room_drafts;
  • тела partitions;
  • тела wall_columns;
  • финальный визуальный union;
  • оптический union;
  • cleanFloor по комнатам.

Ключ кэша включает идентификатор пространства и ревизию соответствующей геометрии. Изменение одного объекта не должно заново строить его неизменившиеся соседние примитивы. Для поиска пересекающихся тел используется spatial index; полный union допустим после commit, загрузки пространства, оптимизации или внешней ревизии, но не на каждом pointermove.

11. Чистая площадь

Для комнаты сначала строится существующая геометрия чистого пола F по внутренним граням комнатных стен. Затем строится union O из тел:

  • сохранённых сегментов room_drafts;
  • перегородок;
  • колонн.

Итог:

cleanFloor(room) = F − O
cleanArea(room)  = area(cleanFloor(room))

Следствия:

  • пересекающиеся препятствия не вычитаются дважды;
  • наложение на уже исключённое тело комнатной стены не уменьшает площадь повторно;
  • вне комнаты препятствие на её площадь не влияет;
  • при частичном пересечении вычитается только пересекающаяся часть;
  • препятствие на общей границе учитывается отдельно по фактическому пересечению с чистым полом каждой комнаты;
  • tooltip, карточка комнаты и все остальные потребители получают одинаковую обновлённую чистую площадь;
  • перегородка от стены до стены не выполняет автоматический Split комнаты;
  • замкнутый набор самостоятельных перегородок вычитает только тела стен: пол внутри продолжает учитываться в исходной комнате;
  • заливка, hover, HA area и состав устройств остаются едиными по обе стороны.

Перегородки и колонны не запускают автоматический перенос названия комнаты или кнопки настроек. Их сохранённые пользовательские позиции остаются неизменными; если объект перекрыл подпись или кнопку, пользователь легко переносит её вручную существующим инструментом.

12. Glow и солнечные лучи

Нужен один чистый резолвер, условно resolvedPhysicalObstacles(space):

  1. Построить тела комнатных стен.
  2. Вырезать только принадлежащие им виртуальные участки, дверные и оконные тоннели.
  3. Добавить сохранённые сегменты draft, перегородки и колонны без проёмов.
  4. Выполнить union результата.

Проёмы вычитаются до добавления самостоятельных объектов. Иначе глобальный дверной тоннель мог бы случайно пробить наложенную перегородку или колонну.

12.1 Glow

  • каждый источник отсекается по реальному телу препятствий, а не по осевым линиям;
  • перегородки блокируют свет по всей толщине;
  • квадратные и круглые колонны создают соответствующую тень;
  • комнатный граф можно использовать только для предварительного выбора кандидатов, итоговая видимость всегда проверяется геометрически;
  • действующий дверной тоннель пропускает Glow только в незакрытой части;
  • источник, ошибочно помещённый внутрь препятствия, не должен освещать пространство сквозь него.

12.2 Солнечные лучи

  • лучи начинаются из внутренних углов оконного проёма;
  • после выхода из оконного тоннеля они отсекаются ближайшим физическим препятствием;
  • колонны и перегородки формируют тени;
  • препятствие, перекрывающее часть окна или тоннеля, блокирует соответствующую часть света;
  • собственная стена окна не отсекает луч сразу благодаря предварительно вырезанному тоннелю.

Изменение, перенос, удаление и Undo/Redo любого препятствия инвалидирует кэш площади, Glow и солнечных масок.

Физическое участие не зависит от визуальной настройки show_borders. Если пользователь скрыл стены, перегородки и колонны остаются препятствиями для площади, Glow и солнца так же, как скрытые комнатные стены. Это осознанная цена режима без границ: настройка меняет представление, но не геометрию дома.

13. Проёмы, комнаты и другие инструменты

  • двери, окна и ворота по-прежнему устанавливаются только в стены комнат;
  • перегородки, draft и колонны не являются целью инструмента проёмов;
  • самостоятельная перегородка всегда является сплошным оптическим препятствием;
  • Merge и Split комнат не изменяют перегородки и колонны автоматически;
  • явный Split комнаты остаётся единственным способом создать две комнаты и два независимых набора настроек;
  • глобальная оптимизация привязывает концы перегородок, точки draft и центры колонн к сетке, не изменяя размеры в сантиметрах;
  • расчёт content bounds включает полные тела draft, перегородок и колонн;
  • физическая геометрия всегда входит в прямоугольник all команды «Вписать всё» и не отбраковывается эвристикой MIN_VOTERS/OUTLIER_K; далеко стоящий объект считается намеренно размещённой частью плана, а не статистическим выбросом;
  • мебель может магнититься к их физическим граням как к обычным стенам.

13.1 Resize комнат и трансформации подложки

Snapping при создании не является сохраняемой магнитной связью. В первой версии перегородки, колонны и draft имеют абсолютные координаты плана:

  • Resize или изменение контура комнаты не перемещает примкнувшие объекты;
  • если после Resize появилась щель либо пересечение, пользователь переносит самостоятельный объект вручную;
  • Merge/Split комнат также не перепривязывает объекты;
  • перемещение, масштабирование и поворот картинки-подложки меняют только изображение и не трансформируют геометрию плана;
  • системная миграция системы координат или всей геометрии пространства обязана одной транзакцией преобразовать комнаты, draft, перегородки, колонны, проёмы, декор и layout.

Неявная магнит-связь отложена: без видимого пользователю anchor она может неожиданно переместить стену через несколько комнат. Если такое поведение будет нужно, оно вводится позднее как явное привязываемое constraint-свойство.

В View перегородки и колонны не получают самостоятельный tooltip и не должны перехватывать hover комнаты. Их pointer-events либо hit-test включаются только в редакторе плана.

14. Серверная валидация и ограничения

Предлагаемые пределы:

  • не более 200 незавершённых контуров на пространство;
  • не более 500 точек в одном draft;
  • не более 2000 сохранённых draft-сегментов суммарно;
  • не более 2000 перегородок на пространство;
  • не более 500 колонн на пространство;
  • толщина комнатной стены, перегородки и каждого draft-сегмента 1–100 см;
  • сторона квадратной колонны 1–150 см;
  • диаметр круглой колонны 1–150 см;
  • только конечные числа в допустимых границах холста;
  • строгое соответствие количества draft-сегментов количеству точек;
  • разные концы перегородки;
  • допустимая форма колонны только square или circle;
  • угол разрешён только квадратной колонне и нормализуется;
  • уникальные идентификаторы;
  • старые конфигурации продолжают работать без обязательной миграции, поскольку новые массивы необязательны.

Операция, создающая новые записи, сначала проверяет итоговые лимиты. Если удаление среднего сегмента потребовало бы создать 201-й draft, действие отклоняется без частичной мутации и показывает локализованный toast. То же правило действует при преобразовании draft в набор перегородок: ошибка лимита не удаляет исходный draft.

Минимум 1 см сохраняется как ранее согласованный физический предел. Визуальная тонкость не уменьшает hit target редактора: выбор использует отдельную экранную зону допуска.

Все новые кнопки, свойства, подтверждения, ошибки диапазона и toast входят в обязательные словари ru и en; пользовательских строк, зашитых напрямую в рендер или валидатор, быть не должно.

15. Обязательные edge cases

Создание и редактирование

  • второй клик перегородки совпадает с первым;
  • Shift переключается во время preview;
  • double-click создания не создаёт нулевую вторую перегородку;
  • два клика колонны в одной точке не создают полный дубликат;
  • Escape и смена инструмента во время preview;
  • быстрый переход между пространствами до debounce-save;
  • drag перегородки около границы холста;
  • drag с pointercancel;
  • двойной клик после минимального движения указателя;
  • double-tap и кнопка «Свойства» на touch без конфликта с drag;
  • удаление перегородки и колонны с touch через контекстную панель;
  • движение указателя ровно около drag-threshold: клик не превращается в перенос случайно, а drag не открывает свойства;
  • значение 101–150 см при переключении с колонны на перегородку;
  • выбор перегородки, полностью лежащей поверх комнатной стены;
  • выбор колонны, пересекающей несколько стен;
  • выбранный draft без drag/resize/rotate-ручек;
  • выбор конца draft после перезагрузки на разных zoom и touch DPI;
  • смена формы колонны с последующим Undo/Redo;
  • поворот квадратной колонны жестом и через числовое свойство;
  • канонизация углов квадрата 5°, 95°, 185° и 275° в одно сохранённое значение;
  • два квадрата одного размера и центра с разными каноническими углами;
  • смена квадрата на круг после поворота и восстановление через Undo;
  • внешнее обновление конфигурации во время drag или открытых свойств.
  • попытка split среднего сегмента при лимите 200 draft;
  • замыкание из трёх вершин с нулевым ребром, повторным ребром или площадью ниже epsilon;

Геометрия

  • T-стык конца перегородки с серединой комнатной стены;
  • T-стык двух перегородок разной толщины;
  • L- и X-пересечения;
  • частичное и полное совпадение перегородок;
  • несколько независимых перегородок на одной прямой;
  • очень короткая перегородка;
  • касание квадратной колонны гранью и углом;
  • касательная к круглой колонне;
  • колонна полностью внутри стены;
  • препятствие на общей стене двух комнат;
  • препятствие в комнате с островом/отверстием;
  • план без комнат, содержащий только перегородки и колонны.
  • одиночная колонна далеко от дома остаётся внутри результата «Вписать всё»;
  • самостоятельное тело далеко за пределами комнат без искусственного расширения бумаги;
  • Resize комнатной стены, к которой была примагничена перегородка;
  • трансформация картинки-подложки без сдвига геометрии плана;

Площадь и свет

  • несколько перекрывающихся препятствий без двойного вычитания;
  • перегородка, частично выходящая за комнату;
  • перегородка от стены до стены без автоматического Split;
  • Glow по разные стороны перегородки внутри одной комнаты;
  • источник Glow внутри препятствия или на его границе;
  • дверной тоннель, частично перекрытый перегородкой;
  • солнечный луч в угол стены и по касательной круглой колонны;
  • круглая тень на максимальном поддерживаемом зуме без заметной гранёности;
  • скрытые через show_borders тела, продолжающие влиять на площадь и свет;
  • пересчёт после свойств, drag, удаления, Undo/Redo и серверного echo.

16. Критерии готовности первой версии

  1. В редакторе плана есть отдельные кнопки «Перегородка» и «Колонна».
  2. Новые настройки на панели не добавляются; существующее поле «Толщина» задаёт начальную толщину перегородки и сторону квадратной колонны.
  3. Один жест перегородки создаёт ровно один независимый сегмент.
  4. Колонна создаётся одним кликом и по умолчанию является квадратной.
  5. Оба объекта сохраняются после перезагрузки.
  6. Оба объекта целиком перетаскиваются по сетке.
  7. Перетаскивание перегородки сохраняет её длину и угол.
  8. Двойной клик/double-tap перегородки и явная кнопка выбранного объекта открывают свойства толщины.
  9. Те же три пути открывают свойства размера, формы и угла колонны.
  10. Квадрат и круг корректно переключаются без смещения центра.
  11. Квадратная колонна поворачивается вокруг привязанного к сетке центра; круг не хранит бессмысленный угол.
  12. Перегородки и колонны визуально объединяются с обычными стенами без швов, торцов и двойной обводки.
  13. Их объединённое тело корректно вычитается из чистой площади.
  14. Tooltip и карточка комнаты показывают новую площадь.
  15. Glow и солнечные лучи блокируются физическими телами объектов.
  16. Проёмы комнатных стен не пробивают наложенные самостоятельные объекты.
  17. В View объекты не перехватывают hover комнаты.
  18. Удаление, перенос, поворот и свойства работают через общий command stack.
  19. Незавершённый контур комнаты сохраняется независимо от перегородок.
  20. «Оставить замкнутыми стенами» преобразует рёбра draft в отдельные перегородки.
  21. Перегородка от стены до стены не разделяет комнату автоматически.
  22. В замкнутом наборе перегородок вычитаются только тела стен, внутренний пол остаётся частью исходной комнаты.
  23. Старые конфигурации работают без миграции.
  24. После перезагрузки draft можно удалить целиком, изменить толщину или удалить произвольный сегмент без зависимости от истории текущей сессии.
  25. Удаление среднего draft-сегмента корректно создаёт два валидных draft.
  26. Соединение draft конец с концом сохраняет порядок точек, толщины и один стабильный id.
  27. Создание комнаты из draft не сдвигает визуальное тело его стен.
  28. Drag/rotate не запускает полный union, площадь или оптический пересчёт на каждом pointermove; производные данные обновляются после commit.
  29. Resize комнаты не перемещает самостоятельные объекты и не создаёт скрытых связей.
  30. Перегородки и колонны не изменяют автоматически пользовательские позиции названия и кнопки комнаты.
  31. При show_borders: false физика площади и света остаётся неизменной.
  32. Новые интерактивные узлы имеют документированные data-hp/data-id/ data-kind, а весь UI локализован на русском и английском.
  33. Выбранный draft имеет понятную подсветку без ложных ручек и не перетаскивается целиком.
  34. Удаление и изменение draft входят в общий command stack.
  35. Конец draft после перезагрузки можно выбрать с действующим touch-допуском.
  36. «Вписать всё» включает каждую физическую перегородку, колонну и draft, даже если эвристика выбросов исключила бы одиночный объект.
  37. Инструмент толщины комнатной стены отклоняет 101–150 см так же, как контур комнаты и перегородка.
  38. Полный дубль колонны с тем же центром и габаритом не создаётся независимо от формы, а инструмент создания остаётся активным.
  39. Удаление перегородки/колонны не подтверждается; удаление draft или его сегмента подтверждается. Оба варианта обратимы через Undo.
  40. Операция split или преобразования draft не превышает лимиты и при отказе сохраняет исходную геометрию без частичной мутации.
  41. Выбранные перегородка и колонна имеют touch-доступную панель «Свойства» / «Удалить»; удаление не требует клавиатуры.
  42. Click, повторный click, double-tap и drag однозначно разделяются общим порогом движения.
  43. Guard дубля квадрата сравнивает канонический угол, поэтому разные ориентации разрешены, а эквивалентные modulo 90° отклоняются.
  44. Backend валидирует толщину каждого draft-сегмента в диапазоне 1–100 см.
  45. Угол квадратной колонны хранится канонически в [0, 90), а Undo/Redo не создаёт эквивалентные неканонические состояния.

17. Не входит в первую версию

  • изменение отдельных концов или длины готовой перегородки;
  • объединение нескольких перегородок в один объект;
  • автоматическое разбиение одной перегородки в T-стыке;
  • сохраняемые magnetic constraints и автоматическое следование за Resize комнат;
  • автоматический Split комнаты физическим препятствием;
  • установка дверей и окон в перегородки;
  • создание HA area по разные стороны перегородки;
  • произвольные формы колонн;
  • материалы и стили, отличные от обычных стен;
  • расширение маски пола/бумаги вокруг самостоятельных стен вне комнат;
  • самостоятельные tooltip или действия перегородок в View.

18. Предлагаемый порядок будущей реализации

  1. Схемы room_drafts, partitions, wall_columns и backend-валидация.
  2. Чистые операции создания, переноса, свойств, удаления, разбиения, соединения и преобразования draft.
  3. Кнопка и preview перегородки с grid/Shift/snap.
  4. Кнопка и preview квадратной колонны.
  5. Выбор, drag, поворот, двойной клик и диалоги свойств.
  6. Общая производная геометрия тел, spatial index, кэши и бесшовный визуальный union.
  7. Сохранение и продолжение room_drafts.
  8. Вычитание препятствий из чистой площади и подключение всех потребителей.
  9. Единый оптический union для Glow и солнечных лучей.
  10. Статическая карточка, content bounds и остальные режимы.
  11. Touch-доступ, data hooks, i18n и доступные hit targets.
  12. Документация и тестовый чек-лист.
  13. Минимально необходимый прогон тестов только перед пре-релизом согласно принятому процессу.

19. Зафиксированные решения владельца

Вопрос Решение
Где размещать перегородки и колонны? В редакторе плана.
Какие инструменты добавить? Две отдельные кнопки: «Перегородка» и «Колонна».
Как рисовать перегородку? Как обычную стену, но каждый жест создаёт только один самостоятельный сегмент.
Какие настройки показывать при создании? Новых настроек не добавлять; использовать существующее поле «Толщина».
Начальная толщина перегородки Значение поля «Толщина» в момент завершающего клика.
Начальный размер колонны Значение поля «Толщина» в момент установки; оно задаёт сторону квадрата.
Какая форма колонны по умолчанию? Квадрат.
Можно ли перемещать готовые объекты? Да, целиком по плану с сохранением привязки к сетке.
Как открывать свойства? Двойным кликом в режиме «Выбрать».
Свойства перегородки Толщина.
Свойства колонны Сторона квадрата или диаметр круга, форма и угол поворота квадрата.
Разрешён ли поворот квадратной колонны? Да; вокруг центра, который остаётся на сетке.
Влияют ли объекты на площадь? Да, их объединённое тело вычитается из чистого пола.
Влияют ли объекты на свет? Да, блокируют Glow и солнечные лучи.
Разделяет ли перегородка от стены до стены комнату и HA area? Нет, только явный Split создаёт отдельные комнаты.
Что происходит с полом внутри замкнутого набора перегородок? Он продолжает учитываться в исходной комнате; вычитаются только тела стен.
Как менять квадрат на круг? Сохранять внешний габарит: сторона квадрата становится диаметром круга.
Допустимые размеры колонн Сторона квадрата и диаметр круга — 1–150 см.
Следуют ли объекты за стенами при Resize комнаты? Нет, сохраняют абсолютные координаты; пользователь при необходимости переносит их вручную.
Создают ли объекты вне комнат бумагу или пол? Нет, отображается только собственное тело объекта поверх сцены.
Отключает ли show_borders: false физическое влияние? Нет, невидимые стены продолжают влиять на площадь, Glow и солнечные лучи.
Нужно ли автоматически уводить названия и кнопки комнат от препятствий? Нет, пользователь при необходимости перемещает их вручную.
Можно ли перетаскивать draft целиком в первой версии? Нет; доступны продолжение, изменение толщины и удаление сегментов.
Участвуют ли удалённые одиночные объекты во «Вписать всё»? Да, любая физическая перегородка, колонна или draft всегда входит в итоговый all-прямоугольник.
Нужно ли подтверждать удаление? Перегородка и колонна удаляются без подтверждения с возможностью Undo; удаление draft или его сегмента требует подтверждения.
Разрешать двери, окна и ворота? Нет, проёмы устанавливаются только в стены комнат.
Как ведут себя в View? Выглядят как обычные стены, подчиняются show_borders, не перехватывают hover комнаты.