Использование метода FindInserts для поиска всех проемов в стене
Недавно я представил новый пример использования класса SpatialElementGeometryCalculator для вычисления полезной и общей площади стен.
В статье обсуждалось несколько интересных моментов:
- Использование класса SpatialElementGeometryCalculator
- Портирование надстройки, написанной на VB.NET в C#
- Использование временной транзакции для решения задач
- Использование FilteredElementCollector для поиска всех проемов в стене
- Оптимизация поиска при использовании FilteredElementCollector
Пользователь Vilo оставил комментарий к этой статье с вопросом:
Вопрос: Почему бы просто не использовать метод (wall as HostObject).FindInserts() для определения того, какие объекты располагаются на заданной стене?
Возможно метод будет более оптимальным с точки зрения производительности.
Ответ: Очень правильный вопрос.
Если коротко, то я не знал об этом, даже не смотря на то что как-то использовал этот метод в примерах.
Спасибо за ценную подсказку.
Кстати, не нужно явно приводить тип wall к HostObject, так как класс Wall и так является наследником класса HostObject.
Добавлено позже: Кажется я понял что вы имели ввиду. В коде переменная wall имеет тип Element.
В итоге я добавил следующий код, чтобы удостовериться, что количество найденых проемов одинаково:
- // Этот подход гораздо эффективнее
- // чем использование FilteredElementCollector
- IList<ElementId> inserts = ( wall as HostObject )
- .FindInserts( true, true, true, true );
- Debug.Assert(
- lstTotempDel.Count.Equals( inserts.Count ),
- "Ожидалось, что FindInserts вернет то же самое количество" );
Проект SpatialElementGeometryCalculator обновлен на GitHub.
Я передал эту информацию Phillip Millier, который дал мне первоначальный код на VB.NET и вот что он ответил:
Ответ: Я уже оптимизировал использование FilteredElementCollector по вашим советам, но я также как и вы не знал о методе FindInserts.
Сегодня я созванивался со своими клиентами и они сказали, что плагин работает нормально со стенами, на которых находятся экземпляры семейств. А с составными cтенами, которые содержать фальш-стену, это не работает.
Как вы видите, в своем коде я сравнивал FamilyInstance.HostId с wall.Id. В случае с составными стенами, проемы на фальш-стене имеют другой идентификатор, отличный от FamilyInstance.HostId и поэтому проемы не находились.
Метод FindInserts решает эту проблему.
Ответ: Спасибо за подтверждение и предоставленные примеры, где использование этого метода позволяет получить другой результат.
Реализовать решение проблемы с составными стенами безусловно также можно и с помощью FilteredElementCollector, только более сложным и запутанным способом.
Использование FindInserts делает код значительно проще.
Вот что у меня получилось:
- [Transaction( TransactionMode.Manual )]
- public class Command : IExternalCommand
- {
- /// <summary>
- /// Конвертация квадратных футов в квадратные метры
- /// </summary>
- double sqFootToSquareM( double sqFoot )
- {
- return Math.Round( sqFoot * 0.092903, 2 );
- }
- /// <summary>
- /// Вычисление площади стен, за исключением проемов.
- /// Временно удаляем двери и окна в транзакции, которую затем откатываем.
- /// </summary>
- /// <param name="subfaceArea">Первоначальная площадь поверхности стены</param>
- /// <param name="wall"></param>
- /// <param name="doc"></param>
- /// <param name="room"></param>
- /// <returns></returns>
- double calwallAreaMinusOpenings(
- double subfaceArea,
- Element wall,
- Room room )
- {
- Document doc = wall.Document;
- // есть ли более надежный способ сравнить документы?
- Debug.Assert(
- room.Document.ProjectInformation.UniqueId.Equals(
- doc.ProjectInformation.UniqueId ),
- "Ожидалось, что помещение и стена будут в одном проекте" );
- // Определяем все проемы в стене.
- IList<ElementId> inserts = ( wall as HostObject )
- .FindInserts( true, true, true, true );
- // Определяем общую площадь проемов.
- double openingArea = 0;
- if( 0 < inserts.Count )
- {
- Transaction t = new Transaction( doc );
- double wallAreaNet = wall.get_Parameter(
- BuiltInParameter.HOST_AREA_COMPUTED )
- .AsDouble();
- t.Start( "tmp Delete" );
- doc.Delete( inserts );
- doc.Regenerate();
- double wallAreaGross = wall.get_Parameter(
- BuiltInParameter.HOST_AREA_COMPUTED )
- .AsDouble();
- t.RollBack();
- openingArea = wallAreaGross - wallAreaNet;
- }
- return subfaceArea - openingArea;
- }
- public Result Execute(
- ExternalCommandData commandData,
- ref string message,
- ElementSet elements )
- {
- UIApplication app = commandData.Application;
- Document doc = app.ActiveUIDocument.Document;
- SpatialElementBoundaryOptions sebOptions
- = new SpatialElementBoundaryOptions();
- sebOptions.SpatialElementBoundaryLocation
- = SpatialElementBoundaryLocation.Finish;
- Result rc;
- try
- {
- FilteredElementCollector roomCol
- = new FilteredElementCollector( doc )
- .OfClass( typeof( SpatialElement ) );
- string s = "Закончили обрабатывать помещение"
- + "Boundary Data\r\n\r\n";
- foreach( SpatialElement e in roomCol )
- {
- Room room = e as Room;
- if( room != null )
- {
- try
- {
- Autodesk.Revit.DB
- .SpatialElementGeometryCalculator
- calc = new Autodesk.Revit.DB
- .SpatialElementGeometryCalculator(
- doc, sebOptions );
- SpatialElementGeometryResults results
- = calc.CalculateSpatialElementGeometry(
- room );
- Solid roomSolid = results.GetGeometry();
- foreach( Face face in roomSolid.Faces )
- {
- IList<SpatialElementBoundarySubface>
- subfaceList = results.GetBoundaryFaceInfo(
- face );
- foreach( SpatialElementBoundarySubface
- subface in subfaceList )
- {
- if( subface.SubfaceType
- == SubfaceType.Side )
- {
- Element wall = doc.GetElement(
- subface.SpatialBoundaryElement
- .HostElementId );
- double subfaceArea = subface
- .GetSubface().Area;
- double netArea = sqFootToSquareM(
- calwallAreaMinusOpenings(
- subfaceArea, wall, room ) );
- s = s + "Room "
- + room.get_Parameter(
- BuiltInParameter.ROOM_NUMBER )
- .AsString()
- + " : Wall " + wall.get_Parameter(
- BuiltInParameter.ALL_MODEL_MARK )
- .AsString()
- + " : Area " + netArea.ToString()
- + " m2\r\n";
- }
- }
- }
- s = s + "\r\n";
- }
- catch( Exception )
- {
- }
- }
- }
- TaskDialog.Show( "Границы помещений", s );
- rc = Result.Succeeded;
- }
- catch( Exception ex )
- {
- TaskDialog.Show( " Границы помещений ",
- ex.Message.ToString() + "\r\n"
- + ex.StackTrace.ToString() );
- rc = Result.Failed;
- }
- return rc;
- }
- }
Я обновил код на C#, но не на VB.NET в репозитории. Версия код, обсуждаемая в проекте – 2015.0.0.2
Спасибо Phillip и Vilo за значительное улучшение решения проблемы.
Обсуждение: http://adn-cis.org/forum/index.php?topic=2663
Опубликовано 24.04.2015