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

24/04/2015

Использование метода FindInserts для поиска всех проемов в стене

Недавно я представил новый пример использования класса SpatialElementGeometryCalculator для вычисления полезной и общей площади стен.

В статье обсуждалось несколько интересных моментов:

  • Использование класса SpatialElementGeometryCalculator
  • Портирование надстройки, написанной на VB.NET в C#
  • Использование временной транзакции для решения задач
  • Использование FilteredElementCollector для поиска всех проемов в стене
  • Оптимизация поиска при использовании FilteredElementCollector

Пользователь Vilo оставил комментарий к этой статье с вопросом:

Вопрос: Почему бы просто не использовать метод (wall as  HostObject).FindInserts() для определения того, какие объекты располагаются на заданной стене?

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

Ответ: Очень правильный вопрос.

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

Спасибо за ценную подсказку.

Кстати, не нужно явно приводить тип wall к HostObject, так как класс Wall и так является наследником класса HostObject.

Добавлено позже: Кажется я понял что вы имели ввиду. В коде переменная wall имеет тип Element.

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

Код - C#: [Выделить]
  1.   // Этот подход гораздо эффективнее
  2.   // чем использование FilteredElementCollector
  3.   IList<ElementId> inserts = ( wall as HostObject )
  4.     .FindInserts( true, true, true, true );
  5.  
  6.   Debug.Assert(
  7.     lstTotempDel.Count.Equals( inserts.Count ),
  8.     "Ожидалось, что FindInserts вернет то же самое количество" );

Проект SpatialElementGeometryCalculator обновлен на GitHub.

Я передал эту информацию Phillip Millier, который дал мне первоначальный код на VB.NET и вот что он ответил:

Ответ: Я уже оптимизировал использование FilteredElementCollector по вашим советам, но я также как и вы не знал о методе FindInserts.

Сегодня я созванивался со своими клиентами и они сказали, что плагин работает нормально со стенами, на которых находятся экземпляры семейств. А с составными cтенами, которые содержать фальш-стену, это не работает.

Как вы видите, в своем коде я сравнивал FamilyInstance.HostId с wall.Id. В случае с составными стенами, проемы на фальш-стене имеют другой идентификатор, отличный от FamilyInstance.HostId и поэтому проемы не находились.

Метод FindInserts решает эту проблему.

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

Реализовать решение проблемы с составными стенами безусловно также можно и с помощью FilteredElementCollector, только более сложным и запутанным способом.

Использование FindInserts делает код значительно проще.

Вот что у меня получилось:

