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

28/08/2013

Как получить абсолютно все видимые и скрытые элементы

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

На одну из затронутых проблем я так и не дал подробный ответ:

Вопрос 3: Я бы хотел экспортировать только видимые объекты. Метод Element.IsHidden возвращает верное значение, только если элемент был скрыт с помощью меню Скрыть на виде -> Элемент (Категорию), но игнорирует скрытие элемента с помощью фильтра.

Как можно точно определить, что пользователь действительно видит на виде? Наши клиенты очень ждут такую возможность для экспорта.

Кавалерия, возглавляемая Скоттом Коновером (Scott Conover) из команды разработчиков Revit и Жоэлем Спаном (Joel Spahn), разработчиком из компаний  Lighting Analysts, Inc. и ElumTools, спешит на помощь.

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

Я уже показывал как его использовать для экспорта в XML, Collada и JSON.

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

Ниже представлено описание и дальнейшее развитие проекта:

Цель:  Я хочу узнать является ли элемент видимым на виде принимая во внимание следующее:

  1. Скрыт ли элемент
  2. Помечена ли категория элемента как скрытая или нет
  3. Фильтры видимости (переопределение видимости/графики)
  4. Другие моменты:
  • Область подрезки
  • Область видимости
  • Стадии проекта
  • Варианты конструкции
  • Различия в отображении плана этажей и плана потолков
  • И т.д.

Я создал методы, позволяющие определить скрыта ли категория. Должен ли я протестировать все фильтры связанные с заданным видом? Или есть более короткий путь?

FilteredElementColector c параметром ViewId, должен все это учитывать, что сэкономило бы кучу времени.

А сейчас о неизбежном ‘если бы не одно но’:

Работая с элементом из связанного файла,  как я могу определить является ли элемент видимым на виде в основном проекте?

Настройки вида не обязательно должны влиять на элементы из связанных файлов, поэтому в этом случае FilteredElementCollector может не работать.

Возможно ли создать такой FilteredElementCollector, где можно передать документ из связанного файла в качестве параметра, но при этом ViewId из основного документа?

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

CustomExporter обрабатывает 3D-вид и имеет функции обратного вызова перед обработкой элемента и после обработки элемента, в том числе и элемента из связанного файла.

Таким образом, возможно построить дерево элементов, которые являются видимыми на этом виде используя CustomExporter и всегда возвращать RenderNodeAction.Skip для элементов.

У меня получилось запустить CustomExporter и создать словарь, содержащий список идентификаторов видимых элементов для каждого документа.

При работе с большой моделью я столкнулся с проблемой производительности. После того как я стал возвращать в качестве результата RenderNodeAction.Skip в методе  OnElementBegin, проблема исчезла.

Также важно помнить, что нужно иметь ввиду, какому документу принадлежит тот ли иной элемент. После экспорта элементов находящихся на выбранном вами виде, вы можете определить, является ли элемент видимым или нет на этом виде, с помощью свойства ElementVisible.

Еще одно важное замечание касается выбора того, что использовать в качестве ключа для определения документа – объект Document или путь к файлу этого документа. Я выбрал путь к файлу документа, так как мне нужно хранить эту информацию некоторое время. Хотя использовать в качестве ключа объект Document было бы более правильно. Также хочу указать на то, что при хранении списка элементов можно использовать целочисленный идентификатор вместо ElementId.

 Все же я думаю, что использовать путь к файлу сейчас лучше, чем ссылку на сам документ. Класс Document переопределяет методы Equals и GetHashCode, но кажется они работают не верно в некоторых случаях.

Ниже предоставлена реализация решения проблемы с использованием CustomExporter на VB.NET

