Quick Filters in Revit APi

Let's cover Quick Filters in Revit API in this lesson. Most importantly I want to give you code snippets so you understand how to use them!

Quick Filters in Revit APi

Let's cover Quick Filters in Revit API in this lesson. Most importantly I want to give you code snippets so you understand how to use them!

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

FEC Shortcut:

None

Elements which have a bounding box that contains a given point

FEC Shortcut:

None

Elements which have a bounding box that contains a given point

FEC Shortcut:

None

Elements which have a bounding box which intersects a given outline

FEC Shortcut:

None

Elements which have a bounding box which intersects a given outline

FEC Shortcut:

None

Elements which have a bounding box inside a given outline

FEC Shortcut:

None

Elements which have a bounding box inside a given outline

FEC Shortcut:

None

Elements matching any of a given set of categories

FEC Shortcut:

None

Elements matching any of a given set of categories

FEC Shortcut:

None

Elements matching a given set of classes (or derived classes)

FEC Shortcut:

None

Elements matching a given set of classes (or derived classes)

FEC Shortcut:

None

Elements matching a given structural type

FEC Shortcut:

None

Elements matching a given structural type

FEC Shortcut:

None

A filter used to find all family symbols of the given family.

FEC Shortcut:

None

A filter used to find all family symbols of the given family.

FEC Shortcut:

None

A filter used to match elements which reside in a given workset.

FEC Shortcut:

None

A filter used to match elements which reside in a given workset.

— Filters with Shortcuts in FEC —

FEC Shortcut:

OfCategory() OfCategoryId()

Elements matching the input category id

FEC Shortcut:

OfCategory() OfCategoryId()

Elements matching the input category id

FEC Shortcut:

OfClass()

Elements matching the input runtime class (or derived classes)

FEC Shortcut:

OfClass()

Elements matching the input runtime class (or derived classes)

FEC Shortcut:

WhereElementIsElementType() WhereElementIsNotElementType()

Elements which are "Element types"

FEC Shortcut:

WhereElementIsElementType() WhereElementIsNotElementType()

Elements which are "Element types"

FEC Shortcut:

ContainedInDesignOption()

Elements in a particular design optionents matching the input runtime class (or derived classes)

FEC Shortcut:

ContainedInDesignOption()

Elements in a particular design optionents matching the input runtime class (or derived classes)

FEC Shortcut:

WhereElementIsCurveDriven()

Elements which are curve driven

FEC Shortcut:

WhereElementIsCurveDriven()

Elements which are curve driven

FEC Shortcut:

.Excluding

All elements except the element ids input to the filter

FEC Shortcut:

.Excluding

All elements except the element ids input to the filter

FEC Shortcut:

OwnedByView

Elements which are view-specific

FEC Shortcut:

OwnedByView

Elements which are view-specific

FEC Shortcut:

2nd Constructor

Elements that are most likely visible in the given view

FEC Shortcut:

2nd Constructor

Elements that are most likely visible in the given view

FEC Shortcut:

3rd Constructor

Elements whose ElementIds are included in a collection

FEC Shortcut:

3rd Constructor

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.

# Use BoundingBoxContainsPoint filter to find elements with a bounding box that contains the 
# given point in the document.

# Create a BoundingBoxContainsPoint filter for base point of (15, 15, 0)
base_pnt = XYZ(15, 15, 0)
filter   = BoundingBoxContainsPointFilter(base_pnt)

# Apply the filter to the elements in the active document
# This filter will excludes all objects derived from View and objects derived from ElementType
collector         = FilteredElementCollector(doc)
contain_elements  = collector.WherePasses(filter).ToElements()
print('BB Contains Point Found:')
for i in contain_elements  :
    print(i)

# Find walls that do not contain the given point: use an inverted filter to match elements
# Use shortcut command of_class() to find walls only
not_contain_filter = BoundingBoxContainsPointFilter(base_pnt, True)  # inverted filter
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.

# Use BoundingBoxIntersects filter to find elements with a bounding box that intersects the 
# given Outline in the document.

