I need to use Paint Tool to replace materials for some components due to certain reasons. I aim to create a component applicable to most family types. Currently, the program correctly executes the operation for floor slabs, walls, overlapping beams, overlapping piles, columns that have undergone fill color processing, and sloping columns. However, it fails to execute for the following three cases: vertical columns, non-intersecting beams, and non-intersecting piles. After thorough investigation, I discovered that columns that have undergone fill color processing differ geometrically from those that have not. I have tried many methods but still cannot find a solution. (paint tool Column rootCount=5 rootTypes=[‘GeometryInstance’, ‘Line’, ‘Point’, ‘Solid’]. without paint tool Column rootCount=3 rootTypes=[‘GeometryInstance’, ‘Line’, ‘Point’]
项目1.rvt (6.3 MB)
paint tool easy.dyn (18.3 KB)
From what you’ve explained, it’s probably better to edit & apply materials rather than painting all the surfaces.
Paint tends to be better for one-off overrides
Hi @1257599758
It’s a bug. A workaround is described here.
https://www.autodesk.com/support/technical/article/caas/sfdcarticles/sfdcarticles/Paint-tool-does-not-work-on-certain-family-entities.html
we can apply the same logic

code Python (tested only with PythonNet3 and Revit 2025)
import clr
import sys
import System
#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.GeometryReferences)
#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
import traceback
def get_Faces_Elements(elem):
opt = Options()
opt.DetailLevel = ViewDetailLevel.Fine;
lst_geo = elem.get_Geometry(opt)
if lst_geo is None:
return []
faces = []
for geo in lst_geo:
if isinstance(geo, DB.Solid) and geo.Volume > 0:
faces.extend(list(geo.Faces))
elif isinstance(geo, DB.GeometryInstance) :
for geoI in geo.GetInstanceGeometry():
if isinstance(geoI, DB.Solid) and geoI.Volume > 0:
faces.extend(list(geoI.Faces))
return faces
toList = lambda x : x if hasattr(x, "__iter__") and not isinstance(x, (str, System.String)) else [x]
#Preparing input from dynamo to revit
lstelems = toList(UnwrapElement(IN[0]))
material = UnwrapElement(IN[1])
#Do some action in a Transaction
TransactionManager.Instance.ForceCloseTransaction()
tg = TransactionGroup(doc, "Dynamo Paint")
tg.Start()
for _ in range(2):
for elem in lstelems:
faces = get_Faces_Elements(elem)
error = False
is_paint = False
#
# remove paint
t0= Transaction(doc, "remove paint1")
t0.Start()
for face in faces:
try:
doc.RemovePaint(elem.Id, face)
except Exception as ex:
print(traceback.format_exc())
t0.Commit()
# apply paint
t1= Transaction(doc, "paint1")
t1.Start()
for face in faces:
try:
doc.Paint(elem.Id, face, material.Id)
except Exception as ex:
print(traceback.format_exc())
error = True
t1.Commit()
# check if faces are painted
try:
is_paint = any(not doc.IsPainted(elem.Id, face) for face in faces)
#doc.Regenerate()
except Exception as ex:
print(traceback.format_exc())
is_paint = False
#
# if failed apply workaround by copy an temp element and join geometry
if error or not is_paint:
#
t2= Transaction(doc, "paint2")
t2.Start()
temp_elemIds = DB.ElementTransformUtils.CopyElement(doc, elem.Id, XYZ(0.1,0.1,0))
doc.Regenerate()
DB.JoinGeometryUtils.JoinGeometry(doc, elem, doc.GetElement(temp_elemIds.First()) )
faces = get_Faces_Elements(elem)
for face in faces:
try:
doc.Paint(elem.Id, face, material.Id)
except Exception as ex:
print(traceback.format_exc())
t2.Commit()
# delete
t4= Transaction(doc, "delete")
t4.Start()
doc.Delete(temp_elemIds)
t4.Commit()
tg.Assimilate()
#
OUT = lstelems
1 Like
This is indeed a great idea. First, duplicate the identical component at the same location, then fill in the color before deleting it.
1 Like