26/04/2015
Улучшение алгоритма по вычислению общей и полезной площади стен
Продолжаем обсуждать решение проблемы по вычислению общей и полезной площади стен. После того, как мы немного переделали алгоритм и стали использовать метод FindInserts для поиска всех проемов, пользователь Håkon Clausen нашел пару недочетов в этом алгоритме и оставил их в комментарии (ориг.):
Я нашел две проблемы с текущей реализаций после того как поигрался с ним пару часов.
- При извлечении всех вставок в стене, метод FindInserts не проверяет, что проемы действительно принадлежат помещению, и, поэтому, в конечном итоге удаляются и проемы, которые находятся в других помещениях, если стена является границей нескольких помещений. Естественно, что это приведет к некорректному результату. Так что следует проверять, что проем действительно принадлежит помещению, как это делали в первоначальном варианте.
- Если стена имеет несколько поверхностей, то площадь проема вычитается несколько раз. Возьмем, к примеру, зал в примере проекта, устанавливающегося вместе с Revit (или любое другое помещение со стеной с несколькими дверями и окнами). Переменная calwallAreaMinusOpenings в этом случае примет отрицательное значение. Нужно вычислить общую площадь для каждой из стен, затем в цикле пройтись по каждой стене и найти и вычислить площадь проемов.
Я добавил это в репозиторий на GitHub.
Вот обновленная реализация:
Код - C#: [Выделить]
- public Result Execute(
- ExternalCommandData commandData,
- ref string message,
- ElementSet elements )
- {
- var app = commandData.Application;
- var doc = app.ActiveUIDocument.Document;
- Result rc;
- string s = string.Empty;
- try
- {
- var roomCol = new FilteredElementCollector( doc )
- .OfClass( typeof( SpatialElement ) );
- foreach( var e in roomCol )
- {
- var room = e as Room;
- if( room == null ) continue;
- if( room.Location == null ) continue;
- var sebOptions = new SpatialElementBoundaryOptions
- { SpatialElementBoundaryLocation
- = SpatialElementBoundaryLocation.Finish
- };
- var calc = new Autodesk.Revit.DB
- .SpatialElementGeometryCalculator(
- doc, sebOptions );
- var results = calc
- .CalculateSpatialElementGeometry( room );
- // отслеживаем каждую стену в помещении
- // и ее площадь в помещении
- var walls = new Dictionary<string, double>();
- foreach( Face face in results.GetGeometry().Faces )
- {
- foreach( var subface in results
- .GetBoundaryFaceInfo( face ) )
- {
- if( subface.SubfaceType
- != SubfaceType.Side ) { continue; }
- var wall = doc.GetElement( subface
- .SpatialBoundaryElement.HostElementId )
- as HostObject;
- if( wall == null ) { continue; }
- var grossArea = subface.GetSubface().Area;
- if( !walls.ContainsKey( wall.UniqueId ) )
- {
- walls.Add( wall.UniqueId, grossArea );
- }
- else
- {
- walls[wall.UniqueId] += grossArea;
- }
- }
- }
- foreach( var id in walls.Keys )
- {
- var wall = (HostObject) doc.GetElement( id );
- var openings = CalculateWallOpeningArea(
- wall, room );
- s += string.Format(
- "Room: {2} Wall: {0} Area: {1} m2\r\n",
- wall.get_Parameter( BuiltInParameter.ALL_MODEL_MARK ).AsString(),
- SqFootToSquareM( walls[id] - openings ),
- room.get_Parameter( BuiltInParameter.ROOM_NUMBER ).AsString() );
- }
- }
- TaskDialog.Show( "Room Boundaries", s );
- rc = Result.Succeeded;
- }
- catch( Exception ex )
- {
- TaskDialog.Show( "Room Boundaries",
- ex.Message + "\r\n" + ex.StackTrace );
- rc = Result.Failed;
- }
- return rc;
- }
- /// <summary>
- /// Конвертация квадратных футов в квадратные метры
- /// </summary>
- private static double SqFootToSquareM(
- double sqFoot )
- {
- return Math.Round( sqFoot * 0.092903, 2 );
- }
- /// <summary>
- /// Вычисление площади стен, за исключением проемов.
- /// Временно удаляем двери и окна в транзакции, которую затем откатываем.
- /// </summary>private static double CalculateWallOpeningArea(
- HostObject wall,
- Room room )
- {
- var doc = wall.Document;
- var wallAreaNet = wall.get_Parameter(
- BuiltInParameter.HOST_AREA_COMPUTED ).AsDouble();
- var t = new Transaction( doc );
- t.Start( "Temp" );
- foreach( var id in wall.FindInserts(
- true, true, true, true ) )
- {
- var insert = doc.GetElement( id );
- if( insert is FamilyInstance
- && IsInRoom( room, (FamilyInstance) insert ) )
- {
- doc.Delete( id );
- }
- }
- doc.Regenerate();
- var wallAreaGross = wall.get_Parameter(
- BuiltInParameter.HOST_AREA_COMPUTED ).AsDouble();
- t.RollBack();
- return wallAreaGross - wallAreaNet;
- }
- /// <summary>
- /// Определение, что заданный экземпляр семейства находится в помещении
- /// </summary>
- static bool IsInRoom(
- Room room,
- FamilyInstance f )
- {
- ElementId rid = room.Id;
- return ( ( f.Room != null && f.Room.Id == rid )
- || ( f.ToRoom != null && f.ToRoom.Id == rid )
- || ( f.FromRoom != null && f.FromRoom.Id == rid ) );
- }
Большое спасибо Håkon за замечания и обновление.
Обновленная версия лежит на GitHub. Версия, обсуждаемая в статье – 2015.0.0.3
Автор перевода: Виктор Чекалин
Обсуждение: http://adn-cis.org/forum/index.php?topic=2669
Опубликовано 26.04.2015