Перед выполнением требовалось решить, как реализовать задачу. Изменить под текущие задачи программу в dynamo или отказаться от наработок, используя только части алгоритма, и попробовать решение, с использованием только текстового программирования на языке IronPython (в дальнейшем ipy) при помощи надстройки RevitPythonShell (в дальнейшем RPS). Я выбрал второй вариант и нисколько в этом не разочаровался. Также возможно было реализовать это решение с помощью языка C# в программе Visual Studio, но в силу того, что я не имею достаточной квалификации, этот вариант пока не рассматривался.

Для выполнения скрипта применен плагин RPS, который представляет собой встроенный в Autodesk Revit интерпретатор IronPython, который выполняет код на ipy без использования Dynamo, что позволяет пользователю не переключаться между программами, также RPS может создавать новые панели и вынести кнопку на панель, при нажатии на которую будет выполнен скрипт, путь к которому прописан в файле настроек RevitPythonShell.xml. Еще одно преимуществом RPS это отсутствие искушения использовать ноды из загружаемых пакетов Dynamo, часто конфликтующих между собой, вызывая тем самым непредсказуемые результаты работы скрипта и осложняющие работу программиста.


При нажатии на соответствующую кнопку скрипт дает пользователю выделить плиту фальшпола в окне Revit, таким образом программа выдаст на выходе объект в виде экземпляра класса FootPrintRoof.

Как исходные данные возьмем имена типоразмеры для опорных стоек на 2, 3 и 4 балки.

Далее путем получения объекта типоразмера по имени из типоразмеров проекта с помощью соответствующей функции. Также понадобится значение уровня, на котором расположен фальшпол, для метода, который создаст экземпляры стойки и значение параметра “Смещение от уровня”, чтобы определить высоту будущих стоек.

Получим линии границ для экземпляра класса FootPrintRoof с помощью метода GetProfiles и свойства GeometryCurve.

Теперь получим линии разрезки фальшпола, используя свойства CurtainGrids и метод GetUGridLineIds(), который выдаст Id элемента, подадим его в следующую конструкцию – Document.GetElement(Id) и на выходе обнаружим элемент, который соответствует этому Id, из него извлечем линию соответствующую элементу с помощью свойства FullCurve

Получение точек для дальнейшего построения опорных стоек происходит путем анализа 2 групп линий, полученных выше на пересечение, в результате получаем 3 группы точек:

1 группа точек (corner_points) образуется путем пересечения линий границ плиты фальшпола между собой с удалением дубликатов, чтобы не было дубликатов стоек в дальнейшем. 

2 группа точек (inner_point) образуется путем пересечения массива линий разрезки фальшпола между собой.

3 группа точек (boundary_point) образуется путем пересечения массива линий разрезки фальшпола и линий границы плиты фальшпола.

Во всех трех случаях используются метод Intersect, класс SetComparisonResult, который показывает тип пересечения, и IntersectionResultArr, хранящий в себе результат пересечения.

Так как получить плоскость из объекта FootPrintRoof затруднительно из-за использования витражного остекления, панели которого являются плитками фальшпола, воспользуемся построением дополнительного объекта перекрытия из линий границ фальшпола. Добавим эти линии в экземпляр класса CurveArray() методом Append и построим по ним перекрытие c помощью следующей конструкции Document.Create.NewFloor. Затем с помощью свойств Geometry[Options()] и Faces получим плоскости элемента, чтобы вычислить плоскость, которая необходима, сделаем проверку на FaceNormal.Z не равно 0, получим 2 плоскости, каждая из которых будет лежать в плоскости XY. После необходимых манипуляций перекрытие удалим.

Ориентация, т.е. угол на который требуется повернуть стойку, определяется с помощью небольшого приращения координат X и Y и проецирования новой точки на плоскость, полученную в предыдущем шаге. Далее происходит проверка – лежит ли проекция точки с приращенными координатами на плоскости или нет. Теперь можно легко получить угол поворота стойки.

