December 27, 2014

Dynamo - Custom Node, 2 Char Zero Padding

Dynamo provides a way to modularize code, both to make it easy to reuse in future projects and to make it easier to follow the flow in the main routine. Creating a custom node is as easy as selecting the group of nodes that perform a particular function in a Dynamo file and choosing Edit > Create Node From Selection from the pull-down menus or pressing CTRL+D.

The image above shows the Coded Date Parameter graph with the custom nodes replaced with the underlying nodes. This works, but it occurred to me that I might want to reuse the conversion of an integer to a two-character, zero-padded string part in the future, By creating a custom node for that part of the graph, I can save the custom node (as a DYF file) to my Dynamo definitions folder, where it will be available in the left-sash Library. An added benefit is the simplification of the main graph.

Creating a Custom Node
  1. Select the nodes to be included in the custom node. Multiple nodes can be selected by holding down the SHIFT key as you select individual nodes or by clicking in an empty space in the graph and, while holding the left mouse button down, dragging to enclose the nodes within a window. Like AutoCAD, if your second drag point is to the right of the first, you will have a window selection that only selects nodes completely within the window; if your second drag point is to the left of the first, you will have a crossing window that will select nodes that are either completely or partially within the window.
  2. Select Edit > Create Node From Selection or press CTRL+D.
  3. In the Custom Node Properties dialog, enter a Name and a Description (tooltip) for your custom node. Then specify a Category, which determines where the node will appear in the Library. NOTE: You do not have to select from the existing categories; you can type in a category of your choosing. Use a period to separate the name of an upper level category from the name of a nested category. In the example shown, I created a new top level category for my own nodes called "DWK" (my initials) and a subcategory called "String" below that. Press OK to create the custom node.
  4. Observe that the selected nodes have been replaced by a custom node, and that a new tab has appeared in the workspace.
  5. Select the Integer to String - 2 Char Zero Padding tab to make it current, and then, in the upper right corner of the graph pane, select the top icon (dashed line square) to zoom to the extents of the graph.
    Notice that Dynamo added Input and Output nodes to the custom node graph, one Input for each "wire" leading into the selected nodes and one Output for each "wire" leading out of the selected nodes. These represent the inputs and outputs of the custom node. You can rename the labels (object and result in this example in the image above) that appear on the custom node by selecting the label and typing the desired name. I chose to rename the input label to integer and the output node to string.
  6. Assuming that your original graph worked, the custom node is now complete. All that remains is to save the node to a DYF file, in your Dynamo definitions folder. Select File > Save from the pull-down menus or press CTRL+S to save the file. The Save As dialog should default to the appropriate folder (in my case, C:\Users\dkoch\AppData\Roaming\Dynamo\0.7\definitions; substitute your user name and Dynamo version). Failure to save the file will likely result in an undefined node in the DYN file from which you created the custom node. If you try to close the custom node tab without saving, you will be alerted to the fact that there are unsaved changes.
Integer To String - 2 Char Zero Padding Node
This custom node expects to receive an integer input, and uses the ToString node (Core > Builtin Functions > ToString) to convert the integer to a string. This string is then sent to three different nodes:
  • A String.Length node (Core > String > Actions > Length) determines the number of characters in the string. A Number node (Core > Input > Number), set to 1.000, is compared to the string length by a != [Not Equal] node (Operators > !=), which will return true if the string length is anything but 1 and false if the string length is one.
  • A String.Concat node (Core > String > Actions > Concat) concatenates the outputs of a String node (Core > Input > String), set to "0", and the output of the ToString node. This creates the potential zero-padded condition.
  • An If node (Core > Logic > If) also receives the output of the ToString node, in its true input. The If node is used to determine what string will become the output of the custom node. If the result of the != node, which is fed to the test input of the If node, is true (string is not one character in length), then the original string from the ToString node is passed through as the result, since it is the input for the true input of the If node. If the original string is one character in length, then false will be passed to the test input of the If node, and the results of the String.Concat node, which is passes to the false input of the If node, will be passed through.
The net result is that if a single-digit integer is the input of the node, it will be converted to a string and have a single zero character added at the front of the string. Any other length string will be passed through "as is." Since the input to this custom node is an integer representing a month or a day, the input will only have one or two digits, and this node will provide the desired zero-padding to yield a two-character string in all cases. Any future reuse of this node would require the main graph to assure that the integer input would be in the range of 0 to 99 for a two-character result.

After getting this to work, it occurred to me that it would be more useful to have a custom node that could handle zero-padding to any specified number of characters. But since this node satisfied the needs of the immediate task, I chose to save creating such a custom node as a future task.

December 23, 2014

Dynamo - Custom Plot Stamp

