Insulation Tag

I am not very fond of duct and pipe insulations in Revit. If the concept is very useful, I find difficult to add and edit them. Furthermore, seeing the insulation on every duct and pipe can quickly clutters the view. We want to know how much insulation we have, but we don’t want to see it everywhere.

In a technical drawing, we generally represent insulation only on small parts of the duct, with an annotation, and then infer that the duck is covered everywhere else.

To adapt this method on Revit, I create two detail components, representing the insulation on a round and on a rectangular duct.


This components have a “Width” instance parameter for setting their width regarding the thickness of the insulation.


I also create a small piece of code to instantiate these components on selected ducts and pipes.

The idea is to pick a series of duct and pipe, read their insulation thickness, and add a detail component with the corresponding thickness. Like this, we can control where we want to see an insulation, and improve the overall legibility of the drawing.


This solution allow me to create quickly some annotations to represent the insulation without having to display it everywhere. However, this is more a workaround than a real solution. Nothing here is adaptive, and you have to restart the tool each time you edit or even move your duct.

public void InsulationTag()
			UIDocument uidoc = this.ActiveUIDocument;
			Document doc = uidoc.Document;
			Reference r = null;
			//Retrive the two symbols
			FamilySymbol rectangularSymbol = null;
			FamilySymbol roundSymbol = null;
			ICollection<Element> collection = new FilteredElementCollector(doc).OfClass(typeof(FamilySymbol)).OfCategory(BuiltInCategory.OST_DetailComponents).ToElements();
			foreach (Element element in collection)
				FamilySymbol current = element as FamilySymbol;
				// This NewFamilyInstance overload requires a curve based family
				if (current.Family.Name == "DuctInsulationAnnotationRectangular")
					rectangularSymbol = current;
				else if (current.Family.Name == "DuctInsulationAnnotationRound")
					roundSymbol = current;

				while (true) {
					using( Transaction t = new Transaction( doc ) )
						t.Start( "Annotate Insulations" );

						r = uidoc.Selection.PickObject(ObjectType.Element, new SelectionFilter(),"Pick a duct");
						if (r != null)
							MEPCurve mepCurve = doc.GetElement(r.ElementId) as MEPCurve;
							//Get the middle of the duct
							LocationCurve locCurve = mepCurve.Location as LocationCurve;
							XYZ insertionPoint = locCurve.Curve.Evaluate(0.5,true);
							//Get the angle
							XYZ vector = locCurve.Curve.GetEndPoint(1) - locCurve.Curve.GetEndPoint(0);
							double angle = new XYZ(1,0,0).AngleTo(vector);
							//Get the insulation
							ICollection<ElementId> insulationIds = InsulationLiningBase.GetInsulationIds(doc,r.ElementId);
							if (insulationIds.Count !=0)
								InsulationLiningBase insulation = doc.GetElement(insulationIds.First()) as InsulationLiningBase;
								if (insulation != null)
									FamilySymbol symbol = null;
									double widht;
									try {
										widht = insulation.Width;
										//Select the detail familly
										symbol = rectangularSymbol;
									} catch (Autodesk.Revit.Exceptions.InvalidOperationException) {
										Parameter outerDiameter = mepCurve.get_Parameter(BuiltInParameter.RBS_PIPE_OUTER_DIAMETER);
										if (outerDiameter != null)
											widht = insulation.Thickness*2 + outerDiameter.AsDouble();
											widht = insulation.Diameter;
										//Select the detail familly
										symbol = roundSymbol;
									//Create the annotation
									FamilyInstance annotation = doc.Create.NewFamilyInstance(insertionPoint,symbol,doc.ActiveView);
									//Change the witdh
									//rotate the component
									annotation.Location.Rotate(Line.CreateUnbound(insertionPoint,new XYZ(0,0,1)),-angle);

			catch( Autodesk.Revit.Exceptions.OperationCanceledException )

public class SelectionFilter : ISelectionFilter
		#region ISelectionFilter Members

		public bool AllowElement(Element elem)

			if (elem.Category.Id.IntegerValue == (int)BuiltInCategory.OST_DuctCurves) return true;
			if (elem.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PipeCurves) return true;

			return false;

		public bool AllowReference(Reference refer, XYZ pos)
			if (refer.ElementReferenceType == ElementReferenceType.REFERENCE_TYPE_NONE) return false;

			if (refer.ElementReferenceType == ElementReferenceType.REFERENCE_TYPE_SURFACE) return true;
			if (refer.ElementReferenceType == ElementReferenceType.REFERENCE_TYPE_LINEAR) return true;

			return false;


Rooms to BCF

I am a huge fan of Tekla BIMSight. It’s powerfull, it’s free and very user-friendly. But like with many other project review solutions, I have difficulties to locate myself in the building while spinning around the 3D model.

It become even more tedious when you have to find a specific room in the model. There is no convenient way to retrieve its by a name or its number or zoom on it quickly.

A solution is to sort the Tekla BIMSight objects browser by Level and by Name to display every room in the model. You can then select a room and start create your clipping planes around it. This is not user-friendly. Furthermore, I haven’t always the luxury of working with IFC.

Object Browser

This is why I create a small application for creating a BCF note for every room of a building. This Revit plug-in save the location, the dimension and the name of all rooms to a BCF file.


When opening this file in Tekla BIMSight, we see every room neatly sorted by level in the Notes tab.


As you select one of these notes, Tekla BIMSight zoom on the selected room, and create nice clipping planes around it.

In Tekla

To do so, I am using XbimBCF, a great library for reading and writing BCF files. I just have to adapt it a bit since Tekla BIMSight does not support yet the second version of the BCF format.

This application is now more a proof a concept than an actual solution, but I will try to find the time to clean and share the code.

The BCF concept appears more and more appealing as I work with it. This format allow to develop small utilities like this one very quickly, and will be very useful for many tasks involving project review.

Revit model management in Excel

Model management can involve some tedious tasks. Cleaning up the mess created by an unruly user when he import all categories of an old Revit model is probably the most tedious of these tasks.

When someone import elements from another model, we quickly end up with thousand of view templates, filters, and other user created views which can become totally unmanageable.

Here come the BIM Manager, who spend two tedious days sorting these views and campaigning against view proliferation.

To help address this problem, I create a small piece of code for exporting every view, template and filter to three CSV files.

CSV Files

To read these files in a meaningful way, I use PowerPivot in Excel to create some kind of a small database, with two relationships :


We can then create tables displaying how filters and views are used, like how many filters are used, or where the templates are applied.

Filters Usage

Templates Usage

Once loaded in the PowerPivot tool, this data allows us to quickly identify which template or filter are used and delete the unwanted ones.

The entire source code is available below, please feel free to use it for your own projects.

public void ExportViewTemplatesList()
	Document doc = this.ActiveUIDocument.Document;
	//find all view
	IEnumerable<View> views = 
from elem in new FilteredElementCollector(doc).OfClass(typeof(View))
let view = elem as View
select view;
	//Create a text file for exporting
	List<string> lines = new List<string>();
	//Add the first line
	foreach (View view in views) {
		if (view.IsTemplate)
	lines = lines.Distinct().ToList();
	string exportpath = @"templates.csv";

public void ExportFiltersList()
	Document doc = this.ActiveUIDocument.Document;
	//find all filters
	IEnumerable<ParameterFilterElement> filters = 
from elem in new FilteredElementCollector(doc)
let filter = elem as ParameterFilterElement
select filter;
	//Create a text file for exporting
	List<string> lines = new List<string>();
	//Add the first line
	foreach (ParameterFilterElement filter in filters) 
	lines = lines.Distinct().ToList();
	string exportpath = @"filters.csv";

public void ExportViewsList()
	Document doc = this.ActiveUIDocument.Document;
	//find all view
	IEnumerable<View> views = 
from elem in new FilteredElementCollector(doc).OfClass(typeof(View))
let view = elem as View
select view;
	//Create a text file for exporting
	List<string> lines = new List<string>();
	//Add the first line
	foreach (View v in views) 
		Level level = v.GenLevel;	
		string levelName = "";
		if (level != null)
			levelName  =  level.Name;
		//Get view filters
		ICollection<ElementId> filterIds;
		try {
			filterIds = v.GetFilters();
		} catch (Autodesk.Revit.Exceptions.InvalidOperationException) {
			filterIds = new List<ElementId>() ;
		string templateName = "";
		if (v.ViewTemplateId.IntegerValue != -1)
			templateName  =  doc.GetElement(v.ViewTemplateId).Name;
		string viewinfos =
			v.ViewName + ";" +
			v.ViewType + ";" +
			v.IsTemplate.ToString()  + ";" +
			templateName  + ";" +
		if (filterIds.Count != 0)
			foreach (ElementId filterId in filterIds)
				string filterName = doc.GetElement(filterId).Name;
				string line  = viewinfos   + ";" + filterName;
	string exportpath = @"views.csv";

BFC Reader

I was talking on my previous post about creating a report from a Open BIM Collaboration Format. This format can be exported from Tekla BIMSight.

I am using the Open BIM Collaboration Format on a daily basis for taking notes during coordination meetings. I am using Tekla BIMSight to create these notes, but any model review solution could do the trick, as long as you can export BCF files from it.

These notes are quite useful for addressing coordination problems, but cannot be seen outside a model.

After the proof of concept I presented to you on my last post, I finally took the time to build a packaged application in order to create a Microsoft Word document from a BCF report.

BCF Reader

Aside from minor technical problems, I was most concerned by the possibilities to edit the style of the report before creating it, and avoid the tedious task to clean it up in Word after.

I finally selected a solution mixing Word template and styles. All you have to do after selecting you BCF report is to load a Word template. The application will automatically retrieve all styles in it, and you will be able to select them for each part of your report.
These parts are described in the picture below, where every information embedded in the BCF note is written down on the report.


You can then save your report in a new word document.

The application can be downloaded here, under the MIT licence.

The entire source code is also available on Bitbucket, feel free to use it for your own project.

Creating a report from a BCF file

I was talking on a previous post about BIMsight and the possibility to export its notes in the BCF format.

This BIM Collaboration Format (BCF) is a common development by Tekla and Solibri to create a standard for exchanging comments between building models. This format can accelerate dataflow during project review by exchanging only comments without having to rely upon the same format and large data exchange through Internet.

The BCF format is currently supported by Tekla Structures, Tekla BIMsight, ArchiCAD, Kubus BCF Manager, Solibri, Elvis, Kymdata’s CADS Planner softwares, DDS-CAD Viewer and DDS-CAD MEP. There is also plug-ins for Revit and Navisworks.

I am using Tekla BIMSight on a daily basis as an advanced BIM notebook. Every problem is addressed during the daily coordination meeting, and documented using notes in Tekla BIMSight.


But for documentation purpose, I also need a paper-based report, quite old fashioned, but handy when you have to work with people without Tekla BIMSight.

I created a little standalone program for converting BCF files to Word reports. These BCF files are created from my notes in Tekla BIMSight.

A BCF file is actually a compressed file, where every note is stored in its own folder, named with the note GUID:


In each of these folder, there is three files:


The markup.bcf file stores all metadata about the note: Its date, its title, its author, its various comment along with their dates, and so on. This is the main source of information for my daily coordination report.

snapshot.png is the first image associated with the note, and an essential part of my report too.

Finally, the viewpoint.bcfv store information about the position of the camera used to capture the snapshot. Since the very point of my report is to work outside the model, I won’t use it here.

I use the XSD Schema provided by Building Smart to create my C# classes and serialize the markup.bcf file.

To write down this report, I use the great DocX library to create a Word 2010 file.

With this little program, I create automatically a nice Word report from my coordination notes, and can share my comments with everyone who does not have Tekla BIMSight.


Getting started programming Revit – Part 2

Last week, we started creating macros in Revit, we saw how to get the current Revit document, and retrieved every visible walls in the current view.

We will see today how to create a tag on these walls.

To tag an element in Revit, we need the Create.NewTag() function. This function is called from the current document, named here myDocument. But to work properly, this function needs a few things as inputs.

It first requires a view to place our tag: we will just use the active view, named myActiveView.

It also needs an element to place a tag on. To do so, we will select each one of our walls with a for each loop.

foreach (Element myElement in myWalls) {
    //Do something with myElement

It means that for every element contained in our list of wall myWalls, we will perform some action, written between the brackets.

A few options have to be set, like the category of our tag, its orientation and its leader.

Finally, it needs a location point to insert our tag. We want our tag to be placed at the center of our wall, so we will retrieve the baseline of our wall, and create a point in the middle of this baseline:

//Get our wall
Wall myWall = myElement as Wall;
//Get its location
LocationCurve myWallLocation = myWall.Location as LocationCurve;
//Get starting point
XYZ myWallStartingPoint = myWallLocation.Curve.GetEndPoint(0);
//Get ending point
XYZ myWallEndingPoint = myWallLocation.Curve.GetEndPoint(1);
//Create the middle point
XYZ myWallCenterPoint =(myWallStartingPoint + myWallEndingPoint)/2;

Know, we have everything we need to create our tag:

  IndependentTag myTag = myDocument.Create.NewTag(

To try this, we need to draw some walls, and load in our model a Wall. We hit F8 to build the macro before running it.

But if we run it, we get the following error:


Its means that we are trying to modifying something inside our model without starting what is called a transaction.

Every modification of our model has to be done within a transaction, a group of modifications that can be discarded. If you remember the list of actions we can cancel in the Revit user interface, each one of them is a transaction that had to be started be before modifying anything in our model.


So let create a transaction:

We start by defining a scope of our transaction with the keyword using. Every piece of code between the following brackets will use the transaction named tx.

using (Transaction tx = new Transaction(myDocument))


Now our transaction is created, we can start it, execute our code, and commit these modifications in our transaction:

using (Transaction tx = new Transaction(myDocument))
                tx.Start("Add Tags on walls");

                foreach (Element myElement in myWalls) {
                    //Do something with myElement

                    //Get our wall
                    Wall myWall = myElement as Wall;
                    //Get its location
                    LocationCurve myWallLocation =
                             myWall.Location as LocationCurve;
                    //Get starting point
                    XYZ myWallStartingPoint =
                    //Get ending point
                    XYZ myWallEndingPoint =
                    //Create the middle point
                    XYZ myWallCenterPoint =
                        (myWallStartingPoint + myWallEndingPoint)/2;

                    IndependentTag myTag = myDocument.Create.NewTag(


We run it and every walls are tagged.


Getting started programming Revit

I recently had some questions on how to start programing in Revit, so I will present here a simple macro created with the embedded macro editor, SharpDevelop.

As an example, I will create a macro for tagging all wall in a given view. Even if this function already exist through the Tag All command, you will see all the possibilities for creating it with a macro.

This week, we will see how to create a macro and retrieve elements draw in our model.

Let’s start it by clicking on the Macro Manager on the Manage tab:

Macro Manager

This is where you create new function in Revit, called “Macro”. These macro are lines of code, conveniently stored in a “Module”.

Macro Manager Interface

After creating a new module, and creating a new macro in this module, Revit starts its embedded code editor, called SharpDevelop. This is where we are going to spend most of our time developing Revit macro.


We can see the name of our macro just after “public void” and before a pair of brackets. We will write all our code between these brackets.

Every action done within Revit are made within a document, generally a .rvt or .rfa file. We have to explain this in our macro by retrieving the current document

Let’s write this:

Document myDocument;

With this line, I explain to Revit that something, a variable, called “myDocument” is a Revit Document.

Don’t forget the trailing “;”, it means the end of the line and has to be placed after every instruction line in a Revit macro.

Then, we write this:

myDocument = this.ActiveUIDocument.Document;

With this line, we explain than myDocument is actually the active document, the Revit document I am currently working with. “this” mean here “this Revit application”.

To be able to annotate every walls in a view, we have to select this view. Here, we call our view myActiveView and set it to the currently active view, the one you are looking at right now.

View myActiveView = myDocument.ActiveView;

Now than our Revit document and the view are selected, we have to retrieve walls to be able to annotate them. The Revit API provide us with a great function, the ability to filter element by category, type, class, … well, pretty much everything.

First, let’s create our filter, with the keyword “new”:

FilteredElementCollector filter 
= new FilteredElementCollector(myDocument,myActiveView.Id);

This “filter” will search for every element contained in “myDocument” and visible in the view “myActiveView”. The “.Id” after “myActiveView” mean that we use the unique identifier of the view instead of the view itself to create our filter.

We can now use this filter to actually catch some walls. Let’s write that:

= filter.OfCategory(BuiltInCategory.OST_Walls).ToList();

We create a list of Revit element named myWalls and retrieve every element of the category Wall (“BuiltInCategory.OST_Walls”). The trailing “.ToList()” convert our filter into an actual list of elements.

Before going any further, we try this. Back in Revit, we draw four walls and run our macro by selecting it in the Macro Manager and hitting “Step Into”. This sends us back to SharpDevelop, with slight changes in the interface, and a yellow highlight on the first line of our macro. This highlight show us which line of our macro is currently executed. Hit “F11” two times to pass the first line.

If we pass our cursor on “myDocument”, a hint appear, showing us than “myDocument” is actually a document.

Document Hint

Let’s hit “F11” a few time to pass the last line. Stop right after it. If we pass our cursor on “myWalls” and click on the small “+” in the highlight, we see the list of walls retrieved by our filter. Everything works as expected, so far.

Walls Hint

We hit “F5” to run in a single stroke the remaining line of code and go back into SharpDevelop.

Next week, we will see how to use this list of walls to create a tag for every one of them.

Use Grasshopper to produce fabrication drawings

I recently have to produce fabrication drawings from a set of panels covering a single curved surface.

These panel were modeled in Rhino 3D, so I decide to extract their boundary curves and use AutoCAD to produce a drawing for every one of them.

I build here an example by extruding a surface between two symmetrical splines.


This construction method create a single curved surface, which is the easiest to cover with plane panel, here with the “Quad Panels” component from the LunchBox plugin.


Then I extract every panel edge and orient it horizontally on the base XY plane.


My objective here is to bake every panel boundary curve on a different layer to be able to sort them separately in AutoCAD. To do so, I use the “Object Bake” component from LunchBox. I also use a “Series” to create a layer names for each panel. Here the names are only numbers formatted as text.


The next step is to add dimensions. Sadly, since native grasshopper dimension does not have any output object, we cannot bake them with the “Object Bake”. So I’m using my own Linear Dimension creation component. As you can see, the code is quite straightforward. I use standard Rhino functions to create my dimensions, and have a small private function for selecting or creating the layer to place our dimension.

 private void RunScript(bool Bake, Point3d A,
                        Point3d B, Point3d C, 
                        string Layer, ref object O)

    if (Bake == true)

      Point3d A0 = new Point3d(A.X, A.Y, 0);
      Point3d B0 = new Point3d(B.X, B.Y, 0);

      double a = (B0.X - A0.X) / (B0.Y - A0.Y);
      double Xc = 1;
      double Yc = Xc / a - A0.X / a + A0.Y;

      Point3d C0 = new Point3d(C.X, C.Y, 0);

      Plane refPlane = new Plane(A0, B0, C0);

      double u,v;
      refPlane.ClosestParameter(A0, out u, out v);
      Point2d A2d = new Point2d(u, v);

      refPlane.ClosestParameter(B0, out u, out v);
      Point2d B2d = new Point2d(u, v);

      refPlane.ClosestParameter(C0, out u, out v);
      Point2d C2d = new Point2d(u, v);

      Rhino.Geometry.LinearDimension dimension = 
                  new LinearDimension(refPlane, A2d, B2d, C2d);

      ObjectAttributes dimAtt = new ObjectAttributes();
      dimAtt.LayerIndex = ensureLayer(tag);

      if (Rhino.RhinoDoc.ActiveDoc.
Objects.AddLinearDimension(dimension, dimAtt) != Guid.Empty)


  // <Custom additional code> 
  private int ensureLayer(string lay)
    int i = doc.Layers.Find(lay, true);
    if(i < 0)
      return doc.Layers.Add(lay, Color.Black);
      return i;

With the “Disc” component, I retrieve every summit of my panel boundary, and place a dimension between two of these points. I also use an offset of my panel boundary to set up the position of the dimension line.


Angular dimensions follow more or less the same principle, with two shift to retrieve the point before and the point after a given submit to create our angle measure.


These two dimension components are linked to layer names, so when hitting Bake, each panel curve end up in its layer along with its dimensions. You can see the result below, with every layer displayed and with only one layer visible.


I can now export every curve and its dimensions to a DWG file. This file is edited with an AutoCAD script to paste each layer in a new template file where the titleblock is prepared. It also add the panel number in the titleblock.


Align Tags Revit Plug-In

I just release a new add-in for Revit on the Autodesk App Exchange for aligning and sorting tags on a view.

This tool is based on the Align command you probably already know from a large bunch of software, from PowerPoint to Adobe Illustrator. It is composed of a set of commands to align or distribute tags on a Revit view, and it is pretty easy to use:


To align two or more of tags, just select them, and select one of the direction of the Align command.

For example, with the Align Left command :

Before After

Every selected tag is aligned with the left one. The alignment reference point is the tag base point (where their reference planes intersect).

To distribute three or more tags along an axis, select them and specify the direction.

For example, with the Distribute Vertically command:

Before After

Vertical space between selected tags is distributed evenly among them. The position reference point is still the tag base point.

Under the hood, everything is pretty simple. The application pick up every tag origin point, and sort them along the View.RightDirection or View.UpDirection regarding the direction you select.

Once these origin points are placed along the chosen vector, each Tag.TagHeadPosition is set to its new position.

The entire source code can be found on Bitbucket, my new place of choice for code hosting. Feel free to use it for whatever you want.

The Align Revit add-in is already available for Revit 2014 and 2015 on the Autodesk App Exchange.

Room Finishes Update

A new version of my Revit plug-in Room Finishes is available on the Autodesk App Exchange.

This major update integrate a new feature for creating floor finishes. The main idea is to create a floor that follow the general outline of a room, at a given height offset from the room level.

The first application is to model quickly floor finishes inside every selected room. Just select a floor type, a height offset and the plug-in will model a finish floor on every selected room.


We can see here the floor created with the previous parameters:


The whole idea came when I have to model an insulation just under the slab for more than a thousand of rooms. Luckily, these rooms where correctly modeled, with their upper limit set just below the upper slab.


So I wrote a small piece of code for creating a floor with the same boundaries than the selected rooms. Just like my previous plug-in for creating skirting board, you just have to select a type of floor to create, enter the desired height and the application will create the floor in the selected rooms.

In my problem, the distance between the room level and the upper slab could change, so I introduce an additional feature, the ability to select one of the parameter of the room to define the height of the floor.


Since my rooms are modeled from slab to slab, I just have to select the Unbounded Height room parameter to create insulation at the correct elevation.


I am also using it to draw a temporary floor at the ceilling height in every room of a project. This temporary floor is used to run a clash detection to check if every HVAC objects are correctly placed above the drop-ceiling height.

The application is already available in the Autodesk App Exchange. If you already have installed Room Finishes, just download the update from the same link.

I also finally clean up my code and upload it on GitHub for everyone to see. The source code is freely available here as a Visual Studio 2012 solution. Feel free to download and use it for any of your own application. I would be delighted if you can use it for something useful.