Как получить абсолютно все видимые и скрытые элементы
Вчера я обсуждал API для работы с фильтрами переопределения видимости/графики.
На одну из затронутых проблем я так и не дал подробный ответ:
Вопрос 3: Я бы хотел экспортировать только видимые объекты. Метод Element.IsHidden возвращает верное значение, только если элемент был скрыт с помощью меню Скрыть на виде -> Элемент (Категорию), но игнорирует скрытие элемента с помощью фильтра.
Как можно точно определить, что пользователь действительно видит на виде? Наши клиенты очень ждут такую возможность для экспорта.
Кавалерия, возглавляемая Скоттом Коновером (Scott Conover) из команды разработчиков Revit и Жоэлем Спаном (Joel Spahn), разработчиком из компаний Lighting Analysts, Inc. и ElumTools, спешит на помощь.
Скотт и Жоэль расскажут нам, как новый фреймворк для создания пользовательского экспорта, можно использовать для решения этой проблемы.
Я уже показывал как его использовать для экспорта в XML, Collada и JSON.
Скотт и Жоэль обращают внимание, что данный фрэймворк можно также использовать и для более банального случая, когда нужно лишь просто определить видимость отдельно взятых элементов, включая все различные способы управления видимостью элементов, и также включая элементы из связанных файлов.
Ниже представлено описание и дальнейшее развитие проекта:
Цель: Я хочу узнать является ли элемент видимым на виде принимая во внимание следующее:
- Скрыт ли элемент
- Помечена ли категория элемента как скрытая или нет
- Фильтры видимости (переопределение видимости/графики)
- Другие моменты:
- Область подрезки
- Область видимости
- Стадии проекта
- Варианты конструкции
- Различия в отображении плана этажей и плана потолков
- И т.д.
Я создал методы, позволяющие определить скрыта ли категория. Должен ли я протестировать все фильтры связанные с заданным видом? Или есть более короткий путь?
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
- Public Class ElementsVisibleInViewExportContext
- Implements IExportContext
- Private Documents As New Stack(Of Document)
- Private Property Elements As New Dictionary( _
- Of String, HashSet(Of ElementId))
- Public ReadOnly Property ElementVisible( _
- ByVal doc As Document, ByVal id As ElementId) _
- As Boolean
- Get
- Dim ids As HashSet(Of ElementId)
- If Elements.TryGetValue(doc.PathName, ids) Then
- If ids.Contains(id) Then
- Return True
- End If
- End If
- Return False
- End Get
- End Property
- Public Sub New(ByVal mainDocument As Document)
- Documents.Push(mainDocument)
- Elements.Add(mainDocument.PathName,
- New HashSet(Of ElementId))
- End Sub
- Public Function Start() As Boolean _
- Implements IExportContext.Start
- Return True
- End Function
- Public Sub Finish() _
- Implements IExportContext.Finish
- 'Nothing.
- End Sub
- Public Function OnViewBegin( _
- ByVal node As ViewNode) _
- As RenderNodeAction _
- Implements IExportContext.OnViewBegin
- Return RenderNodeAction.Proceed
- End Function
- Public Sub OnViewEnd( _
- ByVal elementId As ElementId) _
- Implements IExportContext.OnViewEnd
- 'Nothing.
- End Sub
- Public Function OnLinkBegin( _
- ByVal node As LinkNode) _
- As RenderNodeAction _
- Implements IExportContext.OnLinkBegin
- Dim doc = node.GetDocument
- Documents.Push(doc)
- If Not Elements.ContainsKey(doc.PathName) Then
- Elements.Add(doc.PathName, _
- New HashSet(Of ElementId))
- End If
- Return RenderNodeAction.Proceed
- End Function
- Public Sub OnLinkEnd(ByVal node As LinkNode) _
- Implements IExportContext.OnLinkEnd
- Dim doc = Documents.Pop()
- End Sub
- Public Function OnElementBegin( _
- ByVal elementId As ElementId) _
- As RenderNodeAction _
- Implements IExportContext.OnElementBegin
- Elements(Documents.Peek.PathName).Add(elementId)
- Return RenderNodeAction.Skip
- End Function
- Public Sub OnElementEnd(ByVal elementId As ElementId) _
- Implements IExportContext.OnElementEnd
- 'Nothing.
- End Sub
- Public Function OnInstanceBegin( _
- ByVal node As InstanceNode) As RenderNodeAction _
- Implements IExportContext.OnInstanceBegin
- Return RenderNodeAction.Skip
- End Function
- Public Sub OnInstanceEnd(ByVal node As InstanceNode) _
- Implements IExportContext.OnInstanceEnd
- 'Nothing.
- End Sub
- Public Function OnFaceBegin(ByVal node As FaceNode) _
- As RenderNodeAction _
- Implements IExportContext.OnFaceBegin
- Return RenderNodeAction.Skip
- End Function
- Public Sub OnFaceEnd(ByVal node As FaceNode) _
- Implements IExportContext.OnFaceEnd
- 'Nothing.
- End Sub
- Public Sub OnMaterial(ByVal node As MaterialNode) _
- Implements IExportContext.OnMaterial
- 'Nothing.
- End Sub
- Public Sub OnPolymesh(ByVal node As PolymeshTopology) _
- Implements IExportContext.OnPolymesh
- 'Nothing.
- End Sub
- Public Sub OnRPC(ByVal node As RPCNode) _
- Implements IExportContext.OnRPC
- 'Nothing.
- End Sub
- Public Sub OnDaylightPortal( _
- ByVal node As DaylightPortalNode) _
- Implements IExportContext.OnDaylightPortal
- 'Nothing.
- End Sub
- Public Sub OnLight(ByVal node As LightNode) _
- Implements IExportContext.OnLight
- 'Nothing.
- End Sub
- Public Function IsCanceled() As Boolean _
- Implements IExportContext.IsCanceled
- Return False
- End Function
- End Class
И в заключении, полный исходный код. 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