Revit plugins updates and new features

As usual during this time of the year, I published the new version of my plugins for Revit.

Align

The big improvement this year is the ability to align any type of elements, annotations or tags. I must thank Deyan Nenov and his large contribution to the source code for this new feature.

From now on, you can select any kind of element and align or distribute them evenly. This feature use the bounding box of the element in the view as a reference.

Align or distribute all elements

The Align command is still view-dependent, so using it in section view or in a plan view will not have the same effect on the overall position of a given element. A word of caution however, the align function can be unreliable in a 3D view.

View-dependent align functions

Of course, you can still use it to align or distribute your tags and annotations, and it even works with viewports:

Align viewports

Along with these improvement, Align now fully support Area tags, and a few bugs have been eliminated. You can now use Align even if on tag without a leader, and multi-leader text are now fully supported, and some selection subtlety have been introduced.

Align tags and texts

I haven’t tested it with all categories, if you find something weird, please let me know, I would be happy to fix it.

You can find this new version on the Autodesk App Store.

Room Finishing

Along with the support for Revit 2018, I corrected an issue where a skirting board was created even if the room was not bound by a wall. From now on, Room Finishing will not create a skirting board along room bounding lines.

Support room with room boundary lines

Room Finishing is also available on the App Store.

Time Stamper

The Time Stamper plugin got a few improvements this year along with the usual bug smashing.

You can now choose between applying your time stamp to every element, or just the one visible in the view. This will give you the ability to filter on which element you want to apply a stamp.

You can also choose to keep or override an existing value, this should help you keep the history of a given element.

A few bugs where corrected, making the creation of the four shared parameters far more reliable. Groups are now supported.

Support for groups

Time Stamper is now available here.

These new versions are now available on the Autodesk App Store. Don’t hesitate to report any issue you might find in them or ask for improvement.

These plugins are open source, you can find the source code here, here and here. Feel free to use it on your own project or contribute to these projects.

Modeling a neighborhood with Flux Site Extractor, GIS data and Revit

Since my last post, I kept on working on urban development. I particularly think about importing GIS data into our usual authoring tools. My experience with Infraworks was interesting, but it is still difficult to use this data in a Revit model.

The most promising resource out there for creating a context in a Revit model is the Flux Site Extractor. You select an area, add some features to extract and send them to a Flux project.

The Flux Site Extractor interface

After retrieving this data in my Flux Site Project, I use the Flux Dynamo nodes to get the topography as a mesh, extract the vertices of this mesh as points and use these points to create a toposurface in Revit.

Creating a toposurface from the Flux Site Extractor

Building profiles and heights came from OpenStreetMap, and aren’t accurate enough to be used for site analysis. But I am using building profiles to draw on a plan view the footprint of every building retreived with Flux. This will help me futher down the road to align my buildings on the toposurface.

Buildings footprints drawn in a plan view

I found more accurate data on the parisian buildings on the APUR Open Data plateform. I download this data as a shapefile containing every building in Paris.

Obviously, this dataset is too large to be imported as it in Revit. I am using QGIS, an open source GIS application, to extract a subset of this file. To do so, I draw a polygon encompassing the few city blocks I want to retrieve and use the “Clip” function to create a new shapefile containing only the selected buildings.

Isolate the propers city blocks

I am using the DynamoGIS package to import this shapefile into Revit. These nodes allow me to read the file and extract the shapes of the buildings.

The most difficult thing here was to include inner boundaries in a building shape. These boundaries are not taken into account by the Surface.ByPatch node. I manage to split the first surface (the largest) using the inner curve. This allow me to create the hollowed surface.

Highlight of inner boundaries

The DynamoGIS include nodes to query any value associated with shapes. I am using them to retrieve building heights in the dataset and extrude my buildings at their proper height.

Since it is GIS data, I am using a nice tip from LandArch to move my geometry near the project base point, and make it usable in Revit.

I am using the FamilyInstance.ByGeometry node from the great Spring nodes of Dimitar Venkov to create the buildings as mass families.

This Dynamo definition create a mass family for every building in the shapefile, extruded up to its actual height. Combined with the toposurface created with Flux, this look like an actual neighborhood, where you can think about massing and site integration directly in Revit.

