Showing posts with label Custom Node. Show all posts
Showing posts with label Custom Node. Show all posts

January 06, 2015

Dyanmo: Custom Node, Integer To String - Zero Padding

Having created a node that would convert an integer to a string, padding a single-character string to two characters by adding an initial "0" so that a YYYYMMDD date string could be generated, it occurred to me that having a more general purpose node, where you could specify the number of characters to which the string should be zero-padded, would be more useful. As with the two-character node, no effort is made to truncate the original string if it exceeds the specified length, but strings with fewer characters will have the necessary number of zero characters ("0") added to the front of the string to bring it up to the specified number of characters.

As seen in the short Screencast below, the custom node takes two numeric inputs, the number of characters to which zero-padding is to be applied and an "integer" [see note at end of article] that is to be converted to a string, with zero-padding as necessary. In the Dynamo file I set up to demonstrate the custom node, an Integer Slider node (in the Library, Core > Input > Integer Slider) is used to specify the number of digits desired and a Number node (Core > Input > Number) is used to provide the integer to be converted to a string. Note that the Run Automatically toggle is checked, so as changes are made, the results are calculated and displayed without having to press the Run button each time.

Integer To String - Zero Padding Node
The image above shows the graph of the Integer To String - Zero Padding custom node. A description of the graph function follows.
  1. The integer to be converted to a string is processed by the Math.Floor node (Core > Math > Actions > Floor) to remove any fractional part (see note below) prior to being passed to the ToString node (Builtin Function > ToString), which converts it to a string.
  2. The integer string created in Step 1 is passed to a String.Length node (Core > String > Actions > Length), which returns the string length as an integer. A custom node called Zero Padding String takes the Final String Length as input to the Integer To String - Zero Padding custom node, and the string length of the integer string and returns a string containing the zeros that need to be added to the front of the integer string (if any). See below for the graph of this custom node.
  3. The zeros string from Step 2 is concatenated onto the front of the integer string from Step 1 by a String.Concat node (Core > String > Actions > Concat), which is then passed as the output of the Integer To String - Zero Padding custom node. Note that if the length of the Step 1 integer string is greater than or equal to the Final String Length, the zero string will be an empty string, and the end result will be equivalent to the Step 1 integer string.

Zero Padding String Node
The image above shows the graph of the Zero Padding String custom node. A description of the graph function follows.
  1. The easiest way to explain how this graph functions is to start with the LoopWhile node (Builtin Functions > LoopWhile). The LoopWhile node takes three inputs: init, the intial state of the object being processed; continueWhile, the test condition that determines whether or not the current object being processed should be processed again; and loopBody, which defines what action(s) are taken on each pass of the loop.
  2. The init input is provided by a Code Block node (Core > Input > Code Block; or double-click in the graph canvas). Code Blocks allow for custom scripting in Dynamo; in this case, it is being used to simply generate an empty string. A String node (Core > Input > String), with the input left blank, could also have been used; I felt that seeing the two double quotation mark characters, with no other characters in between, in the Code Block made the node read better. I also double-clicked on the Code Block title and renamed it to "Empty String" to more clearly indicate its purpose.
  3. The continueWhile input is the most complicated. A - [Subtraction] node (Operators > -) calculates the difference between the Final String Length input and the Starting String Length. If this is a positive number, it represents the number of zero characters needed in the final padding string. We want to compare the length of the current string to this number, and continue to process the string if the current string length is less than the desired string length. This comparison is done by the < [Less Than] node (Operators > <), and the current string length is generated by the String.Length node. For reasons beyond my current understanding, the output of the String.Length node cannot simply be put into the x input of the < node, and the result of that fed to the continueWhile input. The continueWhile input requires a single function as the input, and so a Function.Compose node (Core > Evaluate > Function.Compose) is used to combine these nodes in a way that is acceptable as input for the continueWhile input. The + and - buttons on the node allow you to add or remove function inputs so that you have the correct amount for your application. In this case, the initial default of two inputs was what was needed.
    You may have noticed that both the < and String.Length nodes have unconnected inputs. The LoopWhile node presumes that you will be doing something to whatever is passed to the init input, in this case, a string, and will supply that value to any open input of matching type. Likewise, the Function.Compose node supplies the integer value passed to the func0 input by the String.Length node to the open x input of the < node. Also note that by including the current value of the processed object in the evaluation, we make it possible for the the comparison to eventually be false, and for the loop to terminate. Failure to do so would result in an infinite loop, which is not a good thing.
  4. The loopBody input is fairly straightforward. Another Code Block node, renamed to "Zero String" creates a string with a single zero character in it. This is the input to the string0 input of a String.Concat node. The string1 input is left open, so the processed object string will be supplied here. Each time the loop body is entered, a zero character will be added to the front of the current processed object string, making it one character larger. Eventually, the processed string length will be equal to the difference between the Final String Length and the Starting String Length, the loop will terminate and the final result will be a string with the proper number of zero characters.
  5. To summarize, the loop starts with an initial value of an empty string. The length of that empty string (0) is compared to the difference between the Final String Length and Starting String Length inputs, to see if it is less than the difference. If that difference is 0 or a negative number, then the value fed to the continueWhile input is false, no action will be taken, and the result of the custom node will be an empty string. If the difference is 1 or greater, then the value fed to the continueWhile input is true, and the loop body code will be run, adding a zero character to the empty string. The loop process is then run again, but this time the current value is not an empty string but "0", and the string length is 1. If the difference is 1, then the less than condition is false and the "0" string is passed to the output of the custom node. Otherwise, the loop body is run again and "00" becomes the new current value of the string. This continues until the string length is not less than the difference, at which point the loop terminates and the current string value becomes the custom node result.


NOTE: I put "integer" in quotation marks when describing the node input above because while that is the intended input, both inputs will accept a double (precision real number), with a non-zero fractional value, as input. For the number to be converted, any fractional part is discarded prior to the conversion to a string. The way the Final String Length value is used, any fractional amount will be the same as specifying the next higher integer. If a given application could generate a value with a fractional amount as the input for the Final String Length and only the whole number value is desired, the Math.Floor node should be used to strip the fractional amount. This works for non-negative numbers; the function returns the first integer that is lower than the input value. For negative numbers, this will change the units number. So 1.9 becomes 1, but -1.1 becomes -2. I was not expecting to be using zero-padding on negative numbers; if you have an application that would require that, then you may want to modify the code to accommodate that. Note also that the sign ("-") counts as a character after a negative number is converted to a string. If you want the zero padding of a negative number to be calculated without including the "-" sign in the number of characters, the code would have to be modified to account for that.

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.