Сам поворот осуществляется с помощью свойств Location и метода Rotate класса Location, для которого необходимо получить вспомогательную линию вертикальной оси, вокруг которой будет происходить вращение.

Семейство создается с помощью следующей конструкции Document.Create.NewFamilyInstance(point, type, level, StructuralType), где point – точка вставки семейства, type – типоразмер нового семейства, level – уровень, на который будет вставлено семейство и StructuralType – представляет собой структурный тип.


В скрипте надстройка Dynamo использовалась для отладки и контроля правильности построения и получения геометрии, это возможно благодаря нодам python script from string, который “читает” код из файла, библиотекам RevitNodes и расширениям Revit.GeometryConversion и Revit.Elements, которые позволяют преобразовывать элементы Revit в элементы Dynamo, в том числе геометрические. Для преобразования экземпляров классов Line и BoundingBox используется метод ToProtoType(), а для Point – ToPoint(). И уже в Dynamo благодаря встроенному графическому движку возможно контролировать правильность выполнения кода при работе с геометрией. Dynamo мощный инструмент и списывать его со счётов при полном переходе на IronPython или C# не стоит.

Вторая часть скрипта временно выполнена в dynamo,но в будущем будет запускаться через RPS.

После того как пользователь вручную проложит лотки запустим скрипт, которая построит балки и консоли для крепления лотков.

В качестве исходных данных необходимо выбрать в окне Revit все стойки и лотки через нод Select Model Elements, из выпадающего списка нода Family Types выбрать имя типоразмера консоли и балки для получения объекта типоразмера с помощью нодов dynamo. В ноде Boolean выбрать True, чтобы создать балки или False, если они уже созданы и требуется построить только консоли.

На первом этапе проиcходит фильтрация выбранных элементов на стойки и лотки с помощью метода GetType().Name.

Из элемента стойки мы можем получить значение свойства “Уровень” для построения балок на этом уровне с помощью метода GetParameterValueByName() и значения пользовательского параметра “Высота для размещения балки”.

Теперь поработаем с лотками. Получим значения высотной отметки низа каждого из лотков и выделим уникальные с помощью индексатора Parameter для элемента лотка и объекта BuiltInParameter.RBS_CTC_BOTTOM_ELEVATION, который подадим в метод get_Parameter как аргумент. Теперь имея уникальные значения высотных отметок низа лотков , создадим для каждой из высотных отметок массив с лотками ,у которых такая же высотная отметка.

Для каждой базовой точки стойки построим линию во всем остальные точки вставки, теперь выделим среди этих линий только вертикальные и горизонтальные с помощью math.trunc(Line.Direction.X) == 0 и math.trunc(Line.Direction.Y)== 0. Функция trunc из модуля math используется для того, чтобы откинуть дробную часть , делается это из-за того ,что revit получает очень маленькие значения там где должен получать 0 и сравнение будет выдавать False, если не использовать trunc или подобную ему функцию. В итоге получаем линии для будущих балок

Для каждой из групп лотков проходим циклом и находим точку пересечения линии оси лотка и линии будущей балки с помощью метода Geometry.Geometry.DoesIntersect из пространства имен Autodesk.DesignScript происходит проверка на пересечение, а метод Geometry.Geometry.Intersect возвращает результат пересечения в виде точки. Теперь ищем ближайшую базовую точку стойки, чтобы вставить консоль

С помощью метода FamilyInstance.ByPointAndLevel из пространства имен Revit.Elements создаем элемент консоли и поворачиваем его с помощью метода SetRotation, угол для этого метода мы определяем с помощью пользовательской функции AngleByVector, которая анализирует линию на предмет ее направления и возвращает угол на который требуется повернуть консоль.

Если в ноде с пользовательским именем “Создать балки” стоит True и задан типоразмер балки, то будет выполнено построение балок по начальным точкам полученных линий с помощью того же метода, что и для создания консолей, поворот балки также определяется функцией AngleByVector.

В результате работы скриптов мы получим готовую модель фальшпола.

Автор: Андрей Рудин, программист САПР ООО “ИМ Консалт”