Split Multibody floors and roofs
Revit Macro
Description
This macro will help you clean Revit models where the user has created multibody floors and ceilings as these can pose challenges for proper quantity takeoff. The macro has three methods:
- SelectMultibodyFloors: selects floors visible in the view with more than one body. Please note that floors completely split by an opening will be selected also.
- SelectMultibodyCeilings: selects ceilings visible in the view with more than one body. Please note that ceilings completely split by an opening will be selected also. Please note that the Revit API (2019) does not expose methods for creating ceilings so we are limited to selecting them.
- SplitMultibodyFloors: selects and splits floors visible in the view with more than one body. Please note that floors cut or split by openings will be recreated with the visible shape.
/*
* Created by SharpDevelop.
* User: Roberto Molinos [Modelical]
* Date: 3/20/2015
* Time: 1:42 AM
*/
using System;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB.Macros;
namespace GeometryCleaner
{
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.DB.Macros.AddInId("6ACE9B39-339F-4D56-BC11-9D6973B54D94")]
public partial class ThisApplication
{
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
const double _eps = 1.0e-9;
public void SelectMultibodyFloors()
{
Document document = this.ActiveUIDocument.Document;
UIDocument uidoc = new UIDocument (document);
List<ElementId> ids = new List<ElementId>();
List<ElementId> badIds = new List<ElementId>();
List<int> cs = new List<int>();
FilteredElementCollector collector = new FilteredElementCollector(document,this.ActiveUIDocument.ActiveView.Id);
ICollection<Element> elems = collector.WherePasses(new ElementCategoryFilter(BuiltInCategory.OST_Floors)).WhereElementIsNotElementType().ToElements();
Autodesk.Revit.DB.Options geomOption = document.Application.Create.NewGeometryOptions();
Transaction tempTransaction = new Transaction(document, "Temp geometry extraction");
tempTransaction.Start();
foreach (Element e in elems)
{
if (e is Floor)
{
Floor f = e as Floor;
if (!PartUtils.HasAssociatedParts(document, f.Id))
{
FloorType fType = f.FloorType;
CompoundStructure comStruct = fType.GetCompoundStructure();
int c = 0;
for (int j = 0; j < comStruct.LayerCount; j++)
{
IList<CompoundStructureLayer> cslList = comStruct.GetLayers();
bool countLayer = true;
if(comStruct.GetLayerFunction(j) == MaterialFunctionAssignment.Membrane)
{
countLayer = false;
}
if(comStruct.GetLayerFunction(j) == MaterialFunctionAssignment.StructuralDeck)
{
if(comStruct.GetDeckEmbeddingType(j) != StructDeckEmbeddingType.Standalone)
{
countLayer = false;
}
}
if (countLayer)
{
c++;
}
}
cs.Add(c);
ids.Add(f.Id);
}
}
}
PartUtils.CreateParts(document,ids);
document.Regenerate();
int i = 0;
foreach (ElementId fid in ids)
{
ICollection<ElementId> partIds = PartUtils.GetAssociatedParts(document,fid,false,false);
if (cs[i] != partIds.Count)
{
badIds.Add(fid);
//TaskDialog.Show("Warning", "Multibody Object Found");
}
i++;
}
tempTransaction.RollBack();
using (Transaction transaction = new Transaction(document, "Select Multibody Floors"))
{
if(transaction.Start() == TransactionStatus.Started)
{
uidoc.Selection.SetElementIds(badIds);
if(transaction.Commit() == TransactionStatus.Committed)
{
TaskDialog.Show("Selected " , badIds.Count.ToString() + " floor(s) with multiple bodies.");
}
else
{
transaction.RollBack();
}
}
}
}
public void SelectMultibodyCeilings()
{
Document document = this.ActiveUIDocument.Document;
UIDocument uidoc = new UIDocument (document);
List<ElementId> ids = new List<ElementId>();
List<ElementId> badIds = new List<ElementId>();
List<int> cs = new List<int>();
ElementFilter ff = new ElementCategoryFilter(BuiltInCategory.OST_Ceilings);
FilteredElementCollector collector = new FilteredElementCollector(document,this.ActiveUIDocument.ActiveView.Id);
ICollection<Element> elems = collector.WherePasses(ff).WhereElementIsNotElementType().ToElements();
Autodesk.Revit.DB.Options geomOption = document.Application.Create.NewGeometryOptions();
Transaction tempTransaction = new Transaction(document, "Temp geometry extraction");
tempTransaction.Start();
foreach (Element e in elems)
{
if (e is Ceiling)
{
Ceiling f = e as Ceiling;
if (!PartUtils.HasAssociatedParts(document, f.Id))
{
ElementId fTypeId = f.GetTypeId();
CeilingType fType = document.GetElement(fTypeId) as CeilingType;
CompoundStructure comStruct = fType.GetCompoundStructure();
int c = 0;
for (int j = 0; j < comStruct.LayerCount; j++)
{
IList<CompoundStructureLayer> cslList = comStruct.GetLayers();
bool countLayer = true;
if(comStruct.GetLayerFunction(j) == MaterialFunctionAssignment.Membrane)
{
countLayer = false;
}
if(comStruct.GetLayerFunction(j) == MaterialFunctionAssignment.StructuralDeck)
{
if(comStruct.GetDeckEmbeddingType(j) != StructDeckEmbeddingType.Standalone)
{
countLayer = false;
}
}
if (countLayer)
{
c++;
}
}
cs.Add(c);
ids.Add(f.Id);
}
}
}
PartUtils.CreateParts(document,ids);
document.Regenerate();
int i = 0;
foreach (ElementId fid in ids)
{
ICollection<ElementId> partIds = PartUtils.GetAssociatedParts(document,fid,false,false);
if (cs[i] != partIds.Count)
{
badIds.Add(fid);
//TaskDialog.Show("Warning", "Multibody Object Found");
}
i++;
}
tempTransaction.RollBack();
using (Transaction transaction = new Transaction(document, "Select Multibody Ceilings"))
{
if(transaction.Start() == TransactionStatus.Started)
{
uidoc.Selection.SetElementIds(badIds);
if(transaction.Commit() == TransactionStatus.Committed)
{
TaskDialog.Show("Selected " , badIds.Count.ToString() + " ceiling(s) with multiple bodies.");
}
else
{
transaction.RollBack();
}
}
}
}
public void SplitDisjointFloors()
{
Document document = this.ActiveUIDocument.Document;
UIDocument uidoc = new UIDocument (document);
List<ElementId> ids = new List<ElementId>();
List<ElementId> badIds = new List<ElementId>();
List<int> cs = new List<int>();
ElementFilter ff = new ElementCategoryFilter(BuiltInCategory.OST_Floors);
FilteredElementCollector collector = new FilteredElementCollector(document,this.ActiveUIDocument.ActiveView.Id);
ICollection<Element> elems = collector.WherePasses(ff).WhereElementIsNotElementType().ToElements();
Autodesk.Revit.DB.Options geomOption = document.Application.Create.NewGeometryOptions();
Transaction tempTransaction = new Transaction(document, "Temp geometry extraction");
tempTransaction.Start();
foreach (Element e in elems)
{
Floor f = e as Floor;
if (f != null)
{
if (!PartUtils.HasAssociatedParts(document, f.Id))
{
FloorType fType = f.FloorType;
CompoundStructure comStruct = fType.GetCompoundStructure();
int c = 0;
for (int j = 0; j < comStruct.LayerCount; j++)
{
IList<CompoundStructureLayer> cslList = comStruct.GetLayers();
bool countLayer = true;
if(comStruct.GetLayerFunction(j) == MaterialFunctionAssignment.Membrane)
{
countLayer = false;
}
if(comStruct.GetLayerFunction(j) == MaterialFunctionAssignment.StructuralDeck)
{
if(comStruct.GetDeckEmbeddingType(j) != StructDeckEmbeddingType.Standalone)
{
countLayer = false;
}
}
if (countLayer)
{
c++;
}
}
cs.Add(c);
ids.Add(f.Id);
}
}
}
PartUtils.CreateParts(document,ids);
document.Regenerate();
int i = 0;
foreach (ElementId fid in ids)
{
ICollection<ElementId> partIds = PartUtils.GetAssociatedParts(document,fid,false,false);
if (cs[i] != partIds.Count)
{
badIds.Add(fid);
//TaskDialog.Show("Warning", "Multibody Object Found");
}
i++;
}
tempTransaction.RollBack();
using (Transaction transaction = new Transaction(document, "Split Multibody Floors"))
{
if(transaction.Start() == TransactionStatus.Started)
{
//uidoc.Selection.SetElementIds(badIds);
List<Floor> badFloors = new List<Floor>();
foreach (ElementId badId in badIds)
{
Floor sourceFloor = document.GetElement(badId) as Floor;
if (sourceFloor == null) continue;
//int c = 0;
Autodesk.Revit.DB.GeometryElement floorGeometryElement = sourceFloor.get_Geometry( geomOption );
foreach( Autodesk.Revit.DB.GeometryObject geometryObject in floorGeometryElement )
{
var floorSolid = geometryObject as Solid;
if( floorSolid == null )
continue;
var topFace = GetTopFace( floorSolid );
if( topFace == null )
throw new NotSupportedException(
"Floor does not have top face" );
if( topFace.EdgeLoops.IsEmpty )
throw new NotSupportedException(
"Floor top face does not have edges" );
EdgeArrayArray eaa = topFace.EdgeLoops;
foreach (EdgeArray ea in eaa)
{
CurveArray floorCurveArray = GetCurveArrayFromEdgeArary( ea );
document.Create.NewFloor(floorCurveArray,sourceFloor.FloorType,document.GetElement(sourceFloor.LevelId) as Level,false);
}
break;
// Create new floor using source
// floor outer boundaries
}
document.Delete(badId);
}
if(transaction.Commit() == TransactionStatus.Committed)
{
TaskDialog.Show("Split " , badIds.Count.ToString() + " floors with several bodies.");
}
else
{
transaction.RollBack();
}
}
}
}
private CurveArray GetCurveArrayFromEdgeArary(EdgeArray edgeArray )
{
CurveArray curveArray =
new CurveArray();
foreach( Edge edge in edgeArray )
{
var edgeCurve =
edge.AsCurve();
curveArray.Append( edgeCurve );
}
return curveArray;
}
private PlanarFace GetTopFace( Solid solid )
{
PlanarFace topFace = null;
FaceArray faces = solid.Faces;
foreach( Face f in faces )
{
PlanarFace pf = f as PlanarFace;
if( null != pf
&& ( Math.Abs( pf.Normal.X - 0 ) < _eps && Math.Abs( pf.Normal.Y - 0 ) < _eps ) )
{
if( ( null == topFace )
|| ( topFace.Origin.Z < pf.Origin.Z ) )
{
topFace = pf;
}
}
}
return topFace;
}
}
}
Here you can download the .cs file
Thanks to Jeremy Tammik and Spiderinnet for their valuable examples.
Platform
Revit.
Type
Revit API Macro.