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.
To use it, create a new module and name it "GeometryCleaner", then replace all the text with the following content. Press F8 and make sure Build Completed Successfully is shown in the lower left corner of your screen.
/*
 * 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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit exceeded. Please complete the captcha once again.

  • Before submitting your inquiry, take a look at the basic information on data protection here.

    Modelical.com informs you that the personal data you provide will be processed by MODELICAL CONSULTORIA S.L. as the party responsible for this website.

    Purpose of the collection and processing of personal data: To send the information that the user requires through the website. - Legitimation: Consent of the interested party. - Recipients: Hosting: Gigas, 100% Spanish and 100% secure hosting. - Rights: You may exercise your rights of access, rectification, limitation and deletion of unsubscribe@modelical.com data as well as the right to lodge a complaint with a supervisory authority.