Всем привет! Сегодня расскажу, как работать с помещениями, получать его контур разными способами, и превращать его в пол или перекрытие в Revit. Поехали.
Введение
Для работы я буду использовать своё приложение для большого количества команд. В нём создам новый проект с WPF-окном. Пользователь будет выбирать тип пола и помещения, а затем плагин создаст полы для них.
Я давно не писал статьи с полным описанием всех подробностей, и мои читатели могли пропустить что-либо среди других статей, поэтому сейчас некоторые вещи могут повторяться. Там, где я пропускаю что-либо, буду приводить ссылки на свои другие статьи.
Создание проекта
Итак, ещё раз — для старта я использую проект на шаблоне Nice3Point, подготовленный для размещения нескольких команд согласно этой статье.
Жмём правой кнопкой мыши на решение — Добавить — Создать проект.
Выбираем шаблон Revit Addin Module:
Не забываем указать папку source:
В данном случае я выберу модальный UI без инъекции зависимостей:
Проект создан:
Создание окна WPF
Подробности о создании WPF-приложений смотрите в подборке о WPF. Я же сейчас реализую обычное окно: там будет выпадающий список для выбора типа пола, текст, показывающий, сколько помещений выбрано, и 2 кнопки: выбрать помещения и создать полы.
Вот так выглядит код окна (до добавления привязок):
А само окно вот так:
Создание ViewModel
Создадим простую ViewModel, даже без дескрипторов для описания типов пола. У нас будут следующие свойства:
- Список типов полов
- Выбранный тип пола
- Список выбранных помещений
Мы выводим на экран так же ещё и число выбранных помещений, но для этого нам не нужны отдельные свойства. Мы можем привязаться к списку помещений и использовать конвертёр, либо привязаться непосредственно к свойству Length массива помещений. Я сделаю массив, то есть неизменяемый объект — при попытке выбрать помещения, они будут не добавляться к выбору, а сбрасываться.
Получилась вот такая ViewModel:
О том, что такое RelayCommand и как работает ObservableProperty, я пишу здесь.
Ещё я использую события во ViewModel, чтобы управлять видимостью окна:
Чуть более подробно я раскрываю это здесь. Не забывайте добавлять обработчик в файле команды (напомню в конце статьи)
На самом деле, не очень подробно, так что возможно расскажу в следующих статьях, что это за магия.
Теперь давайте добавим ISelectionFilter, чтобы пользователь мог выбирать только комнаты:
Не забудьте добавить его в команду выбора элементов пользователем:
Настройка привязок
Отлично, ViewModel готова. Давайте настроим привязки во View с учётом этого:
Благодаря тому, что в строке 9 xaml указан контекст данных, я могу настраивать привязки со всплывающими подсказками и не переживать о том, что я допустил опечатку
d:DataContext="{d:DesignInstance viewModel:FloorCreatorExampleViewModel}"
Давайте посмотрим, что получилось, в окне Revit:
Я выбрал 2 комнаты — я вижу 2 комнаты.
Список типов тоже на месте. Нужно поправить только размеры окна, и можно писать код по созданию пола.
Создание пола
Я решил создать статический класс с одним методом: он будет принимать на вход тип пола и список помещений, создавать полы, выделять их в модели, и ничего не возвращать. Не вижу тут смысла в создании объекта, поэтому делаю класс статическим. Код тут довольно простой, но и ситуацию я обрабатываю простую:
На что тут нужно обратить внимание:
- Строка 14: SpatialElementBoundaryOptions: вы можете поэкспериментировать с его настройками под ваши нужды
- Строка 24: GetBoundarySegments возвращает список списков сегментов. Как правило, в итоге нам нужен первый, по идее, в помещении могут быть внутренне контуры, так что будьте с этим внимательны: в примере я беру только первый, но он может не сработать в 100% случаев
- Строка 25: Линии в CurveLoop должны идти друг за другом, иначе на 31 строке код не выполнится.
- Проверить, корректный ли у вас контур пола, можно с помощью класса BoundaryValidation, чтоб не ловить исключения
- В случае, если у вас не получается создать пол, рисуйте линии по контуру вашего CurveLoop через NewDetailCurve, и ищите разрывы в них.
- Данный код не заводит полы в проёмы. Это довольно сложная задача, которую нельзя решить в несколько строк для всех возможных случаев (но решить её, разумеется, можно), поэтому я здесь и не привожу своих решений. Я решал такую задачу для конкретных случаев, с учётом стадий и корректировкой по именам семейств. Если у вас есть общее или хоть чуть-чуть обобщённое решение задачи по заведению пола в проёмы, можете написать мне. Возможно, я его опубликую.
В конце удалим комментарий с команды создания полов и передадим её аргументы:
Как видите, полы создадутся и без создания объекта FloorCreator, при этом статический метод CreateFloors отработает как чистая функция: выполнит действия, не меняя состояния других объектов нашего приложения.
Результат
Полы созданы (тут перекрылись с существующими):
И выделены в модели:
Заключение
Итоговый код вы можете посмотреть в моём репозитории на Github в этом коммите. Не забывайте подписываться на мой телеграм-канал о Revit API и ставить лайки статье. До новых встреч!