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

26/04/2015

Улучшение алгоритма по вычислению общей и полезной площади стен

Продолжаем обсуждать решение проблемы по вычислению общей и полезной площади стен. После того, как мы немного переделали алгоритм и стали использовать метод FindInserts для поиска всех проемов, пользователь Håkon Clausen нашел пару недочетов в этом алгоритме и оставил их в комментарии (ориг.):

Я нашел две проблемы с текущей реализаций после того как поигрался с ним пару часов.

  1. При извлечении всех вставок в стене, метод FindInserts не проверяет, что проемы действительно принадлежат помещению, и, поэтому, в конечном итоге удаляются и проемы, которые находятся в других помещениях, если стена является границей нескольких помещений. Естественно, что это приведет к некорректному результату. Так что следует проверять, что проем действительно принадлежит помещению, как это делали в первоначальном варианте.
  2. Если стена имеет несколько поверхностей, то площадь проема вычитается несколько раз. Возьмем, к примеру, зал в примере проекта, устанавливающегося вместе с Revit (или любое другое помещение со стеной с несколькими дверями и окнами). Переменная calwallAreaMinusOpenings в этом случае примет отрицательное значение. Нужно вычислить общую площадь для каждой из стен, затем в цикле пройтись по каждой стене и найти и вычислить площадь проемов.

Я добавил это в репозиторий на GitHub.

Вот обновленная реализация:

Код - C#: [Выделить]
  1. public Result Execute(
  2.   ExternalCommandData commandData,
  3.   ref string message,
  4.   ElementSet elements )
  5. {
  6.   var app = commandData.Application;
  7.   var doc = app.ActiveUIDocument.Document;
  8.   Result rc;
  9.   string s = string.Empty;
  10.   try
  11.   {
  12.     var roomCol = new FilteredElementCollector( doc )
  13.       .OfClass( typeof( SpatialElement ) );
  14.  
  15.     foreach( var e in roomCol )
  16.     {
  17.       var room = e as Room;
  18.       if( room == null ) continue;
  19.       if( room.Location == null ) continue;
  20.  
  21.       var sebOptions = new SpatialElementBoundaryOptions
  22.       { SpatialElementBoundaryLocation
  23.         = SpatialElementBoundaryLocation.Finish
  24.       };
  25.       var calc = new Autodesk.Revit.DB
  26.         .SpatialElementGeometryCalculator(
  27.           doc, sebOptions );
  28.  
  29.       var results = calc
  30.         .CalculateSpatialElementGeometry( room );
  31.  
  32.       // отслеживаем каждую стену в помещении
  33.       // и ее площадь в помещении
  34.  
  35.       var walls = new Dictionary<string, double>();
  36.  
  37.       foreach( Face face in results.GetGeometry().Faces )
  38.       {
  39.         foreach( var subface in results
  40.           .GetBoundaryFaceInfo( face ) )
  41.         {
  42.           if( subface.SubfaceType
  43.             != SubfaceType.Side ) { continue; }
  44.  
  45.           var wall = doc.GetElement( subface
  46.             .SpatialBoundaryElement.HostElementId )
  47.               as HostObject;
  48.  
  49.           if( wall == null ) { continue; }
  50.           var grossArea = subface.GetSubface().Area;
  51.           if( !walls.ContainsKey( wall.UniqueId ) )
  52.           {
  53.             walls.Add( wall.UniqueId, grossArea );
  54.           }
  55.           else
  56.           {
  57.             walls[wall.UniqueId] += grossArea;
  58.           }
  59.         }
  60.       }
  61.  
  62.       foreach( var id in walls.Keys )
  63.       {
  64.         var wall = (HostObject) doc.GetElement( id );
  65.         var openings = CalculateWallOpeningArea(
  66.           wall, room );
  67.  
  68.         s += string.Format(
  69.           "Room: {2} Wall: {0} Area: {1} m2\r\n",
  70.           wall.get_Parameter( BuiltInParameter.ALL_MODEL_MARK ).AsString(),
  71.           SqFootToSquareM( walls[id] - openings ),
  72.           room.get_Parameter( BuiltInParameter.ROOM_NUMBER ).AsString() );
  73.       }
  74.     }
  75.     TaskDialog.Show( "Room Boundaries", s );
  76.     rc = Result.Succeeded;
  77.   }
  78.   catch( Exception ex )
  79.   {
  80.     TaskDialog.Show( "Room Boundaries",
  81.       ex.Message + "\r\n" + ex.StackTrace );
  82.     rc = Result.Failed;
  83.   }
  84.  
  85.   return rc;
  86. }
  87.  
  88.   /// <summary>
  89.   /// Конвертация квадратных футов в квадратные метры
  90.   /// </summary>
  91. private static double SqFootToSquareM(
  92.   double sqFoot )
  93. {
  94.   return Math.Round( sqFoot * 0.092903, 2 );
  95. }
  96.  
  97.   /// <summary>
  98.   /// Вычисление площади стен, за исключением проемов.
  99.  /// Временно удаляем двери и окна в транзакции, которую затем откатываем.
  100.   /// </summary>private static double CalculateWallOpeningArea(
  101.   HostObject wall,
  102.   Room room )
  103. {
  104.   var doc = wall.Document;
  105.   var wallAreaNet = wall.get_Parameter(
  106.     BuiltInParameter.HOST_AREA_COMPUTED ).AsDouble();
  107.  
  108.   var t = new Transaction( doc );
  109.   t.Start( "Temp" );
  110.   foreach( var id in wall.FindInserts(
  111.     true, true, true, true ) )
  112.   {
  113.     var insert = doc.GetElement( id );
  114.     if( insert is FamilyInstance
  115.       && IsInRoom( room, (FamilyInstance) insert ) )
  116.     {
  117.       doc.Delete( id );
  118.     }
  119.   }
  120.  
  121.   doc.Regenerate();
  122.   var wallAreaGross = wall.get_Parameter(
  123.     BuiltInParameter.HOST_AREA_COMPUTED ).AsDouble();
  124.   t.RollBack();
  125.  
  126.   return wallAreaGross - wallAreaNet;
  127. }
  128.  
  129. /// <summary>
  130. /// Определение, что заданный экземпляр семейства находится в помещении
  131. /// </summary>
  132. static bool IsInRoom(
  133.   Room room,
  134.   FamilyInstance f )
  135. {
  136.   ElementId rid = room.Id;
  137.   return ( ( f.Room != null && f.Room.Id == rid )
  138.     || ( f.ToRoom != null && f.ToRoom.Id == rid )
  139.     || ( f.FromRoom != null && f.FromRoom.Id == rid ) );
  140. }

Большое спасибо Håkon за  замечания и обновление.

Обновленная версия лежит на GitHub. Версия, обсуждаемая в статье – 2015.0.0.3

Источник: http://thebuildingcoder.typepad.com/blog/2015/04/gross-and-net-wall-area-calculation-enhancement-and-events.html

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

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

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