Here is an example of using Dynamo to do two things that you cannot do natively in Revit®: convert one data type to another (in this example, converting integers to strings) and concatenating multiple strings into one string. The inspiration for creating this Dynamo file was this thread in the Autodesk Revit Architecture Discussion Group. The request was a wish for the ability to created a customized date stamp, showing just a numeric date in yyyymmdd format, with three "random" numerals in front and in back, to "disguise" the actual purpose of the string, while allowing those who know to see when the plot was made. The comparison was to what you can do with Fields in AutoCAD®. While the Dynamo file, by its very nature, cannot provide the automatic update that AutoCAD Fields provide, it can standardize the formatting of the custom date stamp, and make manual updates easier than editing the value in the Project Properties dialog.

NOTE: If you work in a "shared" environment, please consult with your BIM Manager and/or co-workers before modifying an office- or project-standard shared parameters file, project template or title block family.

The first stage is to set things up in Revit. This is done the same way that you would do it to set up a manually updated title block parameter. A quick summary of that follows:
  1. Create a shared parameter to be used to hold the custom plot stamp text, if you do not already have one. On the Manage ribbon tab, on the Settings panel, select the Shared Parameters tool. Set the appropriate shared parameters file current, select the appropriate parameter group and create the parameter. Set the parameter type to Text and add a tooltip description (2015 release or later), so that others will know the purpose of the shared parameter.
  2. Add a Project Parameter to your project (for an existing project) or your project template(s) (for future projects). Choose the Shared parameter radio button and then choose the Select button to choose the shared parameter created in Step 1. Select a group under which the parameter will be placed, if the initial "Text" group is not to your liking. Choose the Instance radio button and set the Category to Project Information.
  3. Edit your title block family or families. In each one, add a Label to display the value of the shared parameter created in Step 1. On the Create tab, on the Text panel, choose the Label tool. In the Edit Label dialog, select the Add Parameter tool in the lower left, and add the shared parameter created in Step 1. Select the newly added Shared Parameter in the Category Parameters list box and select the Add parameter(s) to label tool (green arrow icon). Change the Sample Value for the parameter, if desired, then select OK. Fine tune the location and orientation of the label in your title block family, then save or save as the family, if desired, and load the title block family into your project and/or project template file.
