Obtain the solid of the python element

Good afternoon.
I’m developing a Python code that allows me to obtain the solid form of an element, but with a cascading flow. If the solid form cannot be obtained through one process, it moves to the next, with a final fallback being the export and import of a SAT solid form.

The code works, but I think it’s not very efficient yet. I would appreciate any help in improving it to make it as fast as possible, as the processing times seem very slow.
Code here..!!!

why the external upload though? just post it in the thread. downloading random files will make trust issues kick in. :joy:

I understand what you’re saying.

But the code has detailed documentation to simply describe the code flow, as well as the functions and the purpose of each function, along with some notes on the importance of that code… this has made the code quite long and exceed the allowed length for pasting. I did it this way because I wanted to clearly show the entire code structure, but if the code documentation isn’t necessary, then I’ll try to leave it here.

import clr

clr.AddReference("RevitAPI")

clr.AddReference("RevitAPIUI")

clr.AddReference("RevitServices")

clr.AddReference("ProtoGeometry")

clr.AddReference("RevitNodes")




import time

import random

import System

from System.Collections.Generic import List

import System.IO




import Autodesk

import Autodesk.Revit.DB

import Autodesk.Revit.DB.Architecture

import Autodesk.DesignScript.Geometry




from Autodesk.Revit.DB import *

from Autodesk.DesignScript.Geometry import *




from RevitServices.Persistence import DocumentManager

from RevitServices.Transactions import TransactionManager




import Revit

clr.ImportExtensions(Revit.GeometryConversion)




doc = DocumentManager.Instance.CurrentDBDocument




factor_metros_a_pies = 3.28084




_PIES3_A_METROS3 = 0.0283168




_ID_LINKS = int(BuiltInCategory.OST_RvtLinks)




umbral_volumen_m3 = 0.000625




def ConstruirMapaVinculosGlobal():

    mapa_local = {}

    try:

        colector_links = FilteredElementCollector(doc).OfClass(RevitLinkInstance).ToElements()

        for instancia_link in colector_links:

            try:

                doc_vinculado = instancia_link.GetLinkDocument()

                if doc_vinculado is not None:

                    mapa_local[doc_vinculado.Title] = instancia_link

            except:

                continue

    except:

        pass

    return mapa_local




mapa_vinculos_global = ConstruirMapaVinculosGlobal()




def ObtenerTipoVista3DGlobal():

    try:

        colector_tipos = FilteredElementCollector(doc).OfClass(ViewFamilyType)

        for tipo in colector_tipos:

            if tipo.ViewFamily == ViewFamily.ThreeDimensional:

                return tipo

    except:

        pass

    return None




tipo_vista_3d_global = ObtenerTipoVista3DGlobal()




opciones_geometria_dac = Autodesk.Revit.DB.Options()

opciones_geometria_dac.ComputeReferences = False

opciones_geometria_dac.IncludeNonVisibleObjects = False

opciones_geometria_dac.DetailLevel = Autodesk.Revit.DB.ViewDetailLevel.Fine




def DesenvolverAlElementoNativo(elemento_entrada):

    if elemento_entrada is None:

        return None

    if isinstance(elemento_entrada, Autodesk.Revit.DB.Element):

        return elemento_entrada

    try:

        return elemento_entrada.InternalElement

    except:

        return None




def ObtenerInstanciaVinculo(elemento_nativo):

    if elemento_nativo is None:

        return None

    try:

        if elemento_nativo.Document == doc:

            return None

    except:

        return None

    try:

        titulo_doc = elemento_nativo.Document.Title

        return mapa_vinculos_global.get(titulo_doc, None)

    except:

        pass

    return None




def EsElementoEscalera(elemento_nativo):

    if elemento_nativo is None:

        return False

    try:

        categoria = elemento_nativo.Category

        if categoria is None:

            return False

        return categoria.Id.IntegerValue == int(BuiltInCategory.OST_Stairs)

    except:

        return False