Код - C#: [Выделить]
  1. [Transaction( TransactionMode.Manual )]
  2. public class Command : IExternalCommand
  3. {
  4.   /// <summary>
  5.   /// Конвертация квадратных футов в квадратные метры
  6.   /// </summary>
  7.   double sqFootToSquareM( double sqFoot )
  8.   {
  9.     return Math.Round( sqFoot * 0.092903, 2 );
  10.   }
  11.  
  12.   /// <summary>
  13.   /// Вычисление площади стен, за исключением проемов.
  14.  /// Временно удаляем двери и окна в транзакции, которую затем откатываем.
  15.   /// </summary>
  16.   /// <param name="subfaceArea">Первоначальная площадь поверхности стены</param>
  17.   /// <param name="wall"></param>
  18.   /// <param name="doc"></param>
  19.   /// <param name="room"></param>
  20.   /// <returns></returns>
  21.   double calwallAreaMinusOpenings(
  22.     double subfaceArea,
  23.     Element wall,
  24.     Room room )
  25.   {
  26.     Document doc = wall.Document;
  27.  
  28.     // есть ли более надежный способ сравнить документы?
  29.  
  30.     Debug.Assert(
  31.       room.Document.ProjectInformation.UniqueId.Equals(
  32.         doc.ProjectInformation.UniqueId ),
  33.       "Ожидалось, что помещение и стена будут в одном проекте" );
  34.  
  35.   // Определяем все проемы в стене.
  36.     IList<ElementId> inserts = ( wall as HostObject )
  37.       .FindInserts( true, true, true, true );
  38.  
  39.     // Определяем общую площадь проемов.
  40.  
  41.     double openingArea = 0;
  42.  
  43.     if( 0 < inserts.Count )
  44.     {
  45.       Transaction t = new Transaction( doc );
  46.  
  47.       double wallAreaNet = wall.get_Parameter(
  48.         BuiltInParameter.HOST_AREA_COMPUTED )
  49.           .AsDouble();
  50.  
  51.       t.Start( "tmp Delete" );
  52.       doc.Delete( inserts );
  53.       doc.Regenerate();
  54.       double wallAreaGross = wall.get_Parameter(
  55.         BuiltInParameter.HOST_AREA_COMPUTED )
  56.           .AsDouble();
  57.       t.RollBack();
  58.  
  59.       openingArea = wallAreaGross - wallAreaNet;
  60.     }
  61.  
  62.     return subfaceArea - openingArea;
  63.   }
  64.  
  65.   public Result Execute(
  66.     ExternalCommandData commandData,
  67.     ref string message,
  68.     ElementSet elements )
  69.   {
  70.     UIApplication app = commandData.Application;
  71.     Document doc = app.ActiveUIDocument.Document;
  72.  
  73.     SpatialElementBoundaryOptions sebOptions
  74.       = new SpatialElementBoundaryOptions();
  75.  
  76.     sebOptions.SpatialElementBoundaryLocation
  77.       = SpatialElementBoundaryLocation.Finish;
  78.  
  79.     Result rc;
  80.  
  81.     try
  82.     {
  83.       FilteredElementCollector roomCol
  84.         = new FilteredElementCollector( doc )
  85.           .OfClass( typeof( SpatialElement ) );
  86.  
  87.       string s = "Закончили обрабатывать  помещение"
  88.         + "Boundary Data\r\n\r\n";
  89.  
  90.       foreach( SpatialElement e in roomCol )
  91.       {
  92.         Room room = e as Room;
  93.  
  94.         if( room != null )
  95.         {
  96.           try
  97.           {
  98.             Autodesk.Revit.DB
  99.               .SpatialElementGeometryCalculator
  100.                 calc = new Autodesk.Revit.DB
  101.                   .SpatialElementGeometryCalculator(
  102.                     doc, sebOptions );
  103.  
  104.             SpatialElementGeometryResults results
  105.               = calc.CalculateSpatialElementGeometry(
  106.                 room );
  107.  
  108.             Solid roomSolid = results.GetGeometry();
  109.  
  110.             foreach( Face face in roomSolid.Faces )
  111.             {
  112.               IList<SpatialElementBoundarySubface>
  113.                 subfaceList = results.GetBoundaryFaceInfo(
  114.                   face );
  115.  
  116.               foreach( SpatialElementBoundarySubface
  117.                 subface in subfaceList )
  118.               {
  119.                 if( subface.SubfaceType
  120.                   == SubfaceType.Side )
  121.                 {
  122.                   Element wall = doc.GetElement(
  123.                     subface.SpatialBoundaryElement
  124.                       .HostElementId );
  125.  
  126.                   double subfaceArea = subface
  127.                     .GetSubface().Area;
  128.  
  129.                   double netArea = sqFootToSquareM(
  130.                     calwallAreaMinusOpenings(
  131.                       subfaceArea, wall, room ) );
  132.  
  133.                   s = s + "Room "
  134.                     + room.get_Parameter(
  135.                       BuiltInParameter.ROOM_NUMBER )
  136.                         .AsString()
  137.                     + " : Wall " + wall.get_Parameter(
  138.                       BuiltInParameter.ALL_MODEL_MARK )
  139.                         .AsString()
  140.                     + " : Area " + netArea.ToString()
  141.                     + " m2\r\n";
  142.                 }
  143.               }
  144.             }
  145.             s = s + "\r\n";
  146.           }
  147.           catch( Exception )
  148.           {
  149.           }
  150.         }
  151.       }
  152.       TaskDialog.Show( "Границы помещений", s );
  153.  
  154.       rc = Result.Succeeded;
  155.     }
  156.     catch( Exception ex )
  157.     {
  158.       TaskDialog.Show( " Границы помещений ",
  159.         ex.Message.ToString() + "\r\n"
  160.         + ex.StackTrace.ToString() );
  161.  
  162.       rc = Result.Failed;
  163.     }
  164.     return rc;
  165.   }
  166. }

Я обновил код на C#, но не на VB.NET в репозитории. Версия код, обсуждаемая в проекте – 2015.0.0.2

Спасибо Phillip и Vilo за значительное улучшение решения проблемы.

Источник: http://thebuildingcoder.typepad.com/blog/2015/03/findinserts-retrieves-all-openings-in-all-wall-types.html?cid=6a00e553e16897883301b7c7789a82970b

Автор перевода: Виктор Чекалин

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

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