# Create an Outline, uses a minimum and maximum XYZ point to initialize the outline.
my_out_ln = Outline(XYZ(50, 0, 0), XYZ(80, 30, 10))

# Create a BoundingBoxIntersects filter with this Outline
filter = BoundingBoxIntersectsFilter(my_out_ln)

# Apply the filter to the elements in the active document
# This filter excludes all objects derived from View and objects derived from ElementType
collector = FilteredElementCollector(doc, doc.ActiveView.Id)
elements  = collector.WherePasses(filter).ToElements()

#👀 Print Elements
for i in elements:
    print(i)

# Find all walls which don't intersect with BoundingBox: use an inverted filter to match elements
# Use shortcut command of_class() to find walls only
invert_filter       = BoundingBoxIntersectsFilter(my_out_ln, True)  # inverted filter
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.

# Use BoundingBoxIsInside filter to find elements with a bounding box that is contained (inside completely)
# by the given Outline in the document.

# Create an Outline, use a minimum and maximum XYZ point to initialize the outline.
my_out_ln = Outline(XYZ(100, 0, 0), XYZ(130, 30, 10))

# Create a BoundingBoxIsInside filter for Outline
filter = BoundingBoxIsInsideFilter(my_out_ln)

# Apply the filter to the elements in the active document
# This filter excludes all objects derived from View and objects derived from ElementType
collector = FilteredElementCollector(doc)
elements  = collector.WherePasses(filter).ToElements()

#👀 Print Elements
for i in elements:
    print(i)

# Find walls outside BoundingBox: use an inverted filter to match elements
# Use shortcut command of_class() to find walls only
outside_filter = BoundingBoxIsInsideFilter(my_out_ln, True)  # inverted filter
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

  1. Provide list of BuiltInCategories

  2. Provide List of ElementIds of Categories

  3. Same as first but with an option to invert with bool

  4. 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. 👇

# List of categories we want to filter
cats = [
    BuiltInCategory.OST_Walls,
    BuiltInCategory.OST_Floors,
    BuiltInCategory.OST_Roofs,
    BuiltInCategory.OST_Ceilings
]

# Convert into List[BuiltInCategory]
import clr
clr.AddReference('System')
from System.Collections.Generic import List
List_cats = List[BuiltInCategory](cats)

# Create the multi-category filter
multi_cat_filter = ElementMulticategoryFilter(List_cats)

# Collecting elements from specified categories
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

  1. Provide list of Types

  2. 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 .NET System Classes
import clr
clr.AddReference('System')
from System.Collections.Generic import List
from System import Type

#📦 List of Types we want to Filter
types = [Wall, Floor, RoofBase, Ceiling]

#🔁 Convert into List[Type]
List_types = List[Type]()
for i in types: 
    List_types.Add(i)

#🔎 Create Filter
multi_class_filter = ElementMulticlassFilter(List_types)

#👉 Get Elements
elements = FilteredElementCollector(doc, doc.ActiveView.Id).WherePasses(multi_class_filter).ToElements()

#👀 Print Elements
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

# Create filter for beam elements
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)

# Collecting Structural Elements
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.

# Get Your Family!
fam = doc.GetElement(ElementId(413400))

# Create a FamilySymbolFilter
family_symbol_filter = FamilySymbolFilter(fam.Id)

# Use FilteredElementCollector to collect elements that match the filter
collector = FilteredElementCollector(doc).WherePasses(family_symbol_filter)

#👀 Display Results
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)

    # Collect elements in the specific workset
    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:

# Find all Wall instances in the document by using category filter
filter = ElementCategoryFilter(BuiltInCategory.OST_Walls)

# Apply the filter to the elements in the active document,
# Use shortcut where_element_is_not_element_type() to find wall instances only
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:

# Use ElementClassFilter to find instance of Walls
filter = ElementClassFilter(Wall)

# Apply the filter to the elements in the active document
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:

# Collecting all element types
type_filter = ElementIsElementTypeFilter(True)
el_types    = FilteredElementCollector(doc).WherePasses(type_filter).ToElements()