Site integration of a project

This process still has a few issues. The buildings aren’t adjusted at the toposurface and aligning them with the toposurface created with the Flux Site Extractor involve some manipulations. I still have some work to do on this process to streamline it, and get a more accurate representation of an existing site.

Another view of the project

 

Room Finishes Update

I keep on working on my Revit add-ins. After Align, it is now Room Finishes who have been updated to support Revit 2017. Along with this update, I also have integrated some new features.

First of all, Room Finishes now support all kind of units. You just have to type your dimension with its unit symbol, and the plugin will convert it in a floor height or a skirting board height. The plug-in will now also use the default length unit of your model.

interface

I have to thanks Brian Winterscheidt for this update, who was kind enough to contribute to my plug-in on Github, and point me to the Revit unit conversion system available in the API.

The other major update is the ability to join skirting board with their supporting wall. You can now join both geometries automatically. This enable one of the most wanted feature, the ability to cut the skirting board around the door.

Just select “Join geometry” before running the command, and every skirting board will be joined with its host wall.

join

This feature could generate its fair share of warning, so I have remove every related error message. You will now be able to run this command without having to dismiss every warning that come up.

I also add some minor UI improvements, like the ability to resize the window, or sorting wall type names by alphabetical order to be able to quickly find the specific wall type you have created for your skirting board or your floor finish.

Of course, Room Finishes is still open source, the entire code can be found on Github.

This plug-in is already available on the Autodesk App Store. If you like it, don’t hesitate to write a nice comment or add a few stars, it always means so much to me!

Align Tag Update

It is this time of the year again, and I have finally take the time to update Align on the App Store for the new version of Revit.

However, there is more in this than a simple version update, and this new release is packed with improvement, both small and large.

The main change reside in the alignment method. In the previous version of Align Tag, I was using the center point of a given tag as a reference to align tag (either left or right). To improve on the alignment of tags of various sizes, I now use the bounding box of the tag.

AlignSolution

Tags will now properly align themselves along their right or left side, regardless of their size or origin point.

Align

However, if you want something similar to the older version, you can use the new Align Center and Align Midlle commands, which will use the center of the tag as a reference.

This new alignment method is more in line with what can be found on solutions like PowerPoint, or Adobe Illustrator, and will allows you to neatly arrange your tag whatever their size or origin point.

Another important improvement is the long awaited support for Text. You can now align Text along with Tag, using the same command.

While I was at it, I also add support for Keynote tag, Room Tag and Space Tag, basically every tag. The Area Tag is still missing, but can be expected for the next version.

However, this support came at a cost, and I have to drop the support for Revit 2015 and prior. So, if you are still using this version, you will have to keep the old Align plugin.

There is also a handful of small UI improvement that I hope will help you.

Aligned tags are now kept selected after running the command so you can align them in another direction right away.

Your Align commands are also one click closer to you! The interface have been artfully arranged in a new tab to keep every icon directly accessible in the ribbon.

icons1

Under the hood, I have rewrote a large part of the code to support more types of annotation elements, and I hope to be able to use this new framework for more complex manipulations, including in the Arrange Tags function.

Of course, Align Tag is still open source, the entire code can be found on Bitbucket.

This plug-in is already available on the Autodesk App Store. If you like it, don’t hesitate to write a nice comment or add a few stars, it always means so much to me!

Bridge design

One of my colleague is currently working on bridge design with Revit. I have to admit, my first reaction was more like “Revit is not fit for bridge modeling, period”. But after some thought, I found Revit to be a pretty interesting solution to design these kind of infrastructures.

The main issue is that even a simple bridge has a rather complex geometry, with a double-curved alignment and potentially a variable cross section. There is various possibilities to create this kind of geometry in Revit, I will present only one of them here.

This entire article is mostly inspired by the work of Matthias Stark, from Autodesk, and its great AU 2015 class.

The alignment

The first issue we ran into while designing our bridge is the inability for Revit to create a real double-curved, 3D curve.
This is why I create my alignment in Rhino, and use Grasshopper to create a list of X, Y and Z coordinates that define points along my alignment. Another solution is presented by Matthias Stark in his class, where he is using AutoCAD Civil 3D to export the coordinates of the alignment in an Excel spreadsheet.