At this point we have a Project Parameter that can be set in one place for the entire project, and which will display in the label added to the title block family(ies).
You could open the Project Properties dialog (Manage ribbon tab, Settings panel, Project Information tool) and manually enter a value (or select an instance of the title block and click on "?" for the label you added and type the value in there. While that is a perfectly valid workflow, by using Dynamo, you can update the value in a consistent manner. The image below shows the overall Dynamo graph, followed by an explanation of the parts.
  1. Three stages of nodes generate the pieces of the numeric date in yyyymmdd format. The DateTime.Now node (Library path = Core > DateTime > Now) generates the current system time and date. In the image below, all of the nodes have a "watch toggle" and they are toggled on, so that the value of the node is displayed below the node. We only need the year, month and day, so the value of that node is passed to the DateTime.Components node (Core > DateTime > Actions > Components), which generates separate numeric values for the listed components. The third stage consists of one ToString node (Core > Builtin Functions > ToString), which converts the numeric year output from the DateTime.Components node to a string (text), and two custom nodes named Integer To String - 2 Char Zero Padding, which convert the month and day output from integers to strings while also assuring a two-character result by adding a "0" at the front of any single-character strings. These custom nodes were created from a number of out-of-the-box nodes; I will discuss the creation of custom nodes and show the inner workings of these nodes in a future article.
  2. The random three-digit prefix and suffix are generated by two similar sets of nodes. In each, two Number nodes (Core > Input > Number) establish the range of double-precision real number values that the Math.Random node (Core > Math > Actions > Random(value1:double, value2:double)) uses when generating a double-precision random number. I chose 100 and 999 as the boundaries to assure I would get a three-digit (whole number part) result. Note that I had chosen three decimal place display for numbers in Dynamo at the time these screen captures were take, so all numeric values, even integers, display with three decimal places (Settings pulldown menu > Number Format > 0.000). The output of the Math.Random node is passed through the Math.Floor node (Core > Math > Actions > Floor), to convert the double to an integer (rounding down to the next integer value, effectively truncating any fractional amount), prior to using another ToString node to convert the integer to a text string.
  3. We now have all of the strings we need to create the text value for the plot stamp. A String.Concat node (Core > String > Actions > Concat) takes the five strings (random prefix, year, month, day and random suffix) and combines them into one string. The String.Concat node starts out with one input, string0; you can use the + button to add more inputs and the - button to remove inputs as required by your application.
  4. Now that we have the nodes required to generate the string we want for the plot stamp, we need to get that value out of Dynamo and into the DateCode parameter in Revit. To do that, we first need to get the ProjectInfo element, which holds the Project Information parameters. Two nodes will do that for us. First, the Categories node (Revit > Selection > Categories) allows you to select from a list of all of the built-in Revit categories, one of which is ProjectInformation. Passing that category to the All Elements of Category node (Revit > Selection > All Elements of Category) selects the ProjectInfo element for the current project. These two nodes do not have a "watch toggle", so I included a Watch node (Core > View > Watch) in the overall graph (not shown in the image below) to view the result of the All Elements of Category node. This is not needed for the Dynamo function to work.
  5. The Element.SetParameterByName node (Revit > Elements > Element > Actions > SetParameterByName) is where our value gets pushed into the Revit project. It takes three inputs, the element we acquired in Step 7, a string from a String node (Core > Input > String) into which the name of the parameter to be set has been entered (case sensitive, enter the name exactly as it appears in Revit), and the value to be set, which is the concatenated string from Step 6.
With all of the elements in place, and the graph saved to a Dynamo file (DYN extension), it is simply a matter of pressing the Run button to initiate the program. Dynamo will generate the prefix and suffix numbers, gather up the year, month and day numbers, convert them all to strings and combine them into one string. The Project Information element will be accessed and the string value will be used to set the value of the DateCode parameter.

This may seem like a lot of work, compared to typing fourteen numbers in a dialog box, and if you only ever use it one time, it is. But you can save the Dynamo file and every time you are about to plot, simply start Dynamo, open the file and select the Run button.

In this admittedly simple example, you can see how easy it is to write a program in Dynamo and have that program interact with Revit. Dynamo takes care of all of the hard work of interfacing with the API; all you need to do is connect the nodes.

December 19, 2014

Dynamo

I had the good fortune to be able to attend Autodesk University this year, and without initially intending to do so, ended up with the majority of my classes centered on Dynamo. Dynamo is an open-source add-in that works with Revit (and Vasari) and provides a graphical means of creating custom programming for Revit. The architect side of me appreciates the graphical interface; the long-time AutoCAD®/AutoCAD® Architecture customization side of me (AutoLISP®, VBScript in formula properties) appreciates the ability to do things and process data in ways not accommodated by the out-of-the-box commands and features.

I have had a long-time desire to become familiar with the Revit API and explore the possibilities available there, but a combination of a lack of significant stretches of dedicated time and being somewhat intimidated by the amount of things I would have to learn just to get started have conspired to keep me from starting that journey. From what I have seen so far of Dynamo, there is a much lower entry barrier, and I am excited to learn more and apply that knowledge to real-world, useful projects.

As I discover things (or have others show them to me), I expect I will be documenting them here. Until then, here are links to the Dynamo website and other related resources.

Dynanmo Website
Dynamo Downloads
Dynamo Tutorials
Dynamo Blog
Dynamo Gallery
Dynamo Community (discussion forum)

I would also like to express my thanks to Zach Kron, Marcello Sgambelluri, Nathan Miller, Ian Keough, Michael Hudson, Andrea Vannini and Nuri Miller, who taught one or more of the classes I took at Autodesk University this year. Check out the materials that are available on line:

AB6644: Dynamo: The Future is Wide Open - Zach Kron (session recorded)
AB6542: Explore the Possibilities with Computational BIM - Ian Keough (session recorded)
AB6557: Practically Dynamo: Practical Uses for Dynamo Within Revit - Marcello Sgambelluri (session recorded)
AB6545-L: Dynamo for Dummies: An Intro to Dynamo and How It Interacts with Revit - Marcello Sgambelluri
AB5482: The Great Dynamo Dig: Mine Your Revit Model with Computation - Nathan Miller (session recorded)
AB5492: White-Glove Packaging: Creating Custom Dynamo Nodes - Nathan Miller
AB6495: Dynamo Hero: Using Revit Scripting Tools to Optimize Real-World Projects - Michael Hudson and Andrea Vannini
SD5132-L: Automation Prototyping: A Side-by-Side Comparison of Dynamo and the Revit API - Nuri Miller

December 11, 2014

AutoCAD: Automation Error. Description was not provided.

While I am certain that the error message in the title above will be issued for any number of reasons, I came across this when some users were trying to run a custom AutoLISP® routine that draws an area to be enlarged "box". Until yesterday, I was unable to reproduce the issue on my own computer, making it hard to figure out what was causing the error. Having determined at least one cause for this error message, I decided to document it here.

Finally running into the error myself yesterday, I was able to use the VisualLisp IDE to step through the main routine and determine that the crash was occuring during a call to a subroutine that returns a list of the Layer Keys in the current Layer Key Style. While I would like to think of myself as a relatively competent AutoLISP programmer, there are definitely areas where my skills and experience are thin; error handling routines are one such area. At the time I wrote that subroutine, I was not aware that the out-of-the-box (AecLayerKeyList) function (defined in AecLMgrLISP.arx) would do what I wanted from my subroutine (I was likely confused by the description of the routine as returning "a resbuf list of strings"), so I wrote my own, with no error handling. It turns out that at some point in the past, our office standard Layer Key Style ended up with a corrupt Layer Key. Attempting to read the name of this layer key results in the "undescribed" error. Both the (AecLayerKeyList) function and the Style Manager are capable of handling the error. Style Manager just omits the corrupt Layer Key; the (AecLayerKeyList) function includes the name as an empty string (""), which is not valid input for the name of a Layer Key.

Overwriting the "bad" Layer Key Style with one that does not have the corrupt Layer Key resolves the error and allows the area-to-be-enlarged routine to run. That was much easier than adding error handling to my routine, and should probably be done in any event.