def ExpandirElementoAComponentes(elemento_nativo):

    lst_componentes = []

    if elemento_nativo is None:

        return lst_componentes

    if isinstance(elemento_nativo, Autodesk.Revit.DB.Architecture.Stairs):

        try:

            doc_elemento = elemento_nativo.Document

            for id_run in elemento_nativo.GetStairsRuns():

                ele = doc_elemento.GetElement(id_run)

                if ele is not None:

                    lst_componentes.append(ele)

            for id_landing in elemento_nativo.GetStairsLandings():

                ele = doc_elemento.GetElement(id_landing)

                if ele is not None:

                    lst_componentes.append(ele)

            for id_support in elemento_nativo.GetStairsSupports():

                ele = doc_elemento.GetElement(id_support)

                if ele is not None:

                    lst_componentes.append(ele)

        except:

            pass

        if len(lst_componentes) == 0:

            lst_componentes.append(elemento_nativo)

    else:

        lst_componentes.append(elemento_nativo)

    return lst_componentes




def RecolectarSolidosDeContenedorGeometria(contenedor_geometria, lst_destino):

    if contenedor_geometria is None:

        return

    try:

        for objeto in contenedor_geometria:

            if objeto is None:

                continue

            if isinstance(objeto, Autodesk.Revit.DB.Solid):

                try:

                    if objeto.Volume > 0:

                        lst_destino.append(objeto)

                except:

                    continue

            elif isinstance(objeto, GeometryInstance):

                try:

                    RecolectarSolidosDeContenedorGeometria(objeto.GetInstanceGeometry(), lst_destino)

                except:

                    continue

    except:

        pass




def ExtraerSolidosRevitDelElemento(elemento_nativo):

    lst_solidos = []

    if elemento_nativo is None:

        return lst_solidos

    for componente in ExpandirElementoAComponentes(elemento_nativo):

        try:

            geometria = componente.get_Geometry(opciones_geometria_dac)

            if geometria is not None:

                RecolectarSolidosDeContenedorGeometria(geometria, lst_solidos)

        except:

            continue

    return lst_solidos




def FiltrarSolidosPorUmbralVolumen(lst_solidos_revit):

    lst_filtrados = []

    if lst_solidos_revit is None:

        return lst_filtrados

    for solido_iter in lst_solidos_revit:

        if solido_iter is None:

            continue

        try:

            volumen_m3 = solido_iter.Volume * _PIES3_A_METROS3

            if volumen_m3 >= umbral_volumen_m3:

                lst_filtrados.append(solido_iter)

        except:

            continue

    return lst_filtrados




def UnirSolidosRevitParcialmente(lst_solidos_revit):

    if lst_solidos_revit is None or len(lst_solidos_revit) == 0:

        return []

    if len(lst_solidos_revit) == 1:

        return [lst_solidos_revit[0]]

    lst_resultado = [lst_solidos_revit[0]]

    for indice in range(1, len(lst_solidos_revit)):

        solido_a_unir = lst_solidos_revit[indice]

        if solido_a_unir is None:

            continue

        unido_correctamente = False

        for indice_acumulado in range(len(lst_resultado)):

            try:

                resultado = BooleanOperationsUtils.ExecuteBooleanOperation(

                    lst_resultado[indice_acumulado], solido_a_unir, BooleanOperationsType.Union)

                if resultado is not None and resultado.Volume > 0:

                    lst_resultado[indice_acumulado] = resultado

                    unido_correctamente = True

                    break

            except:

                continue

        if not unido_correctamente:

            lst_resultado.append(solido_a_unir)

    return lst_resultado




def ConvertirSolidoRevitAProtoType(solido_revit):

    if solido_revit is None:

        return None

    try:

        if hasattr(solido_revit, 'IsValidObject') and not solido_revit.IsValidObject:

            return None

        solido_dynamo = solido_revit.ToProtoType()

        if solido_dynamo is None:

            return None

        return solido_dynamo

    except:

        return None




def ConvertirListaSolidosRevitAProtoType(lst_solidos_revit):

    lst_resultado = []

    todos_convirtieron = True

    for solido_revit_iter in lst_solidos_revit:

        solido_dynamo = ConvertirSolidoRevitAProtoType(solido_revit_iter)

        if solido_dynamo is not None:

            lst_resultado.append(solido_dynamo)

        else:

            todos_convirtieron = False

    return lst_resultado, todos_convirtieron




