Оформление сборок в Revit. Вращение видов, начало сборки

от автора

Сборки в Revit – это инструмент для объединения нескольких элементов модели в единый блок, позволяющий создавать виды и спецификации применительно только к этому блоку без необходимости вводить дополнительные фильтры. Преимущество сборок над группами заключается именно в автоматическом создании обособленных видов, и их удобной группировке в диспетчере проекта.

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

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

О порядке создания сборок и их видов можно прочитать в справке Autodesk или в других источниках, повторять эту информацию я не буду, остановлюсь подробно только на одном моменте.

При попытке создать виды сборок для горизонтального и вертикального элемента вы обнаружите, что на виде в плане элементы ориентированы так же, как в модели, то есть горизонтально и вертикально соответственно. А на вертикальных сечениях, имеющих одинаковые названия, отображаются разные грани сборок. Например, вид спереди горизонтальной панели покажет ее длинную сторону, а вертикальной – торцевую.

Это происходит из-за того, что Начало сборки при ее создании всегда ориентировано в направлении глобальных осей координат.

Начало сборки – это геометрический 3D-объект, представляющий из себя локальную систему координат. Красная, зеленая и синяя стрелки указывают положительное направление осей X, Y и Z соответственно. Увидеть его можно, перейдя в режим редактирования сборки на плане, разрезе или в 3D. Именно этот объект определяет, как будут ориентированы виды сборки.

В русскоязычной версии Revit название этого объекта используется во множественном числе, то есть «Начала сборки», вероятно, предполагая, что этих начал три, по количеству осей координат. Для простоты я буду использовать его в единственном числе, как и в англоязычной версии Revit (Assembly Origin).

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

Такое положение дел нас не устраивает. Часто важно, чтобы оформление сборок было единообразным. Например, для железобетонной панели вид в плане принято располагать горизонтально на листе, а план и фронтальный вид должны быть соосны. Это упрощает чтение чертежей.

Для сборок, расположенных не под прямым углом, это и вовсе критично, так как виды должны быть параллельны плоскости основных элементов сборки.

Борьба врукопашную

Первое, что приходит в голову для решения проблемы, это повернуть виды на листе, используя стандартный функционал Revit.

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

Однако, все это неизбежно приведет к дополнительным сложностям в порядке чтения текста размеров, повороте заголовка видовых экранов, невозможности «примагнитить» виды на листе или неожиданным результатам при экспорте в dwg. К тому же, придется держать в голове, что вид спереди это не всегда фронтальная плоскость элемента, а на вид сзади нужно назначить шаблон для вида сбоку 😵

Есть более чистое решение – повернуть начало сборки до создания видов.

Сделать это можно как вручную, так и автоматически, используя Dynamo. Для начала рассмотрим ручной способ.

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

Эталонное положение будет выглядеть так:

Для поворота начала вручную необходимо зайти в режим редактирования сборки, выбрать начало и повернуть его вокруг оси Z на нужный угол, используя стандартный инструмент Повернуть. В итоге, для всех положений панели начало сборки должно выглядеть так (ось Y направлена к внутренней грани панели):

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

Эта простая манипуляция экономит часы оформления, позволяя избежать необходимости поворачивать виды на листах или границы обрезки. Шаблоны видов можно будет настроить один раз и не жонглировать ими при создании видов на сборку с другой ориентацией. Все по красоте, как мы и хотели.

Когда сборок в проекте большое количество (а с панелями это всегда так), возникает потребность в автоматизации поворота начала. Рассмотрим, как это сделать, используя Dynamo и IronPython. Для этого придется немного углубиться в Revit API.

Что есть начало

В Revit API начало сборки принадлежит к классу Transform, то есть является трансформацией элемента. Трансформация – это матрица, содержащая в себе данные о координатах элемента и его повороте относительно глобальных осей.

Basis – набор векторов в векторном пространстве.

BasisX, BasisY, BasisZ – векторы единичной длины, определяющие направление осей локальной системы координат. Каждый базисный вектор имеет три координаты, эти координаты определяют конечную точку вектора. Начальной точкой является Origin.

