Summary
Quick Filters
We are going to start exploring Quick Filters in this lesson!
As the name suggests, the are quicker than Slow filters because they work on ElementRecord Class. This class represents a record in Revit's DataBase with limited information available to us and therefore they are quicker.
Slow filters on the other hand, have to first expand elements in the memory to read all of their properties to filter. Therefore is the naming Slow vs Quick Filters.
However, keep in mind while some filters are quicker than Slow Filters, we are talking about fraction of a second difference. And in most cases you won't even notice a difference between them. So my advice is to use any filters that you need, and only focus on efficiency if your scripts take too long to execute.
Which Filters are Quick Filters?
Usually when you explore filters in Revit API it will be mentioned somewhere in the description or remarks that it's a quick/slow filter.
But I found an interesting resource that lists all Quick and Slow filters in a table:
Autodesk Revit API Developer's Guide - Applying Filters
Inside this page you will find some description of Quick, Slow and Logical filters but most importantly you will find a table of Quick and Slow filters separately.
I have recreated this cable here with links so it's easier to open them up.
💡 I also noticed that ElementWorksetFilter was missing, so I added it to my list!
Revit API - Quick Filters
Elements which have a bounding box that contains a given point
Elements which have a bounding box that contains a given point
Elements which have a bounding box which intersects a given outline
Elements which have a bounding box which intersects a given outline
Elements which have a bounding box inside a given outline
Elements which have a bounding box inside a given outline
Elements matching any of a given set of categories
Elements matching any of a given set of categories
Elements matching a given set of classes (or derived classes)
Elements matching a given set of classes (or derived classes)
Elements matching a given structural type
Elements matching a given structural type
A filter used to find all family symbols of the given family.
A filter used to find all family symbols of the given family.
A filter used to match elements which reside in a given workset.
A filter used to match elements which reside in a given workset.
— Filters with Shortcuts in FEC —
OfCategory() OfCategoryId()
Elements matching the input category id
OfCategory() OfCategoryId()
Elements matching the input category id
Elements matching the input runtime class (or derived classes)
Elements matching the input runtime class (or derived classes)
WhereElementIsElementType() WhereElementIsNotElementType()
Elements which are "Element types"
WhereElementIsElementType() WhereElementIsNotElementType()
Elements which are "Element types"
ContainedInDesignOption()
Elements in a particular design optionents matching the input runtime class (or derived classes)
ContainedInDesignOption()
Elements in a particular design optionents matching the input runtime class (or derived classes)
WhereElementIsCurveDriven()
Elements which are curve driven
WhereElementIsCurveDriven()
Elements which are curve driven
All elements except the element ids input to the filter
All elements except the element ids input to the filter
Elements which are view-specific
Elements which are view-specific
Elements that are most likely visible in the given view
Elements that are most likely visible in the given view
Elements whose ElementIds are included in a collection
Elements whose ElementIds are included in a collection
This table show you:
Filter's Name + Docs Link
Short Description
FEC Shortcut if available
What's FilteredElementCollector Shortcut?
As you already know, when we use FilteredElementCollector's methods most of them are just applying corresponding filter to the collector. For example:
Reverse Filter
You might have noticed that some filters have multiple shortcuts, and it's because some filters have an option to reverse the filter.
For example ElementIsElementTypeFilter Filter is filtering only types, but if we apply reverse, it will collect anything but types, and so we get instances.
It looks like this in Revit API Docs:
How to use these Filters?
You have a list of all Quick filters now! Let's go through all of them and see how to use them.
I will try to keep it short and most importantly give you a code snippet that you can use in your own scripts! Also some filters have shortcuts in FilteredElementCollector methods, and I will mark them with a * symbol in the end and mention the snippet as well.
💪 Let's Begin!
1️⃣ BoundingBoxContainsPointFilter
A filter used to match elements with a bounding box that contains the given point.
base_pnt = XYZ(15, 15, 0)
filter = BoundingBoxContainsPointFilter(base_pnt)
collector = FilteredElementCollector(doc)
contain_elements = collector.WherePasses(filter).ToElements()
print('BB Contains Point Found:')
for i in contain_elements :
print(i)
not_contain_filter = BoundingBoxContainsPointFilter(base_pnt, True)
collector = FilteredElementCollector(doc)
not_contain_walls = collector.OfClass(Wall).WherePasses(not_contain_filter).ToElements()
print('Walls with BB Not Contains Found:')
for i in not_contain_walls:
print(i)
In my example I got elements that intersect the point XYZ(15,15,0) as contain_elements,
and then I got other walls as not_contain_walls
2️⃣ BoundingBoxIntersectsFilter
A filter used to match elements with a bounding box that intersects the given Outline.
my_out_ln = Outline(XYZ(50, 0, 0), XYZ(80, 30, 10))
filter = BoundingBoxIntersectsFilter(my_out_ln)
collector = FilteredElementCollector(doc, doc.ActiveView.Id)
elements = collector.WherePasses(filter).ToElements()
for i in elements:
print(i)
invert_filter = BoundingBoxIntersectsFilter(my_out_ln, True)
collector = FilteredElementCollector(doc)
not_intersect_walls = collector.OfClass(Wall).WherePasses(invert_filter).ToElements()
In this example I will get all intersecting elements in the specified BoundingBox.
I got 4 Lines and 5 Elements.
💡 Notice that I even got elements that are partially inside the outline we specified!
💡 I also added ActiveView.Id to my collector for this example, but you might want to remove it if you want across the whole model.
3️⃣ BoundingBoxIsInsideFilter
Constructs a new instance of a filter to match elements with a bounding box that is contained by the given Outline.
my_out_ln = Outline(XYZ(100, 0, 0), XYZ(130, 30, 10))
filter = BoundingBoxIsInsideFilter(my_out_ln)
collector = FilteredElementCollector(doc)
elements = collector.WherePasses(filter).ToElements()
for i in elements:
print(i)
outside_filter = BoundingBoxIsInsideFilter(my_out_ln, True)
collector = FilteredElementCollector(doc)
outside_founds = collector.OfClass(Wall).WherePasses(outside_filter).ToElements()
In this example I will get only elements that are completely inside the BoundingBox that I created.
So I only got 4 Lines and 3 Elements.
💡2 Elements on sides were not included as they are only partially intersect the BoundingBox
💡 I also added ActiveView.Id to my collector for this example, but you might want to remove it if you want across the whole model.
4️⃣ ElementMulticategoryFilter
A filter used to find elements whose category matches any of a given set of categories.
This filter has 4 constructors depending on the arguments
Provide list of BuiltInCategories
Provide List of ElementIds of Categories
Same as first but with an option to invert with bool
Same as second but with an option to invert with bool
Here is a snippet to get Walls, Floors, Roofs and Ceilings with this filter. 👇
cats = [
BuiltInCategory.OST_Walls,
BuiltInCategory.OST_Floors,
BuiltInCategory.OST_Roofs,
BuiltInCategory.OST_Ceilings
]
import clr
clr.AddReference('System')
from System.Collections.Generic import List
List_cats = List[BuiltInCategory](cats)
multi_cat_filter = ElementMulticategoryFilter(List_cats)
elements = FilteredElementCollector(doc).WherePasses(multi_cat_filter).ToElements()
5️⃣ ElementMulticlassFilter
A filter used to match elements by their class, where more than one class of element may be passed.
This Filter has 2 constructors
Provide list of Types
Provide list of Types + invert bool
Here is a snippet to get Walls, Floors, Roofs and Ceilings with this filter. 👇
And if you want to get all other types except those, just add True boolean to invert the filter.
import clr
clr.AddReference('System')
from System.Collections.Generic import List
from System import Type
types = [Wall, Floor, RoofBase, Ceiling]
List_types = List[Type]()
for i in types:
List_types.Add(i)
multi_class_filter = ElementMulticlassFilter(List_types)
elements = FilteredElementCollector(doc, doc.ActiveView.Id).WherePasses(multi_class_filter).ToElements()
for el in elements:
print(el)
6️⃣ ElementStructuralTypeFilter
Constructs a new instance of a filter to match elements by structural type.
This filter takes StructuralType as an argument and also has an option to be inverted with a boolean.
StructuralType is an enumeration, but I will show you various option in the snippet.
from Autodesk.Revit.DB.Structure import StructuralType
filter_non_str = ElementStructuralTypeFilter(StructuralType.NonStructural)
filter_beam = ElementStructuralTypeFilter(StructuralType.Beam)
filter_brace = ElementStructuralTypeFilter(StructuralType.Brace)
filter_column = ElementStructuralTypeFilter(StructuralType.Column)
filter_footing = ElementStructuralTypeFilter(StructuralType.Footing)
filter_unknown = ElementStructuralTypeFilter(StructuralType.UnknownFraming)
filter_non_str = FilteredElementCollector(doc).WherePasses(filter_non_str).ToElements()
filter_beam = FilteredElementCollector(doc).WherePasses(filter_beam).ToElements()
filter_brace = FilteredElementCollector(doc).WherePasses(filter_brace).ToElements()
filter_column = FilteredElementCollector(doc).WherePasses(filter_column).ToElements()
filter_footing = FilteredElementCollector(doc).WherePasses(filter_footing).ToElements()
filter_unknown = FilteredElementCollector(doc).WherePasses(filter_unknown).ToElements()
7️⃣ FamilySymbolFilter
Constructs a new instance of a filter to find all family symbols of the given family.
This Filter is great if you have a Family and you want to get all available types.
It has a simple constructor, where we need to provide ElementId of a Family.
fam = doc.GetElement(ElementId(413400))
family_symbol_filter = FamilySymbolFilter(fam.Id)
collector = FilteredElementCollector(doc).WherePasses(family_symbol_filter)
for typ in collector:
type_name = Element.Name.GetValue(typ)
print(type_name)
8️⃣ ElementWorksetFilter
A filter used to match elements which reside in a given Workset.
💡 It's a simple filter but we need to get our worksets. And to do that we need to use FilteredWorksetCollector.
Here is the code snippet:
worksets = FilteredWorksetCollector(doc)\
.OfKind(WorksetKind.UserWorkset)
for workset in worksets:
workset_filter = ElementWorksetFilter(workset.Id)
elements_in_workset = FilteredElementCollector(doc)\
.WherePasses(workset_filter)\
.ToElements()
print('Workset: {} has {} Elements'.format(workset.Name,
len(elements_in_workset)))
9️⃣ ElementCategoryFilter*
A filter used to match elements by their category.
This filter has shortcuts in FilteredElementCollector class and you are already very familiar with how to use it
collector = FilteredElementCollector(doc)
walls = collector.OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements()
print('There are {} Wall Instances in the Project'.format(len(walls)))
I don't think you will ever need to use it as a filter since shortcut is very common and easy to use. But just as an example here is how it's used:
filter = ElementCategoryFilter(BuiltInCategory.OST_Walls)
collector = FilteredElementCollector(doc)
walls = collector.WherePasses(filter).WhereElementIsNotElementType().ToElements()
print('There are {} Wall Instances in the Project'.format(len(walls)))
🔟 ElementClassFilter*
A filter used to match elements by their class.
This filter also has a shortcut in FEC methods, and you are very familiar with it as well!
collector = FilteredElementCollector(doc)
walls = collector.OfClass(Wall).WhereElementIsNotElementType().ToElements()
print('There are {} Wall Instances in the Project'.format(len(walls)))
And again if you really want to see how to make it as a filter, here is an example:
filter = ElementClassFilter(Wall)
collector = FilteredElementCollector(doc)
collector.WherePasses(filter)
print('There are {} Wall Instances in the Project'.format(len(walls)))
1️⃣1️⃣ ElementIsElementTypeFilter*
A filter used to match elements which are ElementTypes or not ElementTypes.
This filter also has a shortcut in FilteredElementCollector Class, and you already very familiar with it.
el_instances = FilteredElementCollector(doc).WhereElementIsNotElementType().ToElements()
el_types = FilteredElementCollector(doc).WhereElementIsElementType().ToElements()
I doubt you will ever need to use it as a filter, but if you do, here is the snippet:
type_filter = ElementIsElementTypeFilter(True)
el_types = FilteredElementCollector(doc).WherePasses(type_filter).ToElements()
instance_filter = ElementIsElementTypeFilter(False)
instances = FilteredElementCollector(doc).WherePasses(instance_filter).ToElements()
1️⃣2️⃣ ElementDesignOptionFilter*
Constructs a new instance of a filter to match elements contained within a particular design option.
This filter also have a shortcut in FilteredElementColelctor and I showed you this example in one of previous lessons:
all_design_opts = FilteredElementCollector(doc)\
.OfClass(DesignOption)\
.ToElements()
if all_design_opts:
for design_opt in all_design_opts:
opt_collector = FilteredElementCollector(doc)\
.ContainedInDesignOption(design_opt.Id)\
.WhereElementIsNotElementType()\
.ToElements()
opt_collector_walls = FilteredElementCollector(doc)\
.ContainedInDesignOption(design_opt.Id)\
.OfClass(Wall)\
.WhereElementIsNotElementType()\
.ToElements()
print('Design Option [{}] has {} Elements'.format(design_opt.Name, len(opt_collector)))
print('Design Option [{}] has {} Walls'.format(design_opt.Name, len(opt_collector_walls)))
And it's very similar if you want to make it as a filter, here is modified code:
all_design_opts = FilteredElementCollector(doc)\
.OfClass(DesignOption)\
.ToElements()
if all_design_opts:
for design_opt in all_design_opts:
filter_ = ElementDesignOptionFilter(design_opt.Id)
opt_collector = FilteredElementCollector(doc)\
.WherePasses(filter_)\
.WhereElementIsNotElementType()\
.ToElements()
1️⃣3️⃣ ElementIsCurveDrivenFilter*
A filter used to match elements which are curve driven.
The term "curve driven" indicates that the element's Location property is a LocationCurve. Example elements found by this filter include walls, beams, and other curve elements.
This Filter also has a shortcut in FilteredElementCollector that we covered in previous module. Here is the snippet:
curve_driven_elements = FilteredElementCollector(doc)\
.WhereElementIsCurveDriven()\
.ToElements()
But in case you want to create a filter and apply it separately, here is an example:
crv_filter = ElementIsCurveDrivenFilter()
crv_elements = FilteredElementCollector(doc)\
.WherePasses(crv_filter)\
.ToElements()
not_crv_filter = ElementIsCurveDrivenFilter(True)
not_crv_elements = FilteredElementCollector(doc)\
.WherePasses(not_crv_filter )\
.ToElements()
1️⃣4️⃣ ExclusionFilter*
A filter used to exclude a set of elements from the collector.
This filter has a shortcut method in FilteredElementCollector:
selected_element_ids = uidoc.Selection.GetElementIds()
if selected_element_ids:
excl_collector = FilteredElementCollector(doc, active_view.Id)\
.Excluding(selected_element_ids)\
.WhereElementIsNotElementType()\
.ToElements()
print('There are {} Elements Excluding current selection in the Active View'.format(len(excl_collector)))
And it's also very simple to make it as a filter
selectedIds = uiDocument.Selection.GetElementIds()
filter = ExclusionFilter(selectedIds)
walls = FilteredElementCollector(doc)\
.WherePasses(filter)\
.OfClass(Wall)\
.ToElements()
1️⃣5️⃣ ElementOwnerViewFilter*
Constructs a new instance of a filter to match elements which are owned by a particular view, with the option to invert the filter and find elements not owned by the given view.
This Filter also has a shortcut method in FilteredElementCollector Class and I showed how to use it previously.
Here is the recap:
owned_view_collector = FilteredElementCollector(doc)\
.OwnedByView(active_view.Id)\
.ToElements()
But if you want to use it as a filter, here is the Snippet.
Notice that this filter also has an option to invert it with a bool argument.
view_id = doc.ActiveView.Id
view_filter = ElementOwnerViewFilter(viewId)
elements = FilteredElementCollector(doc).WherePasses(view_filter).ToElements()
1️⃣6️⃣ VisibleInViewFilter
A quick filter that passes elements that are most likely visible in the given view.
This filter doesn't have any shortcuts, but it's the same as using second constructor of FilteredElementCollector.
elements_in_view = FilteredElementCollector(doc, doc.ActiveView.Id).WhereElementIsNotElementType().ToElements()
If you want to get elements from a single view just stick to the second constructor.
However, if you want to create multiple filters and combine them with LogicalFilter(more on them later), then it makes sense to use these filters.
Here is an example how to combine 3 filters.
view_ids = [ElementId(684875), ElementId(684885), ElementId(684895)]
filters = [VisibleInViewFilter(doc, view_id) for view_id in view_ids]
combined_filter = LogicalOrFilter(filters)
elements = FilteredElementCollector(doc)\
.WherePasses(combined_filter)\
.OfClass(FamilyInstance)\
.ToElements()
for el in elements:
print(el)
1️⃣7️⃣ ElementIdSetFilter*
Constructs a new instance of a filter wrapping a set of elements.
This filter doesn't have a shortcut method, however it's the same as using the 3rd constructor of FilteredElementCollector Class.
import clr
clr.AddReference('System')
from System.Collections.Generic import List
el_ids = [ElementId(12345), ElementId(12346), ElementId(12347), ]
List_el_ids = List[ELementId](el_ids)
if List_el_ids:
collector = FilteredElementCollector(doc, List_el_ids )
Here is a code snippet, but I don't think anyone will ever need to use it like this.
import clr
clr.AddReference('System')
from System.Collections.Generic import List
el_ids = [ElementId(12345), ElementId(12346), ElementId(12347), ]
List_el_ids = List[ELementId](el_ids)
if List_el_ids:
filter_set = ElementIdSetFilter(List_elements)
collector = FilteredElementCollector(doc).WherePasses(filter_set)
Conclusion
When you want to use Revit API filters and they have shortcuts in FilteredElementCollector's methods, I would recommend sticking to using methods. They are easier, quicker and I don't see any benefit of creating filtlers.
However, you also saw many new filters that don't have any shortcuts, and there is only one way to use them. And it means we need to create that filter and apply it using WherePasses method.
Now you have snippets for all Revit API Quick Filters, and you know that they are not that complicated!
Next we will look at Slow Filters in the next lesson!
Should I always use Shortcuts if possible?
Should I always use Shortcuts if possible?
Should I worry if filter is Quick or Slow?
Should I worry if filter is Quick or Slow?
Why only certain filters have shortcuts?
Why only certain filters have shortcuts?