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)