Origin – начало локальной системы координат, по-другому эту матрицу называю сдвигом или переносом локальной системы координат относительно глобальной. А Basis – поворотом.

Получаем начало сборки

Подготовим небольшой скрипт в Dynamo, который будем наполнять по ходу работы.

В Python Script импортируем необходимые модули, определяем переменные doc и selection с экземпляром текущего документа и входными данными.

import clrclr.AddReference('RevitServices')from RevitServices.Persistence import DocumentManagerfrom RevitServices.Transactions import TransactionManagerclr.AddReference("RevitAPI")from Autodesk.Revit import DBdoc = DocumentManager.Instance.CurrentDBDocument# получаем сборкуselection =  UnwrapElement(IN[0])

В переменную selection мы передаем сборку – это экземпляр класса AssemblyInstance. Начало сборки получаем, используя метод GetTransform этого класса. Свойства Origin, BasisX, BasisY, BasisZ класса Transform вернут положение начала сборки и направление его векторов.

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

# получаем трансформацию сборкиtransform = selection.GetTransform()OUT = transform.Origin, transform.BasisX, transform.BasisY, transform.BasisZ

Попробуем повернуть начало вручную и снова получить свойства трансформации. Как видно, координаты векторов изменились.

Кручу-верчу

Для того, чтобы повернуть начало, нужно воспользоваться методом SetTransform класса AssemblyInstance. Этот метод принимает в качестве аргумента объект трансформации. Значит, мы должны передать в него повернутую трансформацию.

selection.SetTransform(rotated_transform)

Для создания повернутой трансформации первое, что приходит в голову, это воспользоваться методом CreateRotation класса Transfrom. Метод принимает два аргумента: ось вращения и угол поворота. Поворот всегда происходит против часовой стрелки.

Вращать мы будет относительно оси Z, получим ее из объекта трансформации как transform.BasisZ.

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

1.     Передаем угол в метод CreateRotation. Угол обозначу переменной angle и присвою ему значение 0.785 в радианах (это примерно 45 градусов)

angle = 0.785# создаем повернутую трансформациюrotation = transform.CreateRotation(transform.BasisZ, angle)

Интуитивно кажется, что эта новая трансформация и есть то, что нам нужно, ведь мы создали вращение вокруг оси Z текущей трансформации и вообще применяем этот метод к текущей трансформации. Но это не так. Если вы попробуете передать переменную rotation в метод SetTransform, то увидите, что начало сборки сместилось во внутреннее начало проекта. То есть rotation – это трансформация, у которой есть поворот, но нет сдвига. Об этом говорит документация к Revit API:

CreateRotation – это метод, создающий новый повернутый объект трансформации в точке с координатами (0, 0, 0)

CreateRotation – это метод, создающий новый повернутый объект трансформации в точке с координатами (0, 0, 0)

2.     Чтобы получить итоговую трансформацию, нужно применить метод Multiply, позволяющий перемножить (объединить) две трансформации, и результат передать в метод SetTransform, не забывая про открытие и закрытие транзакции.

import clrclr.AddReference('RevitServices')from RevitServices.Persistence import DocumentManagerfrom RevitServices.Transactions import TransactionManagerclr.AddReference("RevitAPI")from Autodesk.Revit import DBdoc = DocumentManager.Instance.CurrentDBDocument# получаем сборкуselection =  UnwrapElement(IN[0])# получаем трансформацию сборкиtransform = selection.GetTransform()angle = 0.785# создаем повернутую трансформациюrotation = transform.CreateRotation(transform.BasisZ, angle)rotated_transform = transform.Multiply(rotation)# применяем трансформацию к сборкеTransactionManager.Instance.EnsureInTransaction(doc)selection.SetTransform(rotated_transform)TransactionManager.Instance.TransactionTaskDone()OUT = 'Начало успешно повернуто на угол {}'.format(angle)

