ADN Open CIS
Сообщество программистов Autodesk в СНГ

15/12/2014

Пересечение всех экземпляров семейств с элементом

Вопрос: Как я могу найти все колонны, которые пересекают заданную стену?

Ответ: В Revit API нет метода, который позволили бы определить отношения между колоннами и стеной.

Однако, можно довольно легко реализовать требуемую функциональность использую фильтр пересечения элементов, т.е. один из двух фильтров ElementIntersectsElementFilter или ElementIntersectsSolidFilter. Последниему фильтру требуется твердотельный объект (solid), а первому – элемент, который тем не менее будет также использовать геометрию твердотельного объекта. Для более подробной информации смотрите файл справки Revit API.

Мой бывший коллега Joe Ye написал небольшой код для демонстрации этого алгоритма:

Код - C#: [Выделить]
  1.   // Поиск пересечения между экземплярами семейства и заданным элементом.
  2.  
  3.   Reference Reference = uidoc.Selection.PickObject(
  4.     ObjectType.Element, "Выберите элемент, который нужно проверить на перечение со всеми экземплярами семейства" );
  5.  
  6.   Element e = doc.GetElement( reference );
  7.  
  8.   GeometryElement geomElement = e.get_Geometry(
  9.     new Options() );
  10.  
  11.   Solid solid = null;
  12.   foreach( GeometryObject geomObj in geomElement )
  13.   {
  14.     solid = geomObj as Solid;
  15.     if( solid = !null ) break;
  16.   }
  17.  
  18.   FilteredElementCollector collector
  19.     = new FilteredElementCollector( doc )
  20.       .OfClass( typeof( FamilyInstance ) )
  21.       .WherePasses( new ElementIntersectsSolidFilter(
  22.         solid ) );
  23.  
  24.   TaskDialog.Show( "Revit", collector.Count() +
  25.     " экземпляров семейства пересекаются с выбранным элементом ("
  26.     + element.Category.Name + "ID:" + element.Id + ")" );

Я не могу удержаться от того, чтобы немного почистить этот код и добавить немного LINQ запросов и других проверок:

  • Пропуск solid != null
  • Улучшить результирующее сообщение и добавить информацию об элементах
  • Перечислить идентификаторы элементов, пересекающихся с заданным элементом.

Вот что получилось в итоге:

Код - C#: [Выделить]
  1. /// <summary>
  2. /// Извлекает все экземпляры семейств,
  3. /// которые пересекаются с заданным элементом, например, колонны
  4. /// пересекающие стену
  5. /// </summary>
  6. void GetInstancesIntersectingElement( Element e )
  7. {
  8.   Document doc = e.Document;
  9.  
  10.   Solid solid = e.get_Geometry( new Options() )
  11.     .OfType<Solid>()
  12.     .Where<Solid>( s => null != s && !s.Edges.IsEmpty )
  13.     .FirstOrDefault();
  14.  
  15.   FilteredElementCollector intersectingInstances
  16.     = new FilteredElementCollector( doc )
  17.       .OfClass( typeof( FamilyInstance ) )
  18.       .WherePasses( new ElementIntersectsSolidFilter( solid ) );
  19.  
  20.   int n = intersectingInstances.Count<Element>();
  21.  
  22.   string result = string.Format(
  23.     "{0} экземпляров семейств пересекают "
  24.     + "выбранный элемент",
  25.     n);
  26.  
  27.   string id_list = 0 == n
  28.     ? string.Empty
  29.     : string.Join( ", ",
  30.         intersectingInstances
  31.           .Select<Element, string>(
  32.             x => x.Id.IntegerValue.ToString() ) )
  33.       + ".";
  34.  
  35.   Util.InfoMsg2( result, id_list );
  36. }

Результат

В примере FindReferencesByDirection/FindColumns из Revit SDK есть подходящая модель для тестирования метода - FindColumns-Basic.rvt.

 

Когда я выполнил команду и выбрал прямую стену, я получил такое сообщение:

 

Немного неожиданно. Если мы посмотрим на стену, то увидим гораздо больше пересечений.

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

Я добавил другой код, чтобы сравнить результат фильтра ElementIntersectsElementFilter с ElementIntersectsSolidFilter.

Код - C#: [Выделить]
  1.   Solid solid = e.get_Geometry( new Options() )
  2.     .OfType<Solid>()
  3.     .Where<Solid>( s => null != s && !s.Edges.IsEmpty )
  4.     .FirstOrDefault();
  5.  
  6.   FilteredElementCollector intersectingInstances
  7.     = new FilteredElementCollector( doc )
  8.       .OfClass( typeof( FamilyInstance ) )
  9.       .WherePasses( new ElementIntersectsSolidFilter(
  10.         solid ) );
  11.  
  12.   int n1 = intersectingInstances.Count<Element>();
  13.  
  14.   intersectingInstances
  15.     = new FilteredElementCollector( doc )
  16.       .OfClass( typeof( FamilyInstance ) )
  17.       .WherePasses( new ElementIntersectsElementFilter(
  18.         e ) );
  19.  
  20.   int n = intersectingInstances.Count<Element>();
  21.  
  22.   Debug.Assert( n.Equals( n1 ),
  23.     "ожидалось что пересечение твердотельного объекта будет эквивалентно пересечению элемента" );

Результат был тот же самый.

Объяснение

Как оказалось, присоединенная геометрия удаляет пересечения.

Scott Conver объяснил мне что к чему.

Оба фильтра работают так, как задумано.

Бетонные колонны, пересекающие стену, присоединены к стене. Поэтому и геометрия этих колонн присоединена к стене, соответственно и фильтр не находит эти колонны, так как фактически нет пересечения со стеной.

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

Добавление от Joe Ye.

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

Источник: http://thebuildingcoder.typepad.com/blog/2014/11/determining-intersecting-elements-and-continued-futureproofing.html

Обсуждение: http://adn-cis.org/forum/index.php?topic=1701

Опубликовано 15.12.2014