def UnirSolidosDynamoParcialmente(lst_solidos_dynamo):

    if lst_solidos_dynamo is None or len(lst_solidos_dynamo) == 0:

        return []

    if len(lst_solidos_dynamo) == 1:

        return [lst_solidos_dynamo[0]]

    lst_resultado = [lst_solidos_dynamo[0]]

    for indice in range(1, len(lst_solidos_dynamo)):

        solido_a_unir = lst_solidos_dynamo[indice]

        if solido_a_unir is None:

            continue

        unido_correctamente = False

        for indice_acumulado in range(len(lst_resultado)):

            try:

                resultado = Autodesk.DesignScript.Geometry.Solid.ByUnion(

                    [lst_resultado[indice_acumulado], solido_a_unir])

                if resultado is not None and resultado.Volume > 0:

                    lst_resultado[indice_acumulado] = resultado

                    unido_correctamente = True

                    break

            except:

                continue

        if not unido_correctamente:

            lst_resultado.append(solido_a_unir)

    return lst_resultado




def ExtraerSolidosDynamoDirecto(elemento_entrada, elemento_nativo):

    lst_solidos = []

    volumen_total_m3 = 0.0

    elemento_dynamo = None

    if elemento_entrada is not None and not isinstance(elemento_entrada, Autodesk.Revit.DB.Element):

        elemento_dynamo = elemento_entrada

    else:

        try:

            elemento_dynamo = elemento_nativo.ToDSType(True)

        except:

            elemento_dynamo = None

    if elemento_dynamo is None:

        return lst_solidos, volumen_total_m3

    try:

        geometria_dynamo = elemento_dynamo.Geometry()

    except:

        return lst_solidos, volumen_total_m3

    if geometria_dynamo is None:

        return lst_solidos, volumen_total_m3

    for objeto_geo in geometria_dynamo:

        try:

            if isinstance(objeto_geo, Autodesk.DesignScript.Geometry.Solid):

                volumen_solido = objeto_geo.Volume

                if volumen_solido >= umbral_volumen_m3:

                    lst_solidos.append(objeto_geo)

                    volumen_total_m3 = volumen_total_m3 + volumen_solido

        except:

            continue

    return lst_solidos, volumen_total_m3




def ObtenerBoundingBoxConTolerancia(elemento_nativo, tolerancia_inferior_metros, tolerancia_superior_metros):

    if elemento_nativo is None:

        return None

    try:

        bbox = elemento_nativo.get_BoundingBox(None)

        if bbox is None:

            return None

        punto_min = bbox.Min

        punto_max = bbox.Max

        instancia_vinculo = ObtenerInstanciaVinculo(elemento_nativo)

        if instancia_vinculo is not None:

            try:

                transform = instancia_vinculo.GetTransform()

                punto_min = transform.OfPoint(punto_min)

                punto_max = transform.OfPoint(punto_max)

            except:

                pass

        tol_inf_pies = tolerancia_inferior_metros * factor_metros_a_pies

        tol_sup_pies = tolerancia_superior_metros * factor_metros_a_pies

        punto_minimo_exp = XYZ(

            punto_min.X - tol_inf_pies,

            punto_min.Y - tol_inf_pies,

            punto_min.Z - tol_inf_pies)

        punto_maximo_exp = XYZ(

            punto_max.X + tol_sup_pies,

            punto_max.Y + tol_sup_pies,

            punto_max.Z + tol_sup_pies)

        nuevo_bbox = BoundingBoxXYZ()

        nuevo_bbox.Min = punto_minimo_exp

        nuevo_bbox.Max = punto_maximo_exp

        return nuevo_bbox

    except:

        return None




def CrearNuevaVista3D():

    try:

        tipo_3d = tipo_vista_3d_global

        if tipo_3d is None:

            return None

        nombre_base = "AM3D_vista3d_" + str(random.randint(1000, 9999))

        TransactionManager.Instance.EnsureInTransaction(doc)

        vista = View3D.CreateIsometric(doc, tipo_3d.Id)

        nombre_final = nombre_base

        contador = 1

        while True:

            try:

                vista.Name = nombre_final

                break

            except:

                nombre_final = nombre_base + "_" + str(contador)

                contador = contador + 1

                if contador > 100:

                    break

        TransactionManager.Instance.TransactionTaskDone()

        return vista

    except:

        try:

            TransactionManager.Instance.ForceCloseTransaction()

        except:

            pass

        return None