Once I have all my coordinates in an Excel file, I use a simple definition in Dynamo to create a series of reference points in a Mass family, and use these points to create a curve representing my alignment.

Alignement

I also make sure that units in my Revit mass family are consistent with the unit of my alignment coordinates points.

The cross section

To create the cross section shape of my bridge, I use an adaptive family with a single adaptive point, which will be placed on our alignment.

The keep our cross section properly aligned with our bridge axe, I use the “Orient to” parameter of the adaptive point. By setting it to “Global (z) the Host (xy)”, I make sure than the X and Y axes (Red and Green) will follow my alignment, while the Z axe will stay vertical.

axesI make a few try before getting it right, but I was finally able to properly align my adaptive component with the divided path. Since the X (Red) axis will be tangent to the alignment, I had to draw my cross section in the “Center (Left/Right)” plane of the adaptive family.

adaptiveFamily

Modeling

The rest of the modeling is pretty straightforward. After placing the first adaptive component on a divided path based on our alignment, just select Repeat to place one on every division point, then decompose the Repeat pattern and create a form.

repeat

form

To create the cavity inside the bridge deck, I use the same divided path to place two cavity profiles (right and left) and create two void forms to cut into the bridge deck.

voidForms

Section View

The main issue with this mass family is around dimensions. If you try to add dimensions directly on the Mass family in a section view, these dimensions will be anchored on the wrong point.

firstSection

To be able to create section view anyway, I am using the adaptive component loaded in the Mass family. I make it visible in the project and draw a detail line along one of them. This detail line allows me to draw a section view perfectly aligned with my adaptive component

sectionView

Since my section is perfectly aligned with my profile, it appears in the view and can be used as a reference for the dimensions. I temporary hide my mass family, place the necessary dimensions and show again the mass.

With this workaround, I was able to create a simple section view, with the proper dimensions.

section

Matthias Stark also present in his class an entire detailing process using Dynamo, and that I still have to explore this part. But his approach to bridge modeling allows me to create a great first draft, and I will keep on digging this solution.

Wall Openings (again)

I am kind of obsessed with wall openings. The entire process of asking a structural engineer for holes in its beloved wall to let ducts and pipes goes through has always been a rather frustrating experience.

After my first article about a semi-automated way for placing an opening family, here is my latest attempt at creating the perfect opening family.

A good opening family must have the following features:

  • hosted on a wall, a slab or a beam
  • visible in a 3D view
  • fully parametric
  • schedulable
  • sharable in its own model
  • a nice 2D representation

Lets explore these features.

To host them, but still keep the ability to share them in their own model, I use the Generic Model Face Base family template to create my opening family. This template allows me place my opening on any wall, slab or beam, even if they are in a linked model.

In this Generic Model, I create the Opening subcategory, where I will place every element of my opening. This will allow me to fine tune the display of my openings in my model.

I create a bunch of reference planes, and drive them with three shared parameters, Width, Height and Depth. These reference planes help me constrain the Void Form that will cut the host. This void form will create an actual hole in the wall or slab, and will allow me to perform accurate clash detections after integrating these openings.

To be able to see my opening in a 3D view, I draw some Model Lines to outline the general shape of the opening, and place them in the “Opening” subcategory. These Model Lines are only visible in 3D.

3D View

The 2D representation is a pretty complex subject, and I have yet to find the perfect solution. After some experiences with Model lines, I have switched to annotations elements. These annotations elements are drawn in two nested families, one for the projection symbol, the other for the cut symbol.

These annotation families are drawn in a Generic Model, with the “Opening” subcategory, in order to follow the same graphical rules than the main family.

I also use Masked Regions to draw filled patterns, and use the Generic Model Override in a plan view to fill them with black. I am not entirely satisfied with this solution, but the workaround involve Detail Items, and I don’t want to deal with two different categories.

PlanSection 1

Section 2

To display the elevation of my opening family in a tag, or a schedule, I create two shared parameter, Top Elevation and Bottom Elevation.