# Collecting all instances (not types)
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:

# 👉 .ContainedInDesignOption()

#1️⃣ Get All Design Options
all_design_opts = FilteredElementCollector(doc)\
                        .OfClass(DesignOption)\
                        .ToElements()

#2️⃣ Iterate Through DesignOptions if any.
if all_design_opts:
    for design_opt in all_design_opts:
        
        # 3️⃣  Get All Elements from DesignOption      
        opt_collector = FilteredElementCollector(doc)\
                        .ContainedInDesignOption(design_opt.Id)\
                        .WhereElementIsNotElementType()\
                        .ToElements()

        # 4️⃣  Get only Walls from DesignOption
        opt_collector_walls = FilteredElementCollector(doc)\
                                .ContainedInDesignOption(design_opt.Id)\
                                .OfClass(Wall)\
                                .WhereElementIsNotElementType()\
                                .ToElements()
        #5️⃣ Print Results
        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:

#1️⃣ Get All Design Options
all_design_opts = FilteredElementCollector(doc)\
                        .OfClass(DesignOption)\
                        .ToElements()

#2️⃣ Iterate Through DesignOptions
if all_design_opts:
    for design_opt in all_design_opts:
        filter_ = ElementDesignOptionFilter(design_opt.Id)
        
        # 3️⃣  Get All Elements from DesignOption      
        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:

# 👉 .WhereElementIsCurveDriven()
curve_driven_elements = FilteredElementCollector(doc)\
                         .WhereElementIsCurveDriven()\
                         .ToElements()

But in case you want to create a filter and apply it separately, here is an example:

# Create Filter
crv_filter = ElementIsCurveDrivenFilter()

# Apply Filter
crv_elements = FilteredElementCollector(doc)\
                      .WherePasses(crv_filter)\
                      .ToElements()


# Invert Filter
not_crv_filter = ElementIsCurveDrivenFilter(True) 
# Apply Filter
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:

# 👉 .Excluding()

# Get Current Selection
selected_element_ids = uidoc.Selection.GetElementIds()

if selected_element_ids:
    # Get Everything in view Excluding selected elements
    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

# Get all element ids which are current selected by user
selectedIds = uiDocument.Selection.GetElementIds()

# Use the selection to instantiate an exclusion filter
filter = ExclusionFilter(selectedIds)

# Apply the filter to the elements in the active document,
# Use shortcut method OfClass() to find Walls only
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:

# 👉 .OwnedByView()
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 # Replace with your View.Id

# Create the ElementOwnerViewFilter
view_filter = ElementOwnerViewFilter(viewId)

# Collecting elements visible or dependent on the specified view
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.

#📦 List of View Ids (Change to your views!)
view_ids = [ElementId(684875), ElementId(684885), ElementId(684895)] 

#🔎 Create list of VisibleInViewFilters
filters = [VisibleInViewFilter(doc, view_id) for view_id in view_ids]

# 🎉 Combine them with LogicalOrFilter
combined_filter = LogicalOrFilter(filters)

# 🚀 Collect elements visible in any of these 3 views
elements = FilteredElementCollector(doc)\
                 .WherePasses(combined_filter)\
                 .OfClass(FamilyInstance)\
                 .ToElements()

# ✨ Print elements
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.

# .NET Imports
import clr
clr.AddReference('System')
from System.Collections.Generic import List

# List of ElementIds
el_ids      = [ElementId(12345), ElementId(12346), ElementId(12347), ]
List_el_ids = List[ELementId](el_ids)

# FilteredElementCollector with a set of elements
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.

# .NET Imports
import clr
clr.AddReference('System')
from System.Collections.Generic import List

# List of ElementIds
el_ids      = [ElementId(12345), ElementId(12346), ElementId(12347), ]
List_el_ids = List[ELementId](el_ids)

# FilteredElementCollector with a set of elements
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!

Questions:

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?

Discuss the lesson:

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.

© 2023-2024 EF Learn Revit API

© 2023-2024 EF Learn Revit API

© 2023-2024 EF Learn Revit API