In Revit API 2011, a new FilteredElementCollector mechanism is introduced, all previous filters are replaced, and some brand new ones are added. In addition, the filters are splitted into logical filters (ElementLogicalFilter), quick filters (ElementQuickFilter) and slow filters (ElementSlowFilter). There are two logical filters as before (AND and OR), a dozen of quick filters and a bunch of slow filters.
The logical filters are not new and they seem to be renamed from those old versions. In fact, in the new FilteredElementCollector mechanism, they become redundant as the collector has two shortcut methods, UnionWith() and IntersectWith(), and they can do the same work, more nicely and meaningfully, that the two logical filters are supposed to do. Because of this, we will skip them in this article; instead, we will talk about the two methods a bit and use them in some example code. The quick and slow filter split is for performance consideration as stated that the fommer is quicker and the latter is slower.
Quick filters include: • BoundingBoxContainsPointFilter
• BoundingBoxIntersectsFilter
• BoundingBoxIsInsideFilter
• ElementCategoryFilter
• ElementClassFilter
• ElementDesignOptionFilter
• ElementIsCurveDrivenFilter
• ElementIsElementTypeFilter
• ElementOwnerViewFilter
• ElementStructuralTypeFilter
• ExclusionFilter
• FamilySymbolFilter They are not exclusive to each other. In some cases, for example, we can use the ElementCategoryFilter or the ElementClassFilter to achieve the same goal. We will demonstrate such a case a bit later with code. In terms of quick filters and slow filters, this is also true (maybe more true). For example, the slow RoomFilter seems the only way to filter out rooms, but actually we can use the OST_Rooms category filter (plus another filter like excluding element types, maybe) to get to the same point. We are not sure though which means is really quicker, but it is stated again and again that the quick filters should be considered at the first place. Anyway, the element filtering performance is not our concern in this article. Let’s focus on some aspects of the quick filters now. Assuming we need to fufill a task like filtering out all roofs, ceilings, floors and stairs from a Revit model, which filters should we use and what filter arguments are good and necessary? It is not arguable that the ElementCategoryFilter or the ElementClassFilter or both should be applied, but the question is how. Here are some more clues that can help us make such a decision: there are Floor, CeilingAndFloor, and RoofBase classes in the API, but there is no Ceiling or Stair class; there are OST_Ceilings, OST_Roofs, OST_Floors, and OST_Stairs values and a lot of more specific ones in the BuiltInCategory enumerator. If we prefer the ElementClassFilter, we can filter out the roofs, ceilings and floors using the filter with the CeilingAndFloor and the RoofBase classes as arguments respectively and unit them together, then we filter out stairs using the ElementCategoryFilter with the OST_Stairs BuiltInCategory argument and again unit them together. One thing needs to bear in mind is do not forget to exclude all element types (symbols) from the final FilteredElementCollector.
Here is the example code to demonstrate this approach:
public static List<Element> FindAllRoofsCeilingsFloorsAndStairs_Approach1(RvtDocument doc)
{
List<Element> list = new List<Element>(); FilteredElementCollector finalCollector = new FilteredElementCollector(doc);
ElementClassFilter filter1 = new ElementClassFilter(typeof(CeilingAndFloor), false);
finalCollector.WherePasses(filter1);
ElementClassFilter filter2 = new ElementClassFilter(typeof(RoofBase), false);
finalCollector.UnionWith((new FilteredElementCollector(doc)).WherePasses(filter2));
ElementCategoryFilter filter3 = new ElementCategoryFilter(BuiltInCategory.OST_Stairs, false);
finalCollector.UnionWith((new FilteredElementCollector(doc)).WherePasses(filter3));
ElementIsElementTypeFilter filter4 = new ElementIsElementTypeFilter(true);
finalCollector.IntersectWith((new FilteredElementCollector(doc)).WherePasses(filter4)); list = finalCollector.ToList<Element>(); return list;
} If we prefer the ElementCategoryFilter or it has similar performance as the ElementClassFilter, we can write some other code to do the same thing:
public static List<Element> FindAllRoofsCeilingsFloorsAndStairs_Approach2(RvtDocument doc)
{
List<Element> list = new List<Element>();
FilteredElementCollector finalCollector = new FilteredElementCollector(doc); ElementCategoryFilter filter1 = new ElementCategoryFilter(BuiltInCategory.OST_Ceilings, false);
finalCollector.WherePasses(filter1);
ElementCategoryFilter filter2 = new ElementCategoryFilter(BuiltInCategory.OST_Roofs, false);
finalCollector.UnionWith((new FilteredElementCollector(doc)).WherePasses(filter2));
ElementCategoryFilter filter3 = new ElementCategoryFilter(BuiltInCategory.OST_Floors, false);
finalCollector.UnionWith((new FilteredElementCollector(doc)).WherePasses(filter3));
ElementCategoryFilter filter4 = new ElementCategoryFilter(BuiltInCategory.OST_Stairs, false);
finalCollector.UnionWith((new FilteredElementCollector(doc)).WherePasses(filter4));
ElementIsElementTypeFilter filter5 = new ElementIsElementTypeFilter(true);
finalCollector.IntersectWith((new FilteredElementCollector(doc)).WherePasses(filter5)); list = finalCollector.ToList<Element>(); return list;
} Personally, I like this approach as the code is more readable and its logic is clearer though one more filter is needed in this particular case. And please do not forget, more importantly, the Revit API does not provide classes for all categories!
This leads to another question: does it indicate that the ElementClassFilter is also redundant if it does not have significant performance gain than the ElementCategoryFilter? It is not something we will or can answer in this post.
Supposing we have another task, retrieving all elements from a document, what should we do?
It needs two filters, one ElementIsElementTypeFilter and one inverted ElementIsElementTypeFilter, as follows:
public static List<Element> FindAllElements(RvtDocument doc)
{
List<Element> list = new List<Element>(); FilteredElementCollector finalCollector = new FilteredElementCollector(doc); ElementIsElementTypeFilter filter1 = new ElementIsElementTypeFilter(false);
finalCollector.WherePasses(filter1);
ElementIsElementTypeFilter filter2 = new ElementIsElementTypeFilter(true);
finalCollector.UnionWith((new FilteredElementCollector(doc)).WherePasses(filter2)); list = finalCollector.ToList<Element>(); return list;
}
It is apparently cumbersome to do the simplest task but that is something we have to sacrifice for the performance gain.
Here is some code to call and test these element finders:
List<Element> elements = ElementFinder.FindAllRoofsCeilingsFloorsAndStairs_Approach1(CachedDoc);
MessageBox.Show(string.Format("There are {0} roofs, ceilings, floors and stairs in the model.", elements.Count.ToString())); elements = ElementFinder.FindAllRoofsCeilingsFloorsAndStairs_Approach2(CachedDoc);
MessageBox.Show(string.Format("There are {0} roofs, ceilings, floors and stairs in the model.", elements.Count.ToString())); elements = ElementFinder.FindAllElements(CachedDoc);
MessageBox.Show(string.Format("There are totally {0} elements in the model.", elements.Count.ToString())); So, most of times, we have to guess out which filters should be used and what filter arguments should be applied, and do a lot of experiments regarding these.
Fortunately, the Element Finder of the RevitAddinWizardcan help you generate similar code and actually much more complex code for any combinations of the filters and their arguments in a moment.
|