Time Stamper, the Add-In

There is no easy way to override the color of an entire Revit link. Since most of my work involves linking Revit model from various subcontractors, this is something I miss badly.

Until recently, I was still using some workset hack to create filters on linked models. These filters were allowing me to display linked model with my color of choice.

But relying on workset leave much to be desired, and I have to find another solution.

I recently came up with a different solution, where each model element know where they came from.

The idea is to add file name, date and version in shared parameters on every model element. I created the corresponding Revit Add-In, and it is now available on the Autodesk App Exchange.

This application will:

  • Ask for identification information
  • Create four shared parameters on every category
  • Fill these shared parameters with identification information

The code itself is pretty easy, but there is a lot of applications.

First, it becomes easy to create filters on linked model. These filters allow us to display each linked model with a different color.

ColorsByModel

But it also enables us to tag the origin of every element, like we can see in the following screenshot.

IdentifyElements

You can create a linked models schedule, with date and version.

LinkedFilesSchedule

To help you create filters and tags with these parameters, you will find here the shared parameter text file. Please also note that the application uses a list of categories to create the four shared parameters. You will find this list here.

I hope this application will help you in your work, don’t hesitate to share your suggestions in the comments.

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;
}