def CrearSectionBox(vista_3d, bbox_xyz):

    if vista_3d is None or bbox_xyz is None:

        return None

    if not isinstance(vista_3d, View3D):

        return None

    TransactionManager.Instance.EnsureInTransaction(doc)

    try:

        if not vista_3d.IsSectionBoxActive:

            vista_3d.IsSectionBoxActive = True

        vista_3d.SetSectionBox(bbox_xyz)

        TransactionManager.Instance.TransactionTaskDone()

        return vista_3d

    except:

        TransactionManager.Instance.ForceCloseTransaction()

        return None




def AislarElementoEnVista(elemento_nativo, vista_3d, resetear=True):

    if vista_3d is None or elemento_nativo is None:

        return None

    doc_vista = vista_3d.Document

    try:

        if not vista_3d.CanUseTemporaryVisibilityModes():

            return None

        if elemento_nativo.Document != doc_vista:

            return None

        lista_ids = List[ElementId]()

        lista_ids.Add(elemento_nativo.Id)

        TransactionManager.Instance.EnsureInTransaction(doc_vista)

        if resetear:

            try:

                vista_3d.DisableTemporaryViewMode(TemporaryViewMode.TemporaryHideIsolate)

            except:

                pass

        vista_3d.IsolateElementsTemporary(lista_ids)

        TransactionManager.Instance.TransactionTaskDone()

        return vista_3d

    except:

        try:

            TransactionManager.Instance.ForceCloseTransaction()

        except:

            pass

        return None




def ModificarVisualizacionVinculo(instancia_vinculo, vista_3d):

    if instancia_vinculo is None or vista_3d is None:

        return None

    if not isinstance(instancia_vinculo, RevitLinkInstance):

        return None

    doc_vista = vista_3d.Document

    try:

        if vista_3d.ViewTemplateId != ElementId.InvalidElementId:

            return vista_3d

        TransactionManager.Instance.EnsureInTransaction(doc_vista)

        try:

            overrides = vista_3d.GetLinkOverrides(instancia_vinculo.Id)

            overrides.SetLinkVisibilityType(LinkVisibility.ByHostView)

            vista_3d.SetLinkOverrides(instancia_vinculo.Id, overrides)

        except:

            pass

        TransactionManager.Instance.TransactionTaskDone()

        return vista_3d

    except:

        try:

            TransactionManager.Instance.ForceCloseTransaction()

        except:

            pass

        return None




def AislarCategoriaEnVista(elemento_nativo, vista_3d):

    if elemento_nativo is None or vista_3d is None:

        return None

    doc_vista = vista_3d.Document

    try:

        categoria = elemento_nativo.Category

        if categoria is None:

            return None

        id_categoria = categoria.Id

        es_vinculo = elemento_nativo.Document != doc_vista

        TransactionManager.Instance.EnsureInTransaction(doc_vista)

        for cat in doc_vista.Settings.Categories:

            try:

                id_cat = cat.Id

                if es_vinculo:

                    debe_ocultar = (id_cat != id_categoria and id_cat.IntegerValue != _ID_LINKS)

                    vista_3d.SetCategoryHidden(id_cat, debe_ocultar)

                else:

                    vista_3d.SetCategoryHidden(id_cat, id_cat != id_categoria)

            except:

                pass

        TransactionManager.Instance.TransactionTaskDone()

        return vista_3d

    except:

        try:

            TransactionManager.Instance.ForceCloseTransaction()

        except:

            pass

        return None




def LeerVolumenComputadoRevit(elemento_nativo):

    if elemento_nativo is None:

        return 0

    try:

        param = elemento_nativo.get_Parameter(BuiltInParameter.HOST_VOLUME_COMPUTED)

        if param is None:

            return 0

        volumen_pies3 = param.AsDouble()

        if volumen_pies3 <= 0:

            return 0

        return round(volumen_pies3 * _PIES3_A_METROS3, 5)

    except:

        return 0




def CalcularVolumenEscaleraPorSubcomponentes(elemento_nativo):

    volumen_pies3 = 0.0

    try:

        lst_solidos = ExtraerSolidosRevitDelElemento(elemento_nativo)

        for solido in lst_solidos:

            try:

                volumen_pies3 = volumen_pies3 + solido.Volume

            except:

                continue

    except:

        pass

    return volumen_pies3 * _PIES3_A_METROS3