As I was searching for a solution to calculate the elevation, I notice a feature I wasn’t aware of, the “Schedule Level”, present since at least Revit 2015, that allow us to define a reference level. Revit use it to automatically calculate the corresponding elevation. Since this elevation cannot be used directly in a schedule or a tag, I use a Dynamo definition to update elevation values in my shared parameters. This Dynamo definition perform some simple calculation to retrieve Top and Bottom elevations, and send these values in the corresponding shared parameter.

Dynamo

My work with wall openings is far from complete, and subjects like sharing these openings, and managing their modifications are still pending. You will find here the different families, models and Dynamo definition used in this article, feel free to use or improve on them.

Details Items, subcategories and filled regions

I am currently working with multiple detail items to create a set of views for architectural detailing. These Detail Items must comply with a precise standard for color and line weights, and of course, be consistent all over the model.

To implement these requirements, I define a set of subcategories for these detail items. Let see how it works with these two wood windows details.

01-WindowsDetailsComponents

These detail items already contain various subcategories, and I can control the color, weight and patterns of their lines in Visibility Overrides. Here Medium Lines are blue and Light Lines are red. Interesting, but not quite enough.

02-LineTypeOverrride

I open these two detail items, go to Object Styles, and create four new subcategories: Wood, Glass, Sealant and Steel. I also remove the previous subcategories. I can now select all lines in the detail item, and assign corresponding subcategories to these lines. Once these detail items are loaded back in the model, the four Detail Item subcategories are visible in the Graphic Overrides window.

I can now control line color, weight and pattern directly in my model, in the Graphic Overrides window for the current view, or in the Object Styles window to apply these colors to every details in the model.

03-OverideByMaterial

Sadly, there is no corresponding feature for managing filled region. As you can see in the picture above, the wood pattern is still in black.

In order to change pattern or color in a filled region, you have to edit the detail item and change the filled region style in the family. If you want to edit your wood pattern, you have to open every detail items containing wood, and change the pattern in the family.

To fix this, and be able to edit directly filled region style for every detail item, I create a small piece of code. This function matches model filled region types with those inside the detail item. So if I have a filled region in my Detail Item family with the same name than one in my Revit project, this filled region will take the properties of the one in the project.

matchProperties

After running this function, I end up with nicely matching wood pattern, all set up directly in my Revit project.

04-OverideFilledRegion

As usual, you will find the source code for this solution below, I hope it will help you solve your filled region issues.



public partial class ThisApplication
{
	public void MatchFilledRegion()
	{
		Document doc = this.ActiveUIDocument.Document;
		
		//Find all Filled Region Type, and create a dictonary with it
		Dictionary<string,FilledRegionType> modelFilledRegionTypes =
			new FilteredElementCollector(doc).OfClass(typeof(FilledRegionType)).ToElements().Cast<FilledRegionType>().ToDictionary(e => e.Name);
		
		//Find all loaded families
		IList<Element> elements = new FilteredElementCollector(doc).OfClass(typeof(Family)).ToElements();
		
		//Get Detail Item category id.
		ElementId detailItemCategoryId = doc.Settings.Categories.get_Item(BuiltInCategory.OST_DetailComponents).Id;
		

		
		//Loop on all loaded families
		foreach (Element familyElement in elements) {
			
			Family family = familyElement as Family;
			
			//Exit the families loop if it isn't a Detail Item Familly
			if (family.FamilyCategory.Id != detailItemCategoryId) continue;

			//Open the family
			Document familyDoc = doc.EditFamily(family);
			string familyPath = Path.Combine(Path.GetTempPath(),family.Name+".rfa");
			
			bool familyEdited = false;
			
			//Find all Filled Region Type in the family
			IList<Element> filledRegionTypes = new FilteredElementCollector(familyDoc).OfClass(typeof(FilledRegionType)).ToElements();
			
			using (Transaction famTx = new Transaction(familyDoc))
			{
				famTx.Start("Edit Filled Region Type");
				
				//Loop on all Filled Region types in the family
				foreach (Element filledRegionTypeElement in filledRegionTypes)
				{
					FilledRegionType filledRegionType = filledRegionTypeElement as FilledRegionType;
					if (modelFilledRegionTypes.ContainsKey(filledRegionType.Name))
					{
						FilledRegionType refFilledRegion = modelFilledRegionTypes[filledRegionType.Name];
						//Change the color
						if (filledRegionType.Color.Red != refFilledRegion.Color.Red
						    || filledRegionType.Color.Blue != refFilledRegion.Color.Blue
						    || filledRegionType.Color.Green != refFilledRegion.Color.Green)
						{
							filledRegionType.Color = refFilledRegion.Color;
							familyEdited = true;
						}
						
						//Change the Background
						if (filledRegionType.Background != refFilledRegion.Background)
						{
							filledRegionType.Background = refFilledRegion.Background;
							familyEdited = true;
						}
						
						//Change line weight
						if (filledRegionType.LineWeight != refFilledRegion.LineWeight)
						{
							filledRegionType.LineWeight = refFilledRegion.LineWeight;
							familyEdited = true;
						}
						
					}
					
				}
				
				famTx.Commit();
			}
			
			if (familyEdited)
			{
				familyDoc.LoadFamily(doc, new FamilyOption());
				familyDoc.Close( false );
			}
			else
			{
				familyDoc.Close( false );
			}
		}
	}
}