Скрипт работает, начало повернулось на 45 градусов против часовой стрелки.

Если бы мы точно знали, на какой угол нужно повернуть трансформацию, но мы точно не знаем, на какой угол нужно повернуть трансформацию

Чтобы определить, на какой угол повернуть начало, необходимо получить положение компонентов сборки, точнее, достаточно какого-то одного определяющего компонента. Назову его главным элементом сборки.

Когда панель – это загружаемое семейство, она и будет главным элементом, если же панель собрана из отдельных стен, проемов и закладных деталей, то главным элементом будет стена (это не обязательно, но представляется довольно логичным). Как правило, мы назначаем категорию именования сборки при ее создании по главному элементу.

Соответственно, главный элемент можно найти, сравнив категории компонентов с категорией именования сборки.

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

import clrclr.AddReference('RevitServices')from RevitServices.Persistence import DocumentManagerfrom RevitServices.Transactions import TransactionManagerclr.AddReference("RevitAPI")from Autodesk.Revit import DBdoc = DocumentManager.Instance.CurrentDBDocument# получаем сборкуselection =  UnwrapElement(IN[0])# получаем трансформацию сборкиtransform = selection.GetTransform()# получаем id компонентов сборкиmember_ids = selection.GetMemberIds()# получаем сами компонентыmembers = [doc.GetElement(id) for id in member_ids]# получаем id категории именования сборкиcat = selection.NamingCategoryId# сравниваем категории компонентов с категорией именования, получаем главный элементmain_member = [member for member in members \               if member.Category.Id == cat][0]

В том случае, когда компонентов категории несколько, например, сборка состоит из нескольких стен, в переменную main_member попадет одна из них, в смысле какая-то. Нас такой расклад в принципе устраивает, но, если вы хотите выделить конкретную стену, например, несущий слой, нужно будет добавить отдельный критерий фильтрации.

Главный элемент — загружаемое семейство

Экземпляр загружаемого семейства, который мы получим в переменной main_member – это экземпляр класса FamilyInstance. Его свойство Location вернет положение экземпляра семейства, а свойство Rotation класса Location, в свою очередь, вернет угол поворота, относительно базового положения, определенного в редакторе семейств. Угол всегда будет положительным от 0 до 360 вне зависимости от того, в какую сторону вы вращаете элемент.

Кстати, через свойство Location можно получить и положение сборки, однако, оно вернет не начало сборки, а ее геометрический центр.

Если мы имеем дело со сборкой, начало которой расположено по умолчанию (в направлении глобальных осей), то искомый угол поворота легко получить из угла поворота семейства.

import clrimport mathclr.AddReference('RevitServices')from RevitServices.Persistence import DocumentManagerfrom RevitServices.Transactions import TransactionManagerclr.AddReference("RevitAPI")from Autodesk.Revit import DBdoc = DocumentManager.Instance.CurrentDBDocument# получаем сборкуselection =  UnwrapElement(IN[0])# получаем трансформацию сборкиtransform = selection.GetTransform()# получаем id компонентов сборкиmember_ids = selection.GetMemberIds()# получаем сами компонентыmembers = [doc.GetElement(id) for id in member_ids]# получаем id категории именования сборкиcat = selection.NamingCategoryId# сравниваем категории компонентов с категорией именования, получаем главный элементmain_member = [member for member in members \               if member.Category.Id == cat][0]# получаем угол поворота семействаangle = main_member.Location.Rotation + math.pi# создаем повернутую трансформациюrotation = transform.CreateRotation(transform.BasisZ, angle)rotated_transform = transform.Multiply(rotation)# применяем трансформацию к сборкеTransactionManager.Instance.EnsureInTransaction(doc)selection.SetTransform(transform)TransactionManager.Instance.TransactionTaskDone()OUT = 'Начало успешно повернуто на угол {}'.format(angle)

Я добавила pi к углу поворота семейства для того, чтобы довернуть начало еще на 180 градусов, потому что хочу, чтобы вид спереди сборки показывал наружную грань панели, для этого ось Y начала должна смотреть в направлении внутренней грани.

