Building Coder(Revit 二次开发) - 提高针对族的元素过滤器的性能

原文链接: Family Usage Filtered Element Collector Performance

问题

针对 Revit 数据的访问绝大多数情况下都是基于从 FilteredElementCollector 中返回的 Revit 元素,可见提高元素过滤的性能是保证 Revit 二次开发性能的基石。

Jeremy

我在博文 XML family usage report(http://thebuildingcoder.typepad.com/blog/2010/12/xml-family-usage-report.html) 中介绍了如果使用元素过滤器获取一个文档中所有被加载的族,并为每一个被加载的族创建一个元素过滤器来获取其族实例。不过你的问题中只关心指定类别的族,特别是那些被创建了族实例的族。所以我上面这篇博文里的代码就显得效率不高了。

问题

我想获取当前文档中所有被创建了族实例的族。但是我的代码在加载了几百个族的文档中效率太低。

Sub GetFamilyType(ByVal mydoc As Document)


  Dim collector As New Autodesk.Revit.DB.FilteredElementCollector(mydoc)
  Dim collection As ICollection(Of Autodesk.Revit.DB.Element) = collector.OfClass(GetType(DB.Family)).ToElements()


  ' 获取所有属于指定类别的族
  Dim families As List(Of Family)
  Dim category As DB.Category = Nothing


  Dim familyTypeCategoryList As New Dictionary(Of String, List(Of Family)) '族和族实例
  Dim categoryList As New Dictionary(Of String, Category) '类别及其族实例


  For Each family As DB.Family In collection
    category = Nothing
    If family.Symbols.Size > 0 Then
      Dim symbols As FamilySymbolSetIterator = family.Symbols.ForwardIterator()
      symbols.Reset()
      Dim symbol As Autodesk.Revit.DB.FamilySymbol = Nothing
      While symbols.MoveNext()
        symbol = TryCast(symbols.Current, Autodesk.Revit.DB.FamilySymbol)
        '-------------------------
        '下面这段效率太低
        '-------------------------
        'Find the symbol of which at least a Family instance exists    
        Dim filter As New DB.FamilyInstanceFilter(mydoc, symbol.Id)
        collector = New Autodesk.Revit.DB.FilteredElementCollector(mydoc)
        Dim familyInstances As ICollection(Of DB.Element) = collector.WherePasses(filter).ToElements()
        If familyInstances.Count > 0 Then
          '所有族的类型都属于同一个类别,不同的族也可能包含属于同一类别的族类型
          category = symbol.Category
          Exit While
        End If
      End While
      If category IsNot Nothing Then
        If familyTypeCategoryList.ContainsKey(category.Name) Then
          familyTypeCategoryList.Item(category.Name).Add(family)
        Else
          families = New List(Of DB.Family)
          families.Add(family)
          familyTypeCategoryList.Add(category.Name, families)
          categoryList.Add(category.Name, category)
        End If
      End If
    End If
  Next
End Sub

Jeremy

首先你可以将 Collector 转换成 Collection 的代码删除。Collector 本身就是可以被遍历的,Collector 到 Collection 的转换需要重新申请内存并转换数据类型,这通常需要不少时间。

更重要的是,你不应该首先获取所有的族,然后使用循环的方式为每个族再次创建一个过滤器来找到可能的族实例。如果一个族只是被加载但是没有族实例的话,这部分工作就是多余的了。


所以我建议采用如下步骤:
1. 确认所有你感兴趣的类别
2. 为所有这些类别创建一个族实例过滤器
3. 遍历这些族实例,确认它们属于哪个族或者族类型(FamilySymbol)


我这篇博文里列举了很多关于元素过滤的例子: filtered element collector samples

答复

我的场景里无法预先知道所有感兴趣的类别。不过按照你的建议修改的下面这段代码效率提高了很多(从3到5分钟缩短到了10秒钟)。
Public Shared Sub CreateFamilyTreeTest( ByVal myDoc As Document)

  Dim collector As New FilteredElementCollector(myDoc)
  collector.WhereElementIsNotElementType()

  Dim familyInstances = From elem In collector _
    Where elem.Category IsNot Nothing _
    And TypeOf elem Is FamilyInstance()

  Dim familySymbols = From elem In collector _
    Join sb In familyInstances _
    On elem.UniqueId Equals CType(sb.ObjectType,
      FamilySymbol).Family.UniqueId _
    Where TypeOf elem Is Family Select elem

  familySymbols = familySymbols.Distinct()

  Dim mapCatToFam As New Dictionary(Of String, List(Of Family))

  Dim categoryList As New Dictionary(Of String, Category)

  Dim families As List(Of Family)
  Dim category As Category
  Dim symbol As FamilySymbol

  For Each family As Family In familySymbols
    If family.Symbols.Size > 0 Then
      symbol = family.Symbols(0)
      category = symbol.Category
      If mapCatToFam.ContainsKey(category.Name) Then
        mapCatToFam.Item(category.Name).Add(family)
      Else
        families = New List(Of Family)
        families.Add(family)
        mapCatToFam.Add(category.Name, families)
        categoryList.Add(category.Name, category)
      End If
    End If
  Next
End Sub

Jeremy

下面这段利用泛型的C#代码更加简洁一些:

public static void CreateFamilyTreeTest( Document myDoc )
{
  IEnumerable<Element> familiesCollector = new FilteredElementCollector( myDoc )
      .OfClass( typeof( FamilyInstance ) )
      .WhereElementIsNotElementType()
      .Cast<FamilyInstance>()
      .GroupBy( fi => fi.Symbol.Family )
      .Select( f => f.Key );
 
  var mapCatToFam = new Dictionary<string, List<Element>>();
 
  var categoryList = new Dictionary<string, Category>();
 
  foreach( var f in familiesCollector )
  {
    var catName = f.Category.Name;
 
    if( mapCatToFam.ContainsKey( catName ) )
    {
      mapCatToFam[catName].Add( f );
    }
    else
    {
      mapCatToFam.Add( catName, new List<Element> { f } );
      categoryList.Add( catName, f.Category );
    }
  }
}


你可能感兴趣的:(list,String,filter,文档,performance,Dictionary)