原文链接: 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 );
}
}
}