Код - VB.NET: [Выделить]
  1.  
  2. Public Class ElementsVisibleInViewExportContext
  3.     Implements IExportContext
  4.  
  5.     Private Documents As New Stack(Of Document)
  6.     Private Property Elements As New Dictionary( _
  7.       Of String, HashSet(Of ElementId))
  8.  
  9.     Public ReadOnly Property ElementVisible( _
  10.       ByVal doc As Document, ByVal id As ElementId) _
  11.       As Boolean
  12.  
  13.         Get
  14.             Dim ids As HashSet(Of ElementId)
  15.  
  16.             If Elements.TryGetValue(doc.PathName, ids) Then
  17.                 If ids.Contains(id) Then
  18.  
  19.                     Return True
  20.  
  21.                 End If
  22.             End If
  23.  
  24.             Return False
  25.         End Get
  26.  
  27.     End Property
  28.  
  29.     Public Sub New(ByVal mainDocument As Document)
  30.  
  31.         Documents.Push(mainDocument)
  32.         Elements.Add(mainDocument.PathName,
  33.                      New HashSet(Of ElementId))
  34.  
  35.     End Sub
  36.  
  37.     Public Function Start() As Boolean _
  38.       Implements IExportContext.Start
  39.  
  40.         Return True
  41.  
  42.     End Function
  43.  
  44.     Public Sub Finish() _
  45.       Implements IExportContext.Finish
  46.  
  47.         'Nothing.
  48.  
  49.     End Sub
  50.  
  51.     Public Function OnViewBegin( _
  52.       ByVal node As ViewNode) _
  53.       As RenderNodeAction _
  54.       Implements IExportContext.OnViewBegin
  55.  
  56.         Return RenderNodeAction.Proceed
  57.  
  58.     End Function
  59.  
  60.     Public Sub OnViewEnd( _
  61.       ByVal elementId As ElementId) _
  62.       Implements IExportContext.OnViewEnd
  63.  
  64.         'Nothing.
  65.  
  66.     End Sub
  67.  
  68.     Public Function OnLinkBegin( _
  69.       ByVal node As LinkNode) _
  70.       As RenderNodeAction _
  71.       Implements IExportContext.OnLinkBegin
  72.  
  73.         Dim doc = node.GetDocument
  74.  
  75.         Documents.Push(doc)
  76.         If Not Elements.ContainsKey(doc.PathName) Then
  77.             Elements.Add(doc.PathName, _
  78.                          New HashSet(Of ElementId))
  79.         End If
  80.  
  81.         Return RenderNodeAction.Proceed
  82.  
  83.     End Function
  84.  
  85.     Public Sub OnLinkEnd(ByVal node As LinkNode) _
  86.       Implements IExportContext.OnLinkEnd
  87.  
  88.         Dim doc = Documents.Pop()
  89.  
  90.     End Sub
  91.  
  92.     Public Function OnElementBegin( _
  93.       ByVal elementId As ElementId) _
  94.       As RenderNodeAction _
  95.       Implements IExportContext.OnElementBegin
  96.  
  97.         Elements(Documents.Peek.PathName).Add(elementId)
  98.  
  99.         Return RenderNodeAction.Skip
  100.  
  101.     End Function
  102.  
  103.     Public Sub OnElementEnd(ByVal elementId As ElementId) _
  104.       Implements IExportContext.OnElementEnd
  105.  
  106.         'Nothing.
  107.  
  108.     End Sub
  109.  
  110.     Public Function OnInstanceBegin( _
  111.       ByVal node As InstanceNode) As RenderNodeAction _
  112.     Implements IExportContext.OnInstanceBegin
  113.  
  114.         Return RenderNodeAction.Skip
  115.  
  116.     End Function
  117.  
  118.     Public Sub OnInstanceEnd(ByVal node As InstanceNode) _
  119.       Implements IExportContext.OnInstanceEnd
  120.  
  121.         'Nothing.
  122.  
  123.     End Sub
  124.  
  125.     Public Function OnFaceBegin(ByVal node As FaceNode) _
  126.       As RenderNodeAction _
  127.       Implements IExportContext.OnFaceBegin
  128.  
  129.         Return RenderNodeAction.Skip
  130.  
  131.     End Function
  132.  
  133.     Public Sub OnFaceEnd(ByVal node As FaceNode) _
  134.       Implements IExportContext.OnFaceEnd
  135.  
  136.         'Nothing.
  137.  
  138.     End Sub
  139.  
  140.     Public Sub OnMaterial(ByVal node As MaterialNode) _
  141.       Implements IExportContext.OnMaterial
  142.  
  143.         'Nothing.
  144.  
  145.     End Sub
  146.  
  147.     Public Sub OnPolymesh(ByVal node As PolymeshTopology) _
  148.       Implements IExportContext.OnPolymesh
  149.  
  150.         'Nothing.
  151.  
  152.     End Sub
  153.  
  154.     Public Sub OnRPC(ByVal node As RPCNode) _
  155.       Implements IExportContext.OnRPC
  156.  
  157.         'Nothing.
  158.  
  159.     End Sub
  160.  
  161.     Public Sub OnDaylightPortal( _
  162.       ByVal node As DaylightPortalNode) _
  163.       Implements IExportContext.OnDaylightPortal
  164.  
  165.         'Nothing.
  166.  
  167.     End Sub
  168.  
  169.     Public Sub OnLight(ByVal node As LightNode) _
  170.       Implements IExportContext.OnLight
  171.  
  172.         'Nothing.
  173.  
  174.     End Sub
  175.  
  176.     Public Function IsCanceled() As Boolean _
  177.       Implements IExportContext.IsCanceled
  178.  
  179.         Return False
  180.  
  181.     End Function
  182.  
  183. End Class
  184.  

И в заключении, полный исходный код. CustomExporterElementVisibleInView.zip

Этот метод работает замечательно и возможно это единственно верный способ определить видимость элементов из связанного файла исходя из настроек вида в основном файле.

Огромное спасибо Скотту и Жоэлю за идею и за то что поделились своим решением.

Источник: http://thebuildingcoder.typepad.com/blog/2013/08/determining-absolutely-all-visible-elements.html

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

Опубликовано 28.08.2013
Отредактировано 28.08.2013 в 07:51:15