How to paint every wall surface

hi, i want to paint every single surface wall on a proyect. is there a way to do it?

Hi @marguello49J42,

You’re looking for something like that.

2 Likes

hi, thank u very much for answering, its been a first step. but im looking for a way to apply a different material paint on every wall surface, so i can export it to lumion to apply different materials. should have said that at the begining, didnt i? sorry for that, im kind of new around here

See here for an example of painting surfaces with a material.

1 Like

I’m having trouble with painting wall surfaces too. This thread wasn’t helpful, multiple people had errors with the Python scripts posted and no one seemed to get a solution or a .dyn posted.

What have you tried?

Hi Alex,

It’s good practice here, to post what sort of progress you’ve made on your own.

What sort of errors do you get, when you are trying to run said python scripts? Are you trying to paint all faces of the walls, or is it specific faces you want to paint?

To make it even easier for people trying to help you, you could attach a small Revit test file and your dyn.

I got this Python script from one of the prior threads, but I’m getting the same error someone else got: “TypeError: Multiple targets could match: GetElement(Reference), GetElement(str), GetElement(ElementId)”

Script attached. Select a room with some bordering walls. For now just pulling the first Material in the library:
Paint Walls in Room.dyn (12.8 KB)

Edit: in the model, it’s highlighting the wall faces in an orange, like an error/warning:

The orange faces are the Dynamo preview geometry.

The issue here is that you are working with three different types of faces.

  1. The Dynamo geometry, referred to as a surface in common preview, but the actual object type (use an Object.Type node liberally to get to the root of this) is Autodesk.DesignScript.Geometry.Surface.
  2. The faces which make up the invisible room geometry, which are Revit geometry elements, having a Autodesk.Revit.DB.PlanarFace object.
  3. The faces of the walls which make up the wall geometry, which are actual Revit elements, having the associated geometry values (likely Autodesk.Revit.DB.PlanarFace again but I didn’t check).

It isn’t my code so I am hesitant to comment on it, but the paint method which is illustrated in the thread linked above, you need to feed in a Dynamo surface, which then finds the ‘closest matching Revit face’ from that shape. This starts with the line “surf.Tags.LookupTag(‘RevitFaceReference’)” where it pulls the face reference, then gets the element from that value, and then the element which contains the face, and then pulls the actual Revit face itself, at which point it asks “Is the face painted? If not paint it, otherwise do nothing.” In your case, you’re feeding it a Revit face and it can’t find the underlying Revit face so it’s failing. It’s like asking to divide 1 by “zebra” - Dynamo (or rather Revit via Dynamo) is telling you “you don’t make sense because there is no number called zebra”, or in this case "you don’t make sense as there is no way to pull an associated Revit face from a Revit face.

So that said, hopefully you have an understanding of why this is happening, but not yet how to fix it.

And fixing it is a more difficult issue, as it depends on a variety of stuff including how you are modeling and what you’re really after.

Note that in the UI if you ‘paint’ a wall which runs continuously across rooms as you indicate above, it will spill across rooms. if you’re ok with this, then no need to change anything or complicate it. If you aren’t, then you’ll have to split the face of each wall at the limits of the room just like in real the UI. This can be done either manually or via the Dynamo/API (I don’t have an example of this handy, but I recall seeing them around). Be sure to catch the vertical separations as well (below the floors, above the ceilings, etc). Once that is done your efforts get a LOT simpler as the face of the room may actually return the face of the wall (they’ll now be the same size(ish)), and you should be able to rig up some Python to paint inside the lines via the Revit Autodesk.Revit.DB.PlanarFace objects.

If you don’t do that, or if you don’t want to fiddle with Python, you’ll have to take the time to check which overlapping wall surfaces intersect with the room geometry, and feed those surfaces into the Python node. This does require a few Boolean filters to see which wall surfaces intersect the room geometry, and then which intersecting surfaces are actual surfaces not lines. Once you’re done you can move along pretty quickly to the Python without edits. Graph winds up looking something like this:

Hopefully this sheds enough light and you can make some headway. Keep in mind that the variety of methods used for working in Revit means that you’ll vary rarely find ‘ready to play’ graphs unless your work methods align perfectly with whoever’s code you’re using, and the methods of applying Room finishes is certainly one such application where this variety makes things VERY difficult to easily share.