def ExtraerSolidosViaSAT(elemento_nativo, es_escalera, id_texto_elemento):

    if elemento_nativo is None:

        return []

    bbx = ObtenerBoundingBoxConTolerancia(elemento_nativo, 0, 0)

    if bbx is None:

        return []

    es_vinculo_local = False

    try:

        es_vinculo_local = elemento_nativo.Document != doc

    except:

        es_vinculo_local = False

    volumen_elemento_m3 = 0

    if not es_escalera:

        volumen_elemento_m3 = LeerVolumenComputadoRevit(elemento_nativo)

        if volumen_elemento_m3 == 0:

            return []

    else:

        volumen_escalera_m3 = CalcularVolumenEscaleraPorSubcomponentes(elemento_nativo)

        if volumen_escalera_m3 == 0:

            return []

    vista = None

    vista_copia = None

    geometria_lista = []

    try:

        if es_vinculo_local:

            instancia_vinculo = ObtenerInstanciaVinculo(elemento_nativo)

            if instancia_vinculo is None:

                return []

            vista = CrearNuevaVista3D()

            if vista is None:

                return []

            vista = CrearSectionBox(vista, bbx)

            vista = AislarElementoEnVista(instancia_vinculo, vista)

            vista = ModificarVisualizacionVinculo(instancia_vinculo, vista)

            vista = AislarCategoriaEnVista(elemento_nativo, vista)

        else:

            vista = CrearNuevaVista3D()

            if vista is None:

                return []

            vista = CrearSectionBox(vista, bbx)

            vista = AislarElementoEnVista(elemento_nativo, vista)

        if vista is None:

            return []

        TransactionManager.Instance.EnsureInTransaction(doc)

        vista_copia_id = vista.Duplicate(ViewDuplicateOption.Duplicate)

        doc.Regenerate()

        vista_copia = doc.GetElement(vista_copia_id)

        if vista_copia is None:

            TransactionManager.Instance.TransactionTaskDone()

            return []

        try:

            vista_copia.Name = "AM3D_SAT_" + id_texto_elemento + "_" + str(random.randint(1000, 9999))

        except:

            pass

        TransactionManager.Instance.TransactionTaskDone()

        TransactionManager.Instance.EnsureInTransaction(doc)

        try:

            vista_copia.DisableTemporaryViewMode(TemporaryViewMode.TemporaryHideIsolate)

        except:

            pass

        doc.Regenerate()

        TransactionManager.Instance.TransactionTaskDone()

        ruta = System.IO.Path.GetTempPath()

        timestamp_unico = str(int(time.perf_counter() * 1000000)) + "_" + str(random.randint(1000, 9999))

        nombre = "temp_sat_" + id_texto_elemento + "_" + timestamp_unico + ".sat"

        ruta_completa = System.IO.Path.Combine(ruta, nombre)

        doc.Export(ruta, nombre, List[ElementId]([vista_copia.Id]), SATExportOptions())

        geometria_raw = Autodesk.DesignScript.Geometry.Geometry.ImportFromSAT(ruta_completa)

        for g in geometria_raw:

            geometria_lista.append(g)

    except:

        geometria_lista = []

    finally:

        TransactionManager.Instance.EnsureInTransaction(doc)

        try:

            if vista_copia is not None:

                doc.Delete(vista_copia.Id)

            if vista is not None:

                doc.Delete(vista.Id)

        except:

            pass

        TransactionManager.Instance.TransactionTaskDone()

    solidos = []

    for g in geometria_lista:

        try:

            if isinstance(g, Autodesk.DesignScript.Geometry.Solid):

                if g.Volume > 0:

                    solidos.append(g)

        except:

            pass

    if len(solidos) == 0:

        return []

    if es_escalera:

        if len(solidos) == 1:

            return [solidos[0]]

        try:

            resultado_union = Autodesk.DesignScript.Geometry.Solid.ByUnion(solidos)

        except:

            return []

        if isinstance(resultado_union, Autodesk.DesignScript.Geometry.Solid):

            return [resultado_union]

        try:

            solido_mayor = None

            volumen_mayor = -1

            for s in resultado_union:

                try:

                    v = s.Volume

                except:

                    v = 0

                if v > volumen_mayor:

                    volumen_mayor = v

                    solido_mayor = s

            if solido_mayor is not None:

                return [solido_mayor]

        except:

            pass

        return []

    solido_mas_cercano = None

    diferencia_minima = float("inf")

    for s in solidos:

        try:

            v = round(s.Volume, 5)

        except:

            v = 0

        dif = abs(volumen_elemento_m3 - v)

        if dif < diferencia_minima:

            diferencia_minima = dif

            solido_mas_cercano = s

    if solido_mas_cercano is None:

        return []

    return [solido_mas_cercano]




