There is no denial that Revit has increased productivity and reduced costs in medium and big projects compared to CAD. It has helped the construction process in several ways; track the progress of a project, the coordination, and execution, as well as the lifecycle of a building in a single platform or with very compatible software.
Below this macro view of Revit’s capabilities, there is a microcosmos of freedom that allows you to adapt the software to your needs. Often, it may seem that Autodesk left the software incomplete, but more often than not, Revit allows you to use its API to extract and do what you need, as seen previously in this post: Create your first macro in C#
Documentation standards are left to users’ implementation
Today, we are going to solve one of the headaches that arise from the way Revit number doors. Revit numbers the doors using the Mark parameter, incrementing the last number by one. For example, if 1 is the first number, the next doors’ numbers are 2, 3, and so on. Once a door is deleted, its number will not come back unless inputted manually.
This is tedious to fix manually, and confusing as door numbers can jump floors or buildings as changes occur in the design process. So, this breaks one of the most basic rules to issue documentation sets, which is to keep coherent standards of annotation and naming.
To solve this issue, we are going to create a macro that automatically renames doors after room numbers, check our post about starting with macros if you missed it.
Create the macro
Here is the code you need to create this macro:
public void NumberDoors()
{
Document doc = Application.ActiveUIDocument.Document;
// Create dictionary to store door-room values
Dictionary<FamilyInstance, string> doorNumbers = new Dictionary<FamilyInstance, string>();
// Create dictionary to store number of doors in room
Dictionary<string, int> doorsInRoomCount = new Dictionary<string, int>();
// Collect all doors in project
using (FilteredElementCollector doors = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Doors)
.WhereElementIsNotElementType())
{
// Retrieve rooms from doors
foreach (FamilyInstance door in doors)
{
if (door.ToRoom != null)
{
string roomNumber = door.ToRoom.Number;
ClassifyDoors(doorsInRoomCount, roomNumber, doorNumbers, door);
}
else if (door.FromRoom != null)
{
string roomNumber = door.FromRoom.Number;
ClassifyDoors(doorsInRoomCount, roomNumber, doorNumbers, door);
}
}
}
// Create transaction and make changes in Revit
using (Transaction t = new Transaction(doc, "Number Doors"))
{
t.Start();
// Empty Mark parameter to avoid duplicated values
foreach (KeyValuePair<FamilyInstance, string> entry in doorNumbers)
{
Parameter doorMark = entry.Key.get_Parameter(BuiltInParameter.DOOR_NUMBER);
doorMark.Set("");
}
// Populate door numbers in Mark parameter
foreach (KeyValuePair<FamilyInstance, string> entry in doorNumbers)
{
Parameter doorMark = entry.Key.get_Parameter(BuiltInParameter.DOOR_NUMBER);
doorMark.Set(entry.Value);
}
t.Commit();
}
}
private void ClassifyDoors(Dictionary<string, int> doorsInRoomCount, string roomNumber,
Dictionary<FamilyInstance, string> doorNumbers, FamilyInstance door)
{
// Letters to map from integers
const string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Classify door
if (doorsInRoomCount.ContainsKey(roomNumber))
{
// Check door count is one, a prefix will be added to the number
if (doorsInRoomCount[roomNumber] == 1)
{
// Retrieve door with single count from the dictionary that stores door-room relationship
FamilyInstance keyDoor = doorNumbers.FirstOrDefault(x => x.Value == roomNumber).Key;
// Change single count door prefix
doorNumbers[keyDoor] = roomNumber + "-" + letters[doorsInRoomCount[roomNumber] - 1];
// Store current door and room in dict
doorNumbers[door] = roomNumber + "-" + letters[doorsInRoomCount[roomNumber]];
}
else
{
// Store current door and room in dict
doorNumbers[door] = roomNumber + "-" + letters[doorsInRoomCount[roomNumber]];
}
// Increase the door count in room
doorsInRoomCount[roomNumber] = doorsInRoomCount[roomNumber] + 1;
}
else
{
// No door is in room , add door-room to dict
doorNumbers.Add(door, roomNumber);
doorsInRoomCount.Add(roomNumber, 1);
}
}
Macro logic
To solve this problem, we are going to break the macro into small and easy to accomplish tasks:
- Collect all door in the project
- Retrieve the ToRoom or FromRoom property of the door FamilyInstance
- Store the door number according to room number in a dictionary
- Store the number of doors in a room in a dictionary
- Modify the door-room dictionary defined in step 3 with the new input from dictionary 4
- Populate the door Mark parameter with the data contained in the door-room number dictionary
A point that is worth mentioning is the private method. Private methods are not visible in the macro manager and act as helpers for public methods that every Revit user can execute.
DEVELOPMENT IDEAS
As good practice, macros, plugins, or scripts should consider any case that can happen in the model. Some ideas to further develop this macro are:
- Option to number doors according to Phases
- Create a parameter in the doors that allows users to reverse FromRoom to ToRoom
- Take into account doors that are in different Design Options
Leave a Reply