Try how to use Paint Tool to replace materials for multiple components

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

doc_Paint_workaround

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