3 Likes

Nice write up, @jacob.small :slight_smile:

You covered everything there is to cover, but I thought I’d just chip in as well:

Python Script:

import clr
import sys

sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\Lib')

clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')

import Autodesk
import RevitServices

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc =  DocumentManager.Instance.CurrentDBDocument


faces = UnwrapElement(IN[0])
elems = UnwrapElement(IN[1])
material = UnwrapElement(IN[2])
bool = IN[3]

output = []

if bool:
	try:
		errorReport = None
		TransactionManager.Instance.EnsureInTransaction(doc)
		for i,j in zip(faces, elems):
			doc.Paint(j.Id, i, material.Id)
			output.append('Wall with Element Id ' + '{}'.format(j.Id) + ' was painted')
		TransactionManager.Instance.TransactionTaskDone()
	except:
		import traceback
		errorReport = traceback.format_exc()
		
	if None == errorReport:
		OUT = output
	else:
		OUT = errorReport

else:
	OUT = 'Set bool to true' 

This does feel a bit glitchy though, and the workflow should be optimised according to Jacobs explenation, regarding walls going through multiple rooms. It also seems to not like curtain walls very much. But anyways - here’s something to work with, as a suplement to Jacobs solution.

Good luck :slight_smile:

4 Likes

This is very helpful. I guess I didn’t realize that Room elements had invisible faces on them, though it makes sense considering the error, and how it seems the Room.Finishes node gets its faces. I think I have enough to work with between yours and MartinSpence’s replies. Thank you again!

1 Like

After you get yourself squared away remember to come back and ‘mark’ a solution so others don’t struggle as much as yours.

FWIW I would give it to Martin here as his has code that others can just use (should it work, which I think it will :slight_smile: )

@jacob.small, Great job with this bud!

I was wondering if you can push the paint settings into a Linked File? AKA, I have a team of Interior designers but they want to put materials onto the walls of the Architectural. I always tell them communication is key even if you are doing this, but was wondering if we could modify it for Linked Files?

Thank you so much.
Kevin

1 Like

Yes but much like Mordor, one not simply modify a linked document. Multiple concurrent instance issues, opening the document, syncing, and all the other issues which can occur will matter here. And that is with just local network access - what if this was a consumed model on BIM360?

Best to set the team up to communicate and have the architects run the graph in that model. Ideally this would be by querying the desired finishes from the data provided by the interior team and having Dynamo apply the content for them - basically the arch team needs to run the same graph in their model as the interior team ran in theirs. :slight_smile:

2 Likes

I figured you would say something like that hahahaha! Im just trying to figure out a way to mainstream specific connections between fields.

It comes down to even between lighting professionals
 Which model own’s the lights? Is it the Architects with the ceilings? or is it the Lighting Designers?

Hence why I am trying to utilize scripts to interject information into eachother’s model, while still keeping it separate for management and size purposes. These models I am going to be working with get over 1 million sq ft, so interjecting information would be vital / helpful. I just was not sure if it was possible!

1 Like

It’s possible, but not necessarily easy, and the specifics mean that you need to solve it in the context of your project or start a thread where you provide a complete data set mimicking worksharing methods, links, and the like.

In the end the Architect owns their model, so the ID telling them ‘the roses need to be painted red’ is no different from them saying ‘the window has to move.’ The architect should own and update both (IMO, but other opinions and methods are certainly valid). The application of automation to do this (perhaps by reading an external spreadsheet that provides the room finishes) helps everyone and should be the near term goal while we await the perfect world.

Yeah
 That was the work around I was kind of thinking too
 That the Interior Designer’s can extract the Room Data Via the information from the Link into an excel document, and then translate the Architects to interject the Interior Design Excel sheet into their models.
Basically a To and From Scripts Would Be Needed.

Thanks again for all your help bud and hope all is well!

1 Like

I updated it for Revit 2023:


Clockwork node was not working so I hacked it.
Filtered for only planar faces. Can make work on cylindrical faces?
Paint Walls in Room.dyn (48.3 KB)