Is it possible to get the color of an element in active view when it's defined by a view filter?

Hello,

I want to get the color of an element in active view.

If the color is set by the color of the material or by element override, there is no problem in doing so.

But if the color of the element is set by a view filter, I don’t know how to obtain the color of the element through Dynamo.

I’m looking for some parameter that tells me the color of the element in the view. I am also looking for what elements are affected by a filter, but for now I have not succeeded.

Any suggestions?

Thanks

can you go by obtaining the number of filters in the view and see which is being activated??

I can get the filters with node View.Filters, but I do not know what elements are affected by those filters. I’m looking for a function like “Element.ViewFilters”, but I can’t find anything similar in the API.

Hmm…

I agree, I can’t see a property of an element which is ‘has been overriden by view filter’. Though obviously Revit does know this.

Unfortunately I guess that means you would need to replicate the filter… Retrieve the filter’s properties and run it through the elements in the view…

If you’re brave enough and have the skills, someone’s had a go here… Though given the number of permutations it would be difficult to catch them all without some clever coding…

Hope that’s of interest,

Mark

Edit: It can be translated to Python with a bit of effort…

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference("RevitNodes")
import Revit

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument

filters = UnwrapElement(IN[0])

for f in filters:
    rules = f.GetRules()
    catid = f.GetCategories()
    for c in catid:
        cats = 'Filter applied to ' + Revit.Elements.Category.ById(c.IntegerValue).Name;
        
for r in rules:
    if True == r.GetEvaluator().GetType().Equals(FilterStringContains):
        evaluator = 'Filter Type = Contains'
    ruleString = 'Filter Value = ' + r.RuleString

OUT = evaluator, ruleString, cats,

1 Like

In the case where you have some filter overlap or where an element falls under more than one filter, you have the keep in mind the order of the filters and not let the same element fall under multiple categories.

1 Like

Good point Kenny, unfortunately they seem to be returned in the order they were added, not the order they appear in the list. There must be some kind of view filter sequence number that Revit uses, but I can’t see that property in views?

Yeah, I am lost as to how you would get this done. It seems like a really difficult question. Your best luck is asking a true API expert. Maybe the Autodesk’s Revit API forums would be a good place to ask.

I’ve put a note on Jeremy Tammik’s blog, I’ll go see about that forum :slight_smile:
Edit… Fail… https://forums.autodesk.com/t5/revit-api-forum/view-filter-order/m-p/7282371

While you’re here… I’ve worked on the code a bit more, but the difficulty is that we’re having to test individually for each part of the constructor and these are all different classes… FilterStringRule Class etc.

Do you have a cute way of looping to avoid the individual testing? The 2019 addition of AND, OR etc. will make this worse…
Edit: Perhaps using definitions maybe? It seems inelegant to copy whole bunches of code around :slight_smile:

Cheers,

Mark

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference("RevitNodes")
import Revit

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument

filters = UnwrapElement(IN[0])

name = []
catid, cats, cati, catoutP  = [], [], [],[]
rules = []
evaluator = []
ruleString = []

for f in filters:
    #get filter name
    n = 'Filter Name - ' + f.Name
    name.append(n)
    #get category id for use in getting category    
    catid.append(f.GetCategories())
    for cat in catid:
        #list comprehension allows us to map the For over the base level of the list, this keeps the list structure
        catoutP.append([Revit.Elements.Category.ById(c.IntegerValue).Name for c in cat])
    for o in catoutP:
        #we want to join all the categories that each filter applies to into a single string
        c = 'Category Classes - ' + (', '.join(o))
    cati.append(c)
        
    rules.append(f.GetRules())  
    
for rule in rules:
    for r in rule:
         #get the value which the filter is looking for, this can be different types... strings, booleans... this example just looks for strings, but it should be expanded to cover all types
         ruleString.append('Filter Value = ' + r.RuleString)
         
         #similar to the rule value, the evaluator could be multiple types, this example just looks for string contains and string equals
         if True == r.GetEvaluator().GetType().Equals(FilterStringContains):
            evaluator.append('Filter Type = Contains')
            
         elif True == r.GetEvaluator().GetType().Equals(FilterStringEquals):
            evaluator.append('Filter Type = Equals')
         
         else: 
             evaluator.append('Filter Type Fail')

#we want to gather all the output information together
outInfo = []
outInfo = [name, evaluator, ruleString, cati]

#we want to transpose the order of the output information so that it becomes by each filter, rather than by each output
matrix = zip(*outInfo)
outPut = []
for row in matrix:
    outPut.append(row)

OUT = outPut

image
http://www.revitapidocs.com/2018/166d75f9-1088-3275-2219-867c1142d8da.htm

I tried using a dictionary plus a function for it. Fortunately a few of them use the same name of a function but not all so I still have to use some if statements but it should be easy to add on to it if you come across more filter types than what I had.

Apparently there is a class for inverse filter rules, basically means does not (contain, equal, etc.) that you have to dig one level deeper in, so I just go through the function again but with .GetInnerRule() and added a 'Not - ' infront of it.

filterrules_api

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference('RevitNodes')
import Revit

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument

filtertypes = {
"Autodesk.Revit.DB.FilterDoubleRule" : 0,
"Autodesk.Revit.DB.FilterElementIdRule" : 0,
"Autodesk.Revit.DB.FilterIntegerRule" : 0,
"Autodesk.Revit.DB.FilterStringRule" : 1,
"Autodesk.Revit.DB.FilterInverseRule" : 2}

def filterTesting(rClass):
    if filtertypes[rClass.ToString()] == 0:
        return rClass.RuleValue
    elif filtertypes[rClass.ToString()] == 1:
        return rClass.RuleString
    elif filtertypes[rClass.ToString()] == 2:
        return "Not - " + str(filterTesting(rClass.GetInnerRule()))
    else:
        return rClass.ToString()

filters = UnwrapElement(IN[0])

name = []
catid, cats, cati, catoutP = [], [], [],[]
rules = []
evaluator = []
ruleString = []

for f in filters:
    rules.append(f.GetRules())

OUT = []
for rule in rules:
    for r in rule:
        OUT.append(filterTesting(r))

The best way to do the whole script, though, would probably be to put your second half all in a function and then just run the function on all of the parameter filter element in the first for loop.

2 Likes

Hi Sergio,

Retrieving the color might be a bit tricky. Since we have to deal with multiple cases.

The element may defined by parameters such as:

  1. Be defined by a view-specific override.
  2. Be defined by a view filter
    2a. Be defined by multiple conflicting view filters (lets ignore this for now since it overcomplicates things)
  3. Be defined by the material.

Now if you wish to get the color (in general) I would suggest:

  1. Check if a view-specific override applies .
  2. Figure out which filters applies to the element for the view.
  3. Check the filter overrides of the filter.
  4. Get the color is the (for instance) projectionfillcolor is valid (of either override or filter).
  5. If no override or filter applies get the color for the materials.

For the filter consider solely step 2-4.

Now since I thought it would be a fun exercise I have written a (probably way too complicated) script. Apologies for the lazy imports, hope it will be useful to some people and would like some input as to making it better :grinning:

Unfortunately, I was unable to get around the concern of having to evaluate each of the filters for every element as raised by Mark.Ackerley.

#2019, Lukas Mikkelsen
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
# Import Element wrapper extension methods
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
# Import ToProtoType, ToRevitType geometry conversion extension methods
clr.ImportExtensions(Revit.GeometryConversion)
# Import DocumentManager and TransactionManager
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
clr.AddReference('DSCoreNodes')
import DSCore
from DSCore import *
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument
# Import RevitAPI
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import System
from System import Array
from System.Collections.Generic import *
import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)
import os
#The inputs to this node will be stored as a list in the IN variable.
if(isinstance(IN[0],list)):
	elements = UnwrapElement(IN[0])
else:
	elements =[UnwrapElement(IN[0])]
output = []
for element in elements:
	activeview = doc.ActiveView
	override = activeview.GetElementOverrides(element.Id)
	color = None
	if override.ProjectionFillColor.IsValid:
		color = ','.join(override.ProjectionFillColor.Red.ToString(),override.ProjectionFillColor.Blue.ToString(),override.ProjectionFillColor.Green.ToString())
	else:
		fels = activeview.GetFilters()
		for fel in fels:
			filtercats = [x for x in doc.GetElement(fel).GetCategories()]
			# Check if element category corresponds to filter category
			if element.Category.Id in filtercats:
		 		filterrules = doc.GetElement(fel).GetRules()
		 		rulepassed = []
		 		# Check if element passes all filter rules
		 		for frule in filterrules:
		 			rulepassed.append(frule.ElementPasses(element))
		 		if all(rulepassed):
		 			override = activeview.GetFilterOverrides(fel)
		 			# BEWARE: THIS PART ASSUMES THAT MULTIPLE FILTERS DOES NOT COLLIDE IN REGARDS TO COLOR DEFINITION OF THE ELEMENT
		 			if override.ProjectionFillColor.IsValid:
		 				color = ','.join([override.ProjectionFillColor.Red.ToString(),override.ProjectionFillColor.Blue.ToString(),override.ProjectionFillColor.Green.ToString()])
		 			else:
		 				pass
		 		# Otherwise element does not satisfy the filter
		 		else:
		 			pass
			# Otherwise the element category does not correspond to the filter category
			else:
				pass
		# Check if color has been previously defined.
		# If the color is retrieved based on material, our element may consist of multiple materials.
		if not color:
			color = list()
			mats = [doc.GetElement(x) for x in element.GetMaterialIds(False)]
			for mat in mats:
				color.append(','.join([mat.Color.Red.ToString(),mat.Color.Blue.ToString(),mat.Color.Green.ToString()]))
		else:
			pass
	output.append(color)
#Assign your output to the OUT variable
OUT = output
2 Likes

That’s awesome :slight_smile: I totally missed http://www.revitapidocs.com/2019/d0a73972-2d31-1e23-590a-5094367aff87.htm I feel like an idiot! :smiley:

Hi everyone,

We have used the script to get our filter rules and it works fine for the rules with the AND function.
However, for the filters with the OR function, the script is failing. Is there an other way to get those filter rules also?