Скрипт работает, однако, такой подход не учитывает сценарий, когда начало уже было повернуто: его могли повернуть вручную, либо сборка была повернута целиком. В таком случае, необходимо получить текущий поворот начала сборки. Его можно найти как угол между базисным вектором начала и тем же вектором глобальной системы координат.

# угол поворота началаcurrent_angle = transform.AngleTo(DB.XYZ.BasisY)

Метод AngleTo класса XYZ всегда возвращает положительное значение угла в диапазоне от 0 до 180 градусов. На этом этапе поиск угла поворота превращается в довольно муторное занятие с подключением тригонометрии, ведь, помимо угла между векторами, нам нужно понять, в каком квадранте находится вектор трансформации.

Метод AngleTo вернет одинаковые значения для различных положений начала

Метод AngleTo вернет одинаковые значения для различных положений начала

К счастью, есть способ, позволяющий вообще не использовать углы и вращение.

Переопределение базисных векторов трансформации

Экземпляры загружаемых семейств, как и сборки, имеют трансформации. Метод GetTransform класса Instance (родительского класса FamilyInstance) вернет объект трансформации экземпляра семейства. Получив базисные векторы трансформации семейства и прировняв к ним соответствующие базисные вектора начала сборки, мы получим поворот начала, не задействуя углы и методы вращения.

import clrclr.AddReference('RevitServices')from RevitServices.Persistence import DocumentManagerfrom RevitServices.Transactions import TransactionManagerclr.AddReference("RevitAPI")from Autodesk.Revit import DBdoc = DocumentManager.Instance.CurrentDBDocument# получаем сборкуselection =  UnwrapElement(IN[0])# получаем трансформацию сборкиtransform = selection.GetTransform()# получаем id компонентов сборкиmember_ids = selection.GetMemberIds()# получаем сами компонентыmembers = [doc.GetElement(id) for id in member_ids]# получаем id категории именования сборкиcat = selection.NamingCategoryId# сравниваем категории компонентов с категорией именования, получаем главный элементmain_member = [member for member in members \               if member.Category.Id == cat][0]# переопределяем базисные вектора трансформацииtransform.BasisX = - main_member.GetTransfrom().BasisXtransform.BasisY = - main_member.GetTransfrom().BasisY# применяем трансформацию к сборкеTransactionManager.Instance.EnsureInTransaction(doc)selection.SetTransform(transform)TransactionManager.Instance.TransactionTaskDone()OUT = selection

Это работает благодаря тому, что базисные векторы задаются точками в локальной системе координат, передав точки из одной системы в другую, мы получим новое направление вектора, origin при этом останется на месте.

Что делать, если главный элемент сборки – стена?

Для стен нельзя получить трансформацию, так как это системное семейство. Положение стены можно определить через свойство Orientation класса Wall. Это свойство возвращает вектор, направленный в сторону наружной поверхности стены. Напомню, что наружная поверхность в Revit обозначена стрелками.

Базисный вектор Y начала сборки должен быть противоположен вектору ориентации стены, а базисный вектор X мы можем получить как векторное произведение между базисным вектором Y и Z.

transform.BasisY = - main_member.Orientationtransform.BasisX = (- main_member.Orientation).CrossProduct(DB.XYZ.BasisZ)

Вот так можно вращать начало сборки для стандартизации и ускорения оформления чертежей на изделия из загружаемых семейств или стен, не зарываясь в расчете углов. По-моему, получился простой, изящный и надежный способ.

По ссылке приложила скрипт с кодом для двух разных вариантов, работающий для одной сборки.

Скрипт собран в версии Dynamo 2.12 под Revit 2022.1

Полезные ссылки для понимания матрицы трансформации в общем и класса Transform в Revit API:

Матрицы перехода

Матрицы поворота

Трансформации

Класс Transform в Revit API

ссылка на оригинал статьи https://habr.com/ru/articles/1027214/