public class FamilyOption : IFamilyLoadOptions
{
	public bool OnFamilyFound(bool familyInUse,out bool overwriteParameterValues)
	{
		overwriteParameterValues = true;
		return true;
	}
	
	public bool OnSharedFamilyFound(Family sharedFamily,bool familyInUse, out FamilySource source,out bool overwriteParameterValues )
	{
		source = FamilySource.Family;
		overwriteParameterValues = true;
		return true;
	}
}

Linked MEP systems

I am currently working on a large building complex, where multiple buildings share the same plumbing and HVAC systems. Every building is linked to a common mechanical room, pipes and ducts run between them.

MultipleBuildings

To address some performance issue, I needed to split this complex into multiple Revit files, one per building, consequently dividing the systems into as many pieces.

But to perform flow and loss calculations, these systems need to be in the same file. More ever, MEP connectors from the linked project are not available in the host project.

However, there is a workaround to be able to display accurate results for calculations that require the entire system to work.

The picture below shows a simple hot tap water supply system, spanning across two files. The water heater is in the host file, and the four lavatories are modeled in a linked project. In this configuration, the system is split in two, and the flow is not properly set up between both files.

HotTapWaterSystem

To be able to display the correct flow values on both sides of the system, I create a plumbing fixture placeholder to simulate the rest of the system that isn’t in the file. Autodesk already provides as a part of the standard Revit families four connectors for this kind of emergency. These families are face based, so I create my own, based on the original ones.

This placeholder is a simple plumbing fixture family, with a cylinder shape and a single connector. This connector is set up to be able to define manually every flow and loss values going through the connection.

Pipe Connector - Domestic Hot Water

This plumbing fixture family is placed at the end of the system, on the water heater side. It acts as if the rest of the system, here the four lavatories, were in the host model.

By typing in flow and static pressure values directly in the plumbing fixture placeholder, we end up with the correct values in the host model.

connector

This little workaround allows us to keep accurate system calculation, even with systems split between files.

Some Thoughts About Rooms and Spaces

A lot of MEP calculations available in Revit needs analytical spaces at some point, and it is crucial to have them properly modeled.

Properly placing these spaces can be a tedious business, especially for large buildings with hundreds of rooms. You can use the Place Space Automatically function, but it can create spaces in undesired places, and does not necessarily match the architectural rooms.

Furthermore, superposing of a space upon an existing architectural room only retrieves the name and the number of the corresponding room, and falls short for any other property we could want to add to our MEP space.

One of the solutions for creating MEP spaces is to match rooms created in the linked architectural model. To do so, I wrote a small application for creating a space for each room defined in a linked model.

This application loops on every linked instance, searching for rooms, and uses these rooms to create the corresponding space, retrieving in the process a handful of parameters, like name, number, and most critically, Limit Offset.

You can find here a little ScreenCast showing how it works. The entire source code is available below.

While working on it, I also found some interesting property : Computation Height. This is a level property, and it allows to define the height used to define boundaries on this level.

Let’s create three rooms on a given level:

ThreeRooms

