Обработать выделенные объекты
Выполнить ограниченную операцию над выбранными объектами и обработать неподходящее выделение.
Операция над выделением начинается с определения области действия: какие типы подходят, что делать с группами и как избежать двойного изменения родителя и ребёнка. В этом примере сместим обычные дисплеи и группы по локальной оси X.
Конкретная операция и границы
Скачать полный плагин selection.js. Установите его, перезапустите редактор и откройте копию тестового проекта. В меню дока появится «Сместить выделение по локальной X».
Пример предназначен для обычного режима моделирования вне совместной сессии. Он принимает Block Display, Item Display, Text Display и обычные группы. Корень проекта, настоящие структуры и служебные объекты не входят в область операции.
Прямое изменение через setPosition в этом примере не создаёт запись Undo.
Плагин сообщает об этом до ввода значения. Используйте тестовую копию и не рассчитывайте,
что Ctrl+Z отменит смещение. При исключении сам обработчик пытается восстановить координаты
объектов, которые уже начал изменять; это локальное восстановление, а не транзакция редактора.
Чтение выделения и обновление интерфейса выполняются через стабильный editorAPI.
Пример также читает корень проекта, флаги режимов и состояния совместной работы через
внутренний editor, а у объектов - признаки типа и иерархию. Это зависимость от текущей
реализации, которую нужно сверять при обновлениях.
Получить выделение
const selected = [...new Set(editorAPI.getSelectedObjects())];Метод возвращает массив объектов сцены. Пустой массив - нормальный результат.
Снимок массива не замораживает сами объекты: если ваша обработка содержит await, после
ожидания нужно заново проверить проект, наличие объектов и допустимость операции.
В данном примере изменение координат выполняется синхронно.
Не вызывайте методы трансформации у каждого найденного элемента вслепую. Сначала фильтруйте разрешённые типы и проверяйте необходимые методы. Затем оставьте только верхние объекты выбранной области:
const accepted = new Set(compatible);const roots = compatible.filter(object => { for (let parent = object.parent; parent; parent = parent.parent) { if (accepted.has(parent) || parent.isStructureGroup) return false; } return true;});compatible в этом фрагменте - уже отфильтрованные объекты из полного примера.
Если выбраны группа и её дочерний дисплей, движется только группа; ребёнок переместится
вместе с ней и не получит второе смещение. Если выбраны только дочерние дисплеи,
обрабатываются они сами. Неподходящие элементы не расширяют область операции.
Для поиска по свойству есть editorAPI.find(key, value), но он ищет по всему проекту.
Не заменяйте им чтение выделения без изменения смысла инструмента.
Контракты: объекты и выделение.
Применить поддерживаемое изменение
Смещение вводится в блоках: 0.25 - четверть блока по локальной X.
Локальная ось относится к системе родителя объекта. В повёрнутой или масштабированной
группе одинаковое локальное смещение не равно одинаковому мировому смещению.
Проверяем непустой ввод, конечное число и границы от -16 до 16; нулевое смещение отклоняем. Диапазон выбран для примера, это не лимит координат BDEngine. План для всех объектов готовится до первого изменения:
const before = {...object.getPosition()};const after = {...before, x: before.x + offset};if (![after.x, after.y, after.z].every(Number.isFinite)) { throw new Error('Некорректные координаты.');}object.setPosition(after);object.updateMatrix();object.updateMatrixWorld(true);getPosition() возвращает локальные координаты. setPosition проверяет конечность
значений и округляет их к шагу объекта. Отсутствующие оси не изменяются, но в примере
мы сохраняем все три для возможного восстановления.
Для вращения методы getRotation и setRotation используют градусы.
Нативное свойство Three.js rotation использует радианы; переносить значения между
этими интерфейсами без преобразования нельзя. Здесь вращение и масштаб не меняются.
Подробнее: свойства объектов и единицы.
Обновить состояние редактора
После изменения всех целевых объектов вызывается:
editorAPI.update(true);Метод обновляет редактор, а true дополнительно обновляет временное состояние контроллера
по текущему выделению. Это не команда истории, не универсальная транзакция и не отправка
произвольных изменений другим участникам совместной сессии.
Чтобы изменения выглядели и сохранялись корректно, используйте методы объектов и обновляйте
их матрицы. Простое присваивание object.position.x обходит часть логики объекта.
Для производственного инструмента отдельно спроектируйте Undo и сетевое поведение,
если они нужны; их нельзя получить одним дополнительным update().
Если ваш инструмент удаляет объекты, требуйте подтверждение перед удалением и проверяйте непустой список:
if (targets.length > 0 && confirm(`Удалить объектов: ${targets.length}?`)) { editorAPI.delete(targets);}В текущей реализации delete([]) использует текущее выделение как запасной вариант.
Не передавайте пустой результат фильтрации, рассчитывая на отсутствие действия.
Скачиваемый пример ничего не удаляет.
Проверить ошибки и историю
- Запустите инструмент без выделения: он должен объяснить, что подходящих объектов нет.
- Выделите обычный дисплей и введите
0.25. Измениться должна только локальная X. - Выделите дисплей вместе со служебным объектом. Смещение применится только к поддерживаемому типу.
- Проверьте группу, группу вместе с ребёнком и двух отдельных детей. Вариант «группа + ребёнок» не должен переместить ребёнка дважды.
- Отмените ввод, затем попробуйте пустую строку, текст, 0 и значение вне диапазона. Проект не должен измениться.
- Повторите в повёрнутой группе: оцените результат по локальным координатам, а не по направлению мировой оси на экране.
- Проверьте повторный запуск: каждый успешный запуск добавляет выбранное смещение ещё раз. Убедитесь, что инструмент не рекламирует Ctrl+Z как способ отмены.
После теста проверьте сохранение и повторное открытие копии проекта. Практика повторного выполнения и диагностика ошибок: проверить плагин перед выпуском.