def ProcesarEscaleraAPI(elemento_nativo, lst_solidos_revit, id_texto_elemento):

    cantidad = len(lst_solidos_revit)

    if cantidad == 1:

        solido_dynamo = ConvertirSolidoRevitAProtoType(lst_solidos_revit[0])

        if solido_dynamo is not None:

            return [solido_dynamo]

        return None

    lst_unidos_revit = UnirSolidosRevitParcialmente(lst_solidos_revit)

    if len(lst_unidos_revit) == 1:

        solido_dynamo = ConvertirSolidoRevitAProtoType(lst_unidos_revit[0])

        if solido_dynamo is not None:

            return [solido_dynamo]

        return None

    lst_dynamo, todos_ok = ConvertirListaSolidosRevitAProtoType(lst_unidos_revit)

    if todos_ok:

        return UnirSolidosDynamoParcialmente(lst_dynamo)

    return None




def ProcesarNoEscaleraAPI(lst_solidos_revit):

    lst_dynamo, todos_ok = ConvertirListaSolidosRevitAProtoType(lst_solidos_revit)

    if todos_ok:

        return lst_dynamo

    return None




def ObtenerSolidosDelElemento(elemento_entrada):

    elemento_nativo = DesenvolverAlElementoNativo(elemento_entrada)

    if elemento_nativo is None:

        return []

    try:

        id_texto_elemento = str(elemento_nativo.Id.IntegerValue)

    except:

        id_texto_elemento = "Sin_Id"

    es_escalera = EsElementoEscalera(elemento_nativo)




    volumen_referencia_m3 = LeerVolumenComputadoRevit(elemento_nativo)




    lst_solidos_dynamo, volumen_dynamo_m3 = ExtraerSolidosDynamoDirecto(elemento_entrada, elemento_nativo)

    if len(lst_solidos_dynamo) > 0:

        flujo1_aceptable = True

        if volumen_referencia_m3 > 0:

            if volumen_dynamo_m3 < (volumen_referencia_m3 * 0.90):

                flujo1_aceptable = False

        if flujo1_aceptable:

            if es_escalera:

                return UnirSolidosDynamoParcialmente(lst_solidos_dynamo)

            return lst_solidos_dynamo




    lst_solidos_revit = ExtraerSolidosRevitDelElemento(elemento_nativo)

    lst_solidos_revit = FiltrarSolidosPorUmbralVolumen(lst_solidos_revit)

    if len(lst_solidos_revit) == 0:

        return []

    if es_escalera:

        resultado = ProcesarEscaleraAPI(elemento_nativo, lst_solidos_revit, id_texto_elemento)

    else:

        resultado = ProcesarNoEscaleraAPI(lst_solidos_revit)




    if resultado is None:

        if es_escalera:

            return ExtraerSolidosViaSAT(elemento_nativo, True, id_texto_elemento)

        return ExtraerSolidosViaSAT(elemento_nativo, False, id_texto_elemento)

    return resultado




try:

    valor_entrada = IN[0]

except:

    valor_entrada = None




es_lista = False

if valor_entrada is not None:

    if isinstance(valor_entrada, Autodesk.Revit.DB.Element):

        es_lista = False

    elif hasattr(valor_entrada, "InternalElement") and valor_entrada.InternalElement is not None:

        es_lista = False

    else:

        try:

            iter(valor_entrada)

            es_lista = not isinstance(valor_entrada, str)

        except:

            es_lista = False




if es_lista:

    lst_salida = []

    for elemento_iter in valor_entrada:

        lst_salida.append(ObtenerSolidosDelElemento(elemento_iter))

    OUT = lst_salida

else:

    OUT = ObtenerSolidosDelElemento(valor_entrada)