Расскажу, как с помощью Динамо определить уровень элемента. За уровень элемента примем ближайший уровень снизу от элемента. Таким образом вы сможете проанализировать элементы в модели и записать в указанный параметр имя этажа или ещё что-то. В примере будем записывать имя уровня, то есть примем имя этажа равным имени уровня. Имена нодов в Динамо буду показывать курсивом.
Работать буду только со стандартными нодами в Динамо для Ревита 2021. Версия Динамо — 2.6.1.
Скрипт из статьи выложу в сообществе ВКонтакте для донов сообщества.
Алгоритм
Для начала проговорю последовательность действий и основные идеи.
- Получаем список элементов для обработки. Это могут быть все элементы на виде, все элементы каких-то категорий, тут выбор зависит от того, что конкретно хотите обрабатывать.
- Если в модели есть стояки, то нужно решить, что с ними делать. Вариантов три: не обрабатывать вообще; обрабатывать только те, что идут в пределах одного этажа; делить стояк на отрезки по уровням. Мы в статье будем игнорировать трубы и воздуховоды вообще, так как они добавят сложности алгоритму. Анализ линейных элементов лучше рассмотреть в отдельной статье, так как тут тоже много нюансов и условий.
- У отфильтрованного списка элементов нужно получить абсолютные отметки внутри модели. В инженерке для большинства случаев это либо точка, либо линия. Точка для загружаемых семейств вроде оборудования, арматуры и фитингов. Линия — для труб и воздуховодов. Если линия горизонтальная, то есть это не стояк, то можно взять любую точку на ней.
- Далее нужно получить уровни из модели, их имена и отметки.
- Сравниваем отметку каждого элемента с отметками всех уровней и ищем ближайший снизу. Если взять уровни и упорядочить по отметки от верхнего к нижнему, то первый уровень, чья отметка будет ниже отметки элемента, является ближайшим снизу.
- Имя такого уровня и будем записывать в параметр элемента.
Погнали.
Получение элементов
Здесь можете воспользоваться любым способом, который я описывал в отдельном материале про получение элементов в Динамо. Я для примера соберу все элементы по категориям. Если какой-то категории нет в проекте, то придёт пустой список, его можно без проблем отсеять через List.Flatten.
В моём примере проект ВК на 21 жилой этаж. То есть вполне себе обычная секция жилого дома и типичный проект для пользователя. В проекте, если выделять мышкой на 3Д, 14.5 тысяч элементов. Из них точечно размещаемых элементов, то есть не труб и изоляции, около 11 тысяч. Это не очень много элементов с точки зрения скорости работы скрипта, за минуту справится. У меня на моей машине обработка заняла около 45 секунд, а у меня не супер мощный ноутбук.
Теперь нужно собрать аналогично уровни:
Обработка уровней
Нам нужно получить отметку уровней и их имена. Для этого есть стандартные ноды Level.Name и Level.Elevation. Отметку уровня лучше сразу подать на нод для округления значений, чтобы убрать хвосты вида 499,999999995 или 1050,0000002.
Теперь мне нужно упорядочить все уровни по их отметкам сверху вниз. То есть сначала будет самый высокий уровень, потом ниже и ниже. Для этого сортирую по отметке имена уровней с помощью нода List.SortByKey. На вход list подаю имена, на вход keys — отметки. В итоге нод упорядочить всё от меньшей высоты к большей, как имена, так и отметки. Чтобы развернуть наоборот, от более высокого уровня к более низкому, переворачиваю списки нодом List.Reverse.
Справа у меня ещё ноды List.Deconstruct. Этот нод отделяет самый первый элемент списка от всех остальных. В моём случае это просто подчистка данных, так как из модели приходит уровень парапета, а мне он явно не нужен. Вы тоже должны внимательно проверить, что за уровни пришли, и выкинуть все лишние.
Отметка элементов
Я специально взял только загружаемые семейства, так как все они вставляются за точку. Соответственно, когда я получу координату элемента, это будет точка вставки. У точки мне нужна координата Z — она сразу «придёт» как абсолютная, а не как расстояние от уровня размещения до элемента. Именно такая отметка мне и нужна. Получаю точку нодом Element.GetLocation.
Если на этот нод подать трубу, или воздуховод, или изоляцию, то геометрией вставки элемента будет не точка, а отрезок, так как трубу и воздуховод строим через указание двух точек. Так как всё это нужно отдельно обрабатывать, поэтому пока опускаем такие элементы.
Потом в Код блоке пишу P.Z, здесь P — просто имя переменной, можете тут написать любой символ, а Z через точку — это команда для получения координаты Z от точки. Далее округляю на всякий случай отметку. Ниже Код блок с буквой а — это тоже переменная, в неё подаю список элементов, потому что потом в эти же элементы будем писать имя уровня, это просто для удобства, чтобы лапша не пересекала другие ноды.
Поиск ближайшего уровня
Теперь нужно взять отметки уровней, отметки элементов и сравнить их. Мы специально выше отсортировали отметки уровней так, чтобы они шли от верхнего к нижнему. Потому что теперь будем сравнивать отметки элементов: если отметка элемента больше или равна отметке уровня, значит, элемент принадлежит этому уровню.
Беру нод больше или равно, подаю на x отметки элементов, а на y — отметки уровней. Мне нужно сравнить отметку каждого элемента с каждой отметкой уровня. Для этого включаю уровень L1 для отметок элементов — так я беру каждую-каждую отметку элементов. И вдобавок включаю переплетение векторное, чтобы каждая отметка элемента проверялась с каждой отметкой уровня. Читайте про уровни списков и переплетения в отдельной статье.
В итоге получаю подсписок для каждого элемента и там значения true или false. Если элемент выше уровня, то true, если ниже, то false. Там нужны только значения true, причём важно получить самое первое true, все остальные не нужны.
Вот пример. Пусть у нас элемент на отметке 3500 мм. Есть уровни 1, 2 и 3 этажа с отметками 0, 2500, 5000 мм. Глазами очевидно, что элемент у нас принадлежит уровню с отметкой 2500 мм. Но если переводить это в булеву математику, то получится такая картина.
Упорядочиваю уровни по отметкам сверху вниз, получается: 5000, 2500, 0 мм. Это 3 этаж, 2 этаж и 0 этаж соответственно. Сравниваю отметку 3500 мм с уровнями по больше или равно, получаю: false, true, true. Уровень 3 этажа выше — там false, он нам не подходит. Следующий уровень 2 этажа, это наш уровень для записи и у него первое true. Но наш элемент выше и первого уровня на 0 мм, поэтому там тоже true. А для записи значения нам нужен именно первый true в списке, иначе будет ошибка.
Само по себе значение true нам не даёт имя уровня. Но мы можем получить индекс значения, а потом по тому же индексу взять имя уровня. Мы специально упорядочили имена уровней по отметкам, в итоге получили два списка, в которых имя и отметка строго соответствуют друг другу. Значит, если мы получаем индекс 3 для списка с отметками, то в списке с именами тоже будет 3-й элемент с именем.
Тут мы воспользуемся нодом List.IndexOf — он возвращает из списка индекс первого вхождения. На русском языке это значит, что нод ищет значение в списке, если находит его, то берёт порядковый номер (индекс) и выдаёт его как результат, всё остальное дальше не проверяет. Если не находит, то вернёт -1. В этом главное отличие этого нода от нода List.AllIndicesOf — последний вернёт вам список индексов всего, что нашёл. Нам же как раз нужно только первое true, а не все вообще.
Мой список для обработки нодом List.IndexOf — это подсписки с true и false. Ищу я true. Поэтому подаю на вход «list» свой список с true и false, а для обработки именно подсписков включаю второй уровень входа. На вход «element» подаю true.
Возможна ситуация, когда элемент у вас будет за пределами списка — это ситуация, когда элемент лежит ниже самого нижнего уровня. То есть у него отрицательная отметка от самого низкого уровня. У меня такие элементы в модели были, я не знаю, что там, я не анализировал. Они есть и с ним надо что-то делать.
Раз в модели больше нет уровней ниже, то нужно назначить им ближайший уровень, то есть самый нижний. Ну вот мало ли у вас выпуск канализации из дома, он идёт по подвалу к колодцу, а колодец далеко и из-за уклона ваши элементы канализации оказываются ниже уровня подвала. Тогда их надо куда-то определить, очевидно, что это будет самый нижний уровень в модели, то есть уровень подвала.
Чтобы это сделать, делаю вот что: добавляю Код блок, в котором пишу короткую формулу:
a == -1? b : a;
Это по сути то же самое, что формула в семействе с условием IF. В начале пишу самое условие: "a == -1?", то есть «Если значение в переменной «а» равно -1». В переменную будут приходить индексы из списка. Если пришло -1, то значит, Динамо не нашёл отметку уровня, которая ниже отметки элемента.
Значит, нужно получить самый нижний уровень, это последний индекс в списке с уровнями. Чтобы его получить, считаю нодом List.Count количество уровней и отнимаю один, так как индексы отсчитываются от нуля, а не от единицы. И в переменную b подаю это значение.
В рамках одной модели оно будет постоянным, но в разных проектах будет отличаться. Поэтому нужна формула. В переменную b будет приходить индекс последнего уровня в списке, а последний у нас — самый нижний уровень.
Нодом List.GetItemAtIndex получаем значение по индексу. Индекс у нас соответствует первому уровню, на который получили true. Получаем данные из списка с именами уровней, поэтому подаю на вход «list» именно его. А на «index» — результат нашей формулы в Код блоке.
В результате для каждого элемента получаю имя уровня. Его и будем записывать в параметр элементов.
Запись значений
Тут всё просто: берём нод Element.SetParameterByName, в него подаём список элементов, имя параметра для записи и само значение с именем уровня. На скриншоте у меня нод выдал ошибку, ниже объясню, с чем она связана.
При этом нужно понимать, что мы изначально получали все элементы модели скопом, некоторые из них могут быть общими вложенными семействами. И у некоторых элементов при этом ещё могут быть нужные нам параметры связаны с родительскими параметрами. В этом случае скрипт выдаст ошибку, ведь параметр будет заблокирован формулой.
Чтобы такого не было, можно делать вот что:
- Для записи значений используйте только те параметры, которые не добавляли в семейства, особенно в общие вложенные. Если обрабатываете автоматизацией, то «ADSK_Этаж» не нужно добавлять ни в родительские, ни в общие вложенные. Это разумно исключительно для ручного заполнения, но не для автоматизации.
- Можете создавать отдельный параметр проекта, не общий параметр. Тогда его гарантированно нигде не будет. Это хороший вариант, когда вам нужно только отфильтровать элементы в спецификациях. Если же есть требования заказчик на конкретный общий параметр, а он у вас заблокирован формулами, то вот третий вариант.
- Можно на свой страх и риск удалить этот параметр из всего проекта, а уже после добавить как параметр проекта. Подробнее ниже.
Вот у меня такая ситуация и случилась. Я взял модель из тех, что мне присылали пользователи шаблонов ADSK для тестов. То есть это реальная модель, над которой работали в обычном режиме и по которой выдавали проект. И в при записи значений выяснил, что параметр «ADSK_Этаж» есть в некоторых семействах и в общих вложенных он заблокирован формулой. Видимо, ребята заполняли руками.
Мне нужно записать в этот параметр значение, больше нигде этот параметр мне не нужен. Возможно, ребята его где-то использовали, но мне он не нужен.
Поэтому я иду в Ревит Лукап — это такой плагин, который показывает содержимое базы данных любого проекта в Ревите, жму в меню Snoop DB и нахожу в списке слева SharedParameterElement, раскрываю.
Мне повезло, у него очень простой айди — 3000. Дальше иду на вкладку «Управление», команда «Выбрать по коду». Ввожу значений айди, нажимаю ОК. Внешне ничего не происходит, но в правом нижнем углу будет видно, что в модели выбран один элемент. Это наш общий параметр. Жму Del — удаляю параметр из базы данных проекта.
Важно! После такого действия вы удаляете параметр из всего вообще проекта. Из всех семейств, из всех спецификаций, отовсюду вообще. Все значения, если они были, теряются.
Теперь снова добавляю этот же параметр как параметр проекта по экземпляру ко всем категориям, которые мне нужны. Добавляйте его так, чтобы он менялся по экземплярам группы, мало ли где у вас будут группы, которые будете копировать по этажам.
Теперь можно записывать значения в параметр. Сделайте текстовый нод String, два раза нажмите по его заголовку, появится поле для переименования. Впишите туда понятный текст на вашем языке, так как это поле будем выводить в Проигрыватель Динамо, чтобы пользователь мог сам вписать имя параметра, куда нужно занести имя уровня. Чтобы поле отобразилось в Проигрывателе, нажмите по ноду правой кнопкой мыши и поставьте галочку «Является вводом».
Вот и всё, имена уровней записываются в элементы, дальше можно фильтровать или группировать спецификации по именам этажей.
Сделайте скрипт сами по скриншотами или становитесь платными подписчиками моего сообщества в ВК и скачивайте готовый скрипт. Я делал его в Динамо для Ревита 2021, поэтому сможете открыть скрипт в любом Динамо в Ревитах от 2021 и более новых. В 2019 открыть не получится.