But if we add some variation on the floor level, the room disappears with the following warning:Warning

ARoomDisappear

By default, these rooms are calculated at the level elevation. Every wall at “0 m” above the level will be used as a room boundary.

The Computation Height properties allows us to change the elevation where we calculate the room. In our example, we change the Computation Height of the Level 1 to 1 meter, and the room fit nicely between its boundaries.

ComputationHeight

Of course, this is also true for Spaces.

public void RoomToSpace()
{
	Document activeDocument = this.ActiveUIDocument.Document;
	
	//Get All linked instance
	FilteredElementCollector collector = new FilteredElementCollector(activeDocument);
	List<RevitLinkInstance> linkInstances = collector.OfCategory(BuiltInCategory.OST_RvtLinks).WhereElementIsNotElementType().ToElements().Cast<RevitLinkInstance>().ToList();
	
	//Get all levels
	collector = new FilteredElementCollector(activeDocument);
	List<Level> levels = collector.OfCategory(BuiltInCategory.OST_Levels).WhereElementIsNotElementType().ToElements().Cast<Level>().ToList();
	
	using (Transaction tx = new Transaction(activeDocument)) {
tx.Start("Create Spaces");

//Loop on all linked instance
foreach (RevitLinkInstance linkInstance in linkInstances) {
	
	//Get linked document
	Document linkedDocument = linkInstance.GetLinkDocument();
	
	//Get linked instance position
	Transform t = linkInstance.GetTotalTransform();
	
	//Get rooms in the linkedDocument
	collector = new FilteredElementCollector(linkedDocument);
	List<Room> linkedRooms = collector.OfCategory(BuiltInCategory.OST_Rooms).ToElements().Cast<Room>().ToList();
	
	//Create a space for each room
	foreach (Room room in linkedRooms) {
LocationPoint locationPoint = room.Location as LocationPoint;
XYZ roomLocationPoint = locationPoint.Point;
roomLocationPoint = t.OfPoint(roomLocationPoint);

if (roomLocationPoint != null)
{
	Level level = GetNearestLevel(roomLocationPoint,levels);
	UV uv = new UV(roomLocationPoint.X, roomLocationPoint.Y);
	
	Space space = activeDocument.Create.NewSpace(level,uv);
	
	space.Number = room.Number;
	space.Name = room.Name;

	Parameter limitOffset = space.get_Parameter(BuiltInParameter.ROOM_UPPER_OFFSET);
	limitOffset.Set(room.get_Parameter(BuiltInParameter.ROOM_UPPER_OFFSET).AsDouble());
}
	}
}

tx.Commit();
	}
	
}

private Level GetNearestLevel(XYZ point,List<Level> levels)
{
	Level nearestLevel = levels.FirstOrDefault();
	double delta = Math.Abs(nearestLevel.ProjectElevation - point.Z);
	
	foreach (Level currentLevel in levels) {
if (Math.Abs(currentLevel.ProjectElevation - point.Z) < delta) {
	nearestLevel = currentLevel;
	delta = Math.Abs(currentLevel.ProjectElevation - point.Z);
}
	}
	
	return nearestLevel;
}

Align Tags Update

This is a long overdue update, but I finally take the time to prepare my Align Tag plug-in for Revit 2016.

Along with the annual clean up and bug fixes, I also add a new feature called Arrange Tags. This will automatically arrange all tags with a leader all around the active view.

Buttons

This function is slightly different from the previous ones since you don’t need to select any tags before running it. Just click on Arrange Tags, and every tag will be neatly placed on each side of the active view.

arrangeTaganimation

This function works great on small views, like local sections or callouts. Don’t expect too much on plan views, you will be disappointed.

How it work?

The main idea behind Arrange Tag is to find on the left or the right of the view the nearest available space to place a tag. Once every tags are neatly placed on each side of the view, a second subroutine check every tag leaders and uncross them.

process

This feature use an opinionated way to organize tags on a view, and it probably won’t please everybody. I created it specifically to place tags on section views, and it follows my own drafting rules. That being said, I hope you will find it useful.

The application is available on Autodesk App Exchange. If you don’t mind fumbling into my code, you will also find the entire solution on Bitbucket, fell free to use it for your own projects.