It has been a while since I last posted and that's because I've recently started consulting for the splendid architectural company of Glenn Howells Architects. As I am trying to help their awesomely cute young architectural staff overcome the initial hurdles of taking up Revit, I enjoy the dynamics of travelling between their Birmingham and London offices.
What has taken the most part of my working hours lately, however, has been my recent recruitment with BIG. The BIG, as in Bjarke Ingles Group BIG. A studio that I've always admired both for their visionary architecture and for the unique image that they have managed to coin for themselves - that of a rebellious, beer drinking, bearded intellectual. Or maybe that's just Bjarke Ingles himself, I'm not too sure.
Anyways, those and other (successful) projects have been taking a lot of my time, but they have also given me the opportunity to build some new, handy Dynamo definitions that I will now share with you. As you can imagine from the title of the post, I am going to show you 3 separate definitions, all of them using Python script extensively to achieve their tasks.
Batch Sheet Creation
"This should be in by default!" you sometimes exclaim when working with Revit, am I right? And it should. It annoys the hell out of me that Revit still doesn't give you those simple, trivial even functionalities that end up wasting if not the majority than at least some of your production time.
Inside the python node, we will first see all the import statements. We need certain libraries and in most cases, those will be quite similar, that's why I have a separate image which I will refer to when speaking about the other two scripts. One thing that I find myself doing over and over again is copying these statements from an old python node when I start working on a new one. You might find it handy to keep a default python script node stored with your favorite presets.
After we have that covered, we can start writing the specifics of our script. The node will take 2 user parameters - the number of 'copies' and the desired sheet number pattern (this is something you might want to change outside as well as inside the python script if your project sheet numbering follows a different pattern style). To complement the 'duplication' nature of the definition, we need to be working within an active sheet view which parameters we will copy. The bulk of the code is executed inside an 'if' statement, which makes sure we are inside such a view.
Next, we will do some basic string manipulation to make sure we create a new sheet sequence with appropriate numbering. The following piece of code is taken from a node written by Dimiter Venkov (respect) which basically retrieves all elements in the view. What we need to get is the TitleBlock from the active view sheet (which automatically means that if we don't have a TitleBlock on the sheet, we will get an error). Of course, we can always replace this with another input to the python node which feeds an existing in the document TitleBlock.
Once that's done, we are ready to create as many sheets as we want. In a new Transaction, we iterate over the number of sheets we have chosen to make and we increment the sheet_number each time to make sure we create those unique sheet numbers.
That's it! You are free to change anything around that code to make it fit the different scenarios you have in mind.
Duplicate Views and Place on Sheets
Here, we will still be creating new sheets, but this time, the focus is on duplicating a number of views first and then placing them on new sheets. This is yet another example of something that we wish we were given out-of-the-box and something that made a lot of people out there create custom macros, plugins, and dynamo definitions in an attempt to patch that Revit hole (I've spoken about the View Duplicator plugin in one of my previous posts). This python script is just another take on the same subject. Let's take a look.This time, the sheet creation part is externalized and handled by the Sheet.ByNameNumberTitleBlockAndView node. You can find this lengthy title under the Revit/Views/Sheet tab. It takes a couple of parameters (you can see how we generate the TitleBlock this time) and the one we care about the most is the View or the list of Views that we would like to duplicate. This is done inside the python node, which itself takes 5 string parameters - a Prefix and a Suffix of the views that we are looking for, a Prefix and a Suffix with which we are going to replace the old ones (this gives you control over how the views will be named) and a string that will supply our sheets with unique numbers.
The so-called 'Sheet Sequence' code block has a little quirkiness that I'd like to explain - since normally we would be looking for a sequential sheet numbering, the string is of the format "xx-nn%02d". What that means is that, once inside python, the sheet number will append the alphabetical part of the provided string, while incrementing the numerical one. Not only that but the numerical part, the way you can see it on the image, will be of the form "100" and it will increment to "101", "102" and so on. The "02d" part after the "%" sign tells the formatted to treat whatever integer it is given as a double decimal number. So, if you would like to replace that with a single number, you would change it to "xx-nn%01d". Mini exercise - if you want your sheet sequence to be "SK-3020", "SK-3021" ... you will have to put? That's right, "SK-302%01d". As we will not be selecting the views directly from the project, we would like to be able to find them from all the other existing views. In a suitably maintained project, your view names will follow a certain pattern and this will make a lot of sense. If, for some reason, this method of selecting views does not work for you, feel free to change the python node to your liking.
As we agreed, all the import statements are the same as the ones we had before, so we can just skip that part and go straight to the core of the node. We are retrieving all views in the project and we match the ones that have the Prefix and Suffix strings in their names (hint - if those strings are left blank "", this code will obviously match every single view in the project). There is one more requirement that you need to be aware of:
v.ViewType == ViewType.FloorPlan
This will only check our FloorPlan views. We can certainly change that when we are looking for CeilingPlans, AreaPlans and so on. For more information on the correct names of all view types, you can refer to the View.ViewType Property in the Revit Api documentation.
The rest of the code is pretty straight forward to follow, with the only exception of how we create the duplicate view and change access its properties.
v_duplicate = doc.GetElement(v.Duplicate(ViewDuplicateOption.Duplicate))
v_duplicate.ViewName = new_name
There are few things happening in that first line. First of all, we create a new view with the following piece of the code: v.Duplicate(ViewDuplicateOption.Duplicate). There are three options that we can use - .Duplicate, .AsDependent, .WithDetailing. I will leave cracking this complicated code to you. Then we get an object (v_duplicate) by retrieving the newly create view from the document via doc.GetElement. Finally, we can manipulate that object and assign our desirable name to it.
There are lots of ways to break the above code and that is simply because of the many different ways to define the task, especially in the string manipulation part. As always, this is meant to be a guideline and a source of python code that you can string together in a fashion that will do your bidding.
Our final definition deals with the tedious task of cleaning up worksets. I don't think that the problem needs explanation and I am sure that we are all familiar with the fact that our project files need maintenance. The definition that I will show you works especially well in a scenario where Worksets attribution follows the Revit Categories, but even in a non-standard setup it can still be modified in such a way as to allow you to automate at least part of the whole process.
I believe that the graph is easy to read - we get elements by Categories and check if their Workset parameter is not equal to the desired one. If it's not, then we will assign the correct one. Of course, we can simply override the Workset parameter blindly, but that will take more time while the above solution will be snappy and quick the more we use it. It also has embedded the resources to make a more complex solution that filters elements by some other input. Finally, if the setup of your project is robust enough, you can batch collect categories by name and funnel those into their respected Worksets, potentially creating a 1 button solution for the whole problem.
As it turns out, this definition does not have any Python code in it. I got confused, as my usual workflow in Dynamo inevitably involves Python scripting. I would like to say a few words about that and I think that this is as good place as any for my little Grasshopper/Dynamo tirade that has been building inside for a while now.
"Dynamo is Grasshopper for Revit". How many times have we heard this line? I bet a lot. And while Dynamo seems to have started as a Grasshopper for Revit and while there are certainly enough people out there that drive steadily in that direction, I personally think that because of the underlying differences between the two core programs that those "add-ons" serve - Rhino and Revit - there will and should always be a difference between GH and Dynamo. Rhino deals with complex geometry in a simple way. Revit deals with simple building components in a complex and granular way. Grasshopper has a clean, well-defined interface that intuits the way you deal with it. It is hard to say that Dynamo has any interface at all. It is ugly, awkward, has tons of outdated, free components that no one knows how to use but the builders themselves (there is no certainty in that either) and it forces its users to deal with scripting first hand. And you know what? I think that's great! I think that scripting is not so hard and that Dynamo will eventually create a more knowledgeable and intelligent community around itself than the cry-baby generation of Rhino geometry junkies. By the way, I don't mean that. I love Rhino and Grasshopper, I do, I do, I do. But that's exactly why we should leave complex geometry to Rhino and embrace the documentation awesomeness of Revit and bring it to the next level with our Python augmented Dynamo super powers!