BLOG Post

Revit Macro – Dividir Muros Multicapa

WB Lab

Tipo: Macro – Revit API

Versión: Revit 2020

Para usar dicha macro dispone, principalmente, de tres opciones:

  1. Utilizar el archivo .rvt facilitado: WB-DividirMurosMulticapa.rvt.
  2. Con Revit cerrado, extraer el contenido del .zip facilitado (editGeometry.zip) en la siguiente ruta: C:\ProgramData\Autodesk\Revit\Macros\2020\Revit\AppHookup.
  3. En un proyecto existente, crear un nuevo módulo de macros en C#, nombrarlo como “editGeometry” y pegar el código que se adjunta.

Por defecto, las macros de Revit se encuentran disponibles en Gestionar -> Macros -> Administrador de macros.

Esta macro le permitirá dividir muros multicapas en muros independientes conservando todas las propiedades del muro original*.

La macro muestra, a modo de ejemplo, como seleccionando un muro multicapa y se crean nuevos muros independientes a partir de cada una de las capas que contiene el muro. Para el ejemplo, se nombran los nuevos tipos de muro con una concatenación del nombre del material y el espesor de la capa.

El funcionamiento de la macro es muy sencillo. Selecciona un muro existente, adquiere todas las propiedades de sus capas individualmente, crea un nuevo muro en la misma posición del muro original y elimina éste.

Explicación

La estructura de la macro se divide en tres bloques muy sencillos:

Bloque 1: Selección de elemento y sus propiedades.

Se obtienen características necesarias para posteriormente crear los nuevos tipos de muro.

Para crear el/los nuevos tipos de muro se usará el método crear que necesita la siguiente información:

Create Method (Document, Curve, ElementId, ElementId, Double, Double, Boolean, Boolean)  

Mediante el siguiente extracto se declaran las variables necesarias y se obtienen las características necesarias para hacer funcionar correctamente el método mencionado.

Extracto:
            FilteredElementCollector levels = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).WhereElementIsNotElementType(); ICollection levelElem = levels.ToElementIds(); ElementId levelId = levelElem.First() as ElementId; ICollection selIds = uiDoc.Selection.GetElementIds(); IList refs = uiDoc.Selection.PickObjects(ObjectType.Element,"Seleccione los muros que desea dividir"); foreach (Reference refe in refs) { Wall muro = doc.GetElement(refe) as Wall; List ids = new List(refs.Select(r => r.ElementId)); List muroList = new List(); muroList.Add(muro); foreach (Wall muroNuevo in muroList) { double alturaMuro = muroNuevo.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble(); double espesor = 0; double capaAnt = 0; double offsetMuro = 0; bool flipMuro = false; bool strMuro = false; double medioEspesor; double capas; XYZ lineaInicio, lineaFin, v, w, a, b; List listaMuros = new List(); int counter = 0; LocationCurve muroLoc = muroNuevo.Location as LocationCurve; Curve muroLinea = muroLoc.Curve; lineaInicio = muroLoc.Curve.GetEndPoint( 0 ); lineaFin = muroLoc.Curve.GetEndPoint( 1 ); medioEspesor = 0.5 * muroNuevo.WallType.Width; v = lineaFin - lineaInicio; v = v.Normalize(); w = XYZ.BasisZ.CrossProduct( v ).Normalize(); a = lineaInicio + medioEspesor * w; b = lineaFin + medioEspesor * w; Line lineaExterior = Line.CreateBound(a, b); XYZ pInicio, pFinal; pInicio = lineaExterior.GetEndPoint(0); pFinal = lineaExterior.GetEndPoint(1); XYZ muroNormal = lineaExterior.Direction.Normalize(); XYZ muroDir = new XYZ(0, 0, 1); XYZ cross = muroNormal.CrossProduct(muroDir); CompoundStructure estructura = muroNuevo.WallType.GetCompoundStructure(); };

Bloque 2: Gestión de capas y nueva estructura de muro.

Por cada capa que contenga el muro seleccionado, se crea un tipo de muro nuevo con las características de dicha capa (material y espesor). Además, se debe obtener la nueva ubicación de los nuevos muros.

Existen múltiples maneras de realizar este proceso. Para este ejemplo se ha optado por obtener la línea de ubicación exterior del muro original e ir obteniendo el eje de cada una de las capas mediante una iteración de su CompoundStructureLayer. Dicho eje, será la nueva ubicación de cada tipo de muro creado.

Extracto:
foreach( CompoundStructureLayer layer in estructura.GetLayers()) { capas = estructura.GetLayerWidth(counter); if (layer.LayerId <= 0) { capaAnt += (estructura.GetLayerWidth(0))/2; espesor += estructura.GetLayerWidth(0); } else { if (layer.LayerId == 1) { capaAnt += capaAnt + (estructura.GetLayerWidth(counter))/2; } else { capaAnt += (estructura.GetLayerWidth(counter-1))/2+ (estructura.GetLayerWidth(counter))/2; }; }; XYZ normal = XYZ.BasisZ.CrossProduct(muroNormal); XYZ desplaza = normal.Multiply(-capaAnt); XYZ ptoInicio = lineaExterior.GetEndPoint(0).Add(desplaza); XYZ ptoFinal = lineaExterior.GetEndPoint(1).Add(desplaza); Line lineaParalela = Line.CreateBound(ptoInicio, ptoFinal); };

Bloque 3: Creación de nuevos tipos y elementos.

Una vez obtenidas todas las características necesarias para la creación de los nuevos tipos de muro, se ejecuta la transacción que crea; primero un nuevo tipo de muro en el proyecto y después el elemento en su nueva ubicación.

Extracto:
  Transaction transaction = new Transaction(doc, "Crear muro"); { transaction.Start(); ElementId tipoMuroId = layer.MaterialId; string layerName = doc.GetElement(tipoMuroId).Name; double layerWidth = layer.Width; ElementId layerMaterial = layer.MaterialId; string muroNombre = layerName+"_"+(layerWidth *304.800).ToString()+"mm"; WallType nuevoTipo = null; WallType tipoMuro = muroNuevo.WallType; FilteredElementCollector col = new FilteredElementCollector(doc).OfClass(typeof(WallType)); IEnumerable wName = col.Cast().Select (e => e.Name ); foreach (WallType wt in col) { if(WallKind.Basic == wt.Kind) { if(wName.Contains(muroNombre)) { Element ele = new FilteredElementCollector(doc).WhereElementIsElementType().ToElements().Where(o => o.Name == muroNombre).First(); nuevoTipo = ele as WallType; break; } else { nuevoTipo = tipoMuro.Duplicate((layerName+"_"+(layerWidth *304.800).ToString()+"mm")) as WallType; listaMuros.Add(nuevoTipo.Name); break; } } } List layerList = new List(); layerList.Add(new CompoundStructureLayer(layer.Width, MaterialFunctionAssignment.Structure, layerMaterial)); CompoundStructure compStructure = nuevoTipo.GetCompoundStructure(); compStructure.SetLayers(layerList); nuevoTipo.SetCompoundStructure(compStructure); Wall muroNew = Wall.Create(doc, lineaParalela, nuevoTipo.Id, levelId,alturaMuro,offsetMuro,flipMuro,strMuro); (muroNew.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM)).Set(alturaMuro); transaction.Commit();

EXTRA:

Los nuevos tipos de muro se crearán en la misma ubicación que ahora tienen las capas del muro compuesto, con lo cual, por cada muro independiente que se cree, Revit lanzará una ventana emergente de avisando de la existencia de elementos solapados.

Para evitar el lanzamiento de esa ventana emergente, se procede a gestionar dicho proceso mediante el siguiente método:

  void omisionAvisos (object sender, Autodesk.Revit.DB.Events.FailuresProcessingEventArgs e) { FailuresAccessor fa = e.GetFailuresAccessor(); IList failList = new List(); failList = fa.GetFailureMessages(); foreach (FailureMessageAccessor failure in failList) { fa.DeleteWarning(failure); } }

Dicho método debe de integrarse dentro de la solución de la siguiente manera:

  doc.Application.FailuresProcessing += omisionAvisos; { //Código que provoque un aviso con ventana emergente. } doc.Application.FailuresProcessing -= omisionAvisos;

Mediante la integración de los tres bloques principales, se obtiene una macro que permite la división de muros multicapa en muros independientes mediante nuevos tipos de muro que conserven, en esencia, las propiedades el muro original.

Código completo

  /* * Created by SharpDevelop. * User: Jose Valverde [Wise Build] (www.wisebuild.es) */ using System; using Autodesk.Revit.UI; using Autodesk.Revit.DB; using Autodesk.Revit.UI.Selection; using System.Collections.Generic; using System.Linq; namespace editGeometry { [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)] [Autodesk.Revit.DB.Macros.AddInId("21AE7E06-094F-4E34-A17B-E958DD812E9F")] public partial class ThisDocument { private void Module_Startup(object sender, EventArgs e) { } private void Module_Shutdown(object sender, EventArgs e) { } #region Revit Macros generated code private void InternalStartup() { this.Startup += new System.EventHandler(Module_Startup); this.Shutdown += new System.EventHandler(Module_Shutdown); } #endregion public void dividirMuros() { UIDocument uiDoc = this.Application.ActiveUIDocument; Document doc = uiDoc.Document; FilteredElementCollector levels = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).WhereElementIsNotElementType(); ICollection levelElem = levels.ToElementIds(); ElementId levelId = levelElem.First() as ElementId; ICollection selIds = uiDoc.Selection.GetElementIds(); IList refs = uiDoc.Selection.PickObjects(ObjectType.Element,"Seleccione los muros que desea dividir"); foreach (Reference refe in refs) { Wall muro = doc.GetElement(refe) as Wall; List ids = new List(refs.Select(r => r.ElementId)); List muroList = new List(); muroList.Add(muro); foreach (Wall muroNuevo in muroList) { double alturaMuro = muroNuevo.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble(); double espesor = 0; double capaAnt = 0; double offsetMuro = 0; bool flipMuro = false; bool strMuro = false; double medioEspesor; double capas; XYZ lineaInicio, lineaFin, v, w, a, b; List listaMuros = new List(); int counter = 0; LocationCurve muroLoc = muroNuevo.Location as LocationCurve; Curve muroLinea = muroLoc.Curve; lineaInicio = muroLoc.Curve.GetEndPoint( 0 ); lineaFin = muroLoc.Curve.GetEndPoint( 1 ); medioEspesor = 0.5 * muroNuevo.WallType.Width; v = lineaFin - lineaInicio; v = v.Normalize(); w = XYZ.BasisZ.CrossProduct( v ).Normalize(); a = lineaInicio + medioEspesor * w; b = lineaFin + medioEspesor * w; Line lineaExterior = Line.CreateBound(a, b); XYZ pInicio, pFinal; pInicio = lineaExterior.GetEndPoint(0); pFinal = lineaExterior.GetEndPoint(1); XYZ muroNormal = lineaExterior.Direction.Normalize(); XYZ muroDir = new XYZ(0, 0, 1); XYZ cross = muroNormal.CrossProduct(muroDir); CompoundStructure estructura = muroNuevo.WallType.GetCompoundStructure(); foreach( CompoundStructureLayer layer in estructura.GetLayers()) { capas = estructura.GetLayerWidth(counter); if (layer.LayerId <= 0) { capaAnt += (estructura.GetLayerWidth(0))/2; espesor += estructura.GetLayerWidth(0); } else { if (layer.LayerId == 1) { capaAnt += capaAnt + (estructura.GetLayerWidth(counter))/2; } else { capaAnt += (estructura.GetLayerWidth(counter-1))/2+ (estructura.GetLayerWidth(counter))/2; }; }; XYZ normal = XYZ.BasisZ.CrossProduct(muroNormal); XYZ desplaza = normal.Multiply(-capaAnt); XYZ ptoInicio = lineaExterior.GetEndPoint(0).Add(desplaza); XYZ ptoFinal = lineaExterior.GetEndPoint(1).Add(desplaza); Line lineaParalela = Line.CreateBound(ptoInicio, ptoFinal); doc.Application.FailuresProcessing += omisionAvisos; Transaction transaction = new Transaction(doc, "Crear muro"); { transaction.Start(); ElementId tipoMuroId = layer.MaterialId; string layerName = doc.GetElement(tipoMuroId).Name; double layerWidth = layer.Width; ElementId layerMaterial = layer.MaterialId; string muroNombre = layerName+"_"+(layerWidth *304.800).ToString()+"mm"; WallType nuevoTipo = null; WallType tipoMuro = muroNuevo.WallType; FilteredElementCollector col = new FilteredElementCollector(doc).OfClass(typeof(WallType)); IEnumerable wName = col.Cast().Select (e => e.Name ); foreach (WallType wt in col) { if(WallKind.Basic == wt.Kind) { if(wName.Contains(muroNombre)) { Element ele = new FilteredElementCollector(doc).WhereElementIsElementType().ToElements().Where(o => o.Name == muroNombre).First(); nuevoTipo = ele as WallType; break; } else { nuevoTipo = tipoMuro.Duplicate((layerName+"_"+(layerWidth *304.800).ToString()+"mm")) as WallType; listaMuros.Add(nuevoTipo.Name); break; } } } List layerList = new List(); layerList.Add(new CompoundStructureLayer(layer.Width, MaterialFunctionAssignment.Structure, layerMaterial)); CompoundStructure compStructure = nuevoTipo.GetCompoundStructure(); compStructure.SetLayers(layerList); nuevoTipo.SetCompoundStructure(compStructure); Wall muroNew = Wall.Create(doc, lineaParalela, nuevoTipo.Id, levelId,alturaMuro,offsetMuro,flipMuro,strMuro); (muroNew.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM)).Set(alturaMuro); transaction.Commit(); doc.Application.FailuresProcessing -= omisionAvisos; } counter += 1; } } Transaction trDelete = new Transaction(doc, "Eliminar muro original"); { trDelete.Start(); ICollection deletedIdSet = doc.Delete(muro.Id); trDelete.Commit(); } } } void omisionAvisos (object sender, Autodesk.Revit.DB.Events.FailuresProcessingEventArgs e) { FailuresAccessor fa = e.GetFailuresAccessor(); IList failList = new List(); failList = fa.GetFailureMessages(); foreach (FailureMessageAccessor failure in failList) { fa.DeleteWarning(failure); } } } }


 

Archivos

Notas

No se considera para el presente ejemplo la existencia de membranas en los muros (espesor = 0).

* En la macro que se facilita, se tratan algunas de las propiedades, pero no se han considerado todas las opciones posibles. El código facilitado NO pretende ser una solución completa; se trata de un ejemplo de uno de los posibles caminos para conseguir el objetivo propuesto.

La macro permite dividir muros multicapas y crear muros independientes a partir de cada una de sus capas.

linkedin de
artículos relacionados

Continúa leyendo