June 30, 2017

Dynamo: Rounding Up

I was looking to try to automate some life-safety-related tasks in Revit® today, specifically related to occupant load calculations, and set out to create a Dynamo graph. I wanted to be able to round up the Area parameter value derived from an Area object, so that a whole number would be used (matching the calculated value in a Schedule) and I also wanted to round up any "fraction of a person" that results from dividing the Area value by the area per occupant value (as specified in the governing building code). I did not see a "Round Up" node in the standard Dynamo nodes, just two different Round nodes, one to round to the nearest whole number and one where you can specify the number of decimal places in the rounded number. (I am currently using the 1.2 release - yes, I know I am behind.) So I set out to create my own Round Up node. NOTE: As my interest here is with positive numbers and rounding up to the next largest whole number for values that are not already a whole number, I did not worry about how negative numbers should be treated (should they round toward or away from zero?). If your application does involve negative numbers, you will need to determine which direction they should round, and adjust the node definition accordingly.

The image above shows the definition of the Round Up node that I created. (As always, you can select an image to see the full-size version.) The math behind it is fairly straightforward:
  • The Input node takes a number as input.
  • The Math.Floor node takes the input number and truncates any fractional part.
  • The x subtract y. node subtracts the result of the Math.Floor node from the original number, to determine the fractional value (if any).
  • The x less y? node compares that fractional value to zero, generating a value of true if it is greater than zero, or false if not.
  • The If node uses that true/false value as the test input, and passes along a value of 1 if the test value is true or 0 if the test value is false.
  • Finally, the Adds x to y. node adds the truncated result of the Math.Floor node to the value of the If node, and passes this along to the Output node as the result of this custom node.

So, if there is a fractional amount, one is added to the whole number portion of the input value; otherwise, zero is added to the whole number portion, which is "rounding up".

While testing this as part of my Dynamo graph, I noticed that one of the Area values, which was reporting as 100 square feet in Revit (after using the RoundUp function in Revit) was unexpectedly rounding up to 101. I took a look at the node values and discovered that an Area that was inside boundaries that formed a 10'-0" square was reporting an area of 100.000000000002 square feet inside Dynamo. While I always want to round any true fractional value up, I decided that it was unreasonable to assign two occupants to that 100 square foot Office (at 100 square feet per occupant) for a non-zero value in the twelfth decimal place. In my mind, that is a computational error. So I came up with another custom node, called Round Up with Floor (see image below).
This custom node definition has all of the same nodes as the Round Up node, except the Code Block that supplies the zero value to the x less y? node is replaced with a second Input node. That allows you to specify the value above which the rounding up will occur. I still need to do additional testing to determine at what value the RoundUp function in Revit will actually round up. For my first test of the Round Up with Floor node, I used a floor value of 0.000001 (one millionth of a square foot), and that eliminated the rounding up of the one area with the very small fractional amount.

June 25, 2017

In What Version of AutoCAD Was My File Saved?

As I start thinking about deploying AutoCAD® Architecture and AutoCAD® MEP 2018, I have been considering how to manage having multiple file formats in use at the same time. Not that we have not had to deal with that in the past; but we have been primarily using 2013-format releases for quite some time, so I will need to make and keep users aware of the fact that once a file is saved in the 2018 format, there is no going back.

One "trick" I personally have used to check on the file format of a file prior to opening it is discussed in this Autodesk Knowledge Network article. You can determine the file format of an AutoCAD® drawing file by opening the file in a plain text editor (like Notepad) and looking at the first six characters. That article lists the "codes" for the 2000 through 2013 file formats. Shaan Hurley, in this article in his Between the Lines blog, has a more complete listing of the codes, including the fact that the new 2018 file format is AC1032. Scroll down to the bottom of the article, under the DWG File History header, for the full listing.

One drawback to that trick is that really large files can take quite some time to open in Notepad, and there is always the risk (however small) that you could accidentally make a change and then save the file in Notepad. It occurred to me that an AutoLISP routine ought to be able to use the read-line function to read the first line of a drawing file, extract the first six characters, and then report the results; this proved to be true. You do have to have an instance of AutoCAD open first, but if you do, the routine works much faster than opening in Notepad for large files, and there is no risk of changing the file. I chose to limit the versions for which it tests to the AC1001 (Version 2.2) format. I was not able to fully test all of those, as I was unable to find a file in my archives that was last saved in anything earlier than Release 9 (AC1004). If your archives include files of earlier vintage, you could extend the code for the earlier versions listed in Shaan's article, assuming that those older files have the "code" in the first six characters.

(defun C:FMT ( / file1 sfile1 sline1 stext1)
  (setq sfile1 (getfiled "Select Drawing File" "" "dwg" 0)
        file1  (open sfile1 "r")
  ) ;_ End setq.
  (if file1
    (progn
      (setq sline1 (read-line file1)
            stext1 (substr sline1 1 6)
      ) ;_ End setq.
      (close file1)
      (cond    ; Condition A.
        ((= "AC1032" stext1)
         (alert
           (strcat
             "Header = AC1032."
             "\nFile " sfile1
             "\nis saved in the 2018 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A1.
        ((= "AC1027" stext1)
         (alert
           (strcat
             "Header = AC1027."
             "\nFile " sfile1
             "\nis saved in the 2013 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A2.
        ((= "AC1024" stext1)
         (alert
           (strcat
             "Header = AC1024."
             "\nFile " sfile1
             "\nis saved in the 2010 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A3.
        ((= "AC1021" stext1)
         (alert
           (strcat
             "Header = AC1021."
             "\nFile " sfile1
             "\nis saved in the 2007 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A4.
        ((= "AC1018" stext1)
         (alert
           (strcat
             "Header = AC1018."
             "\nFile " sfile1
             "\nis saved in the 2004 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A5.
        ((= "AC1015" stext1)
         (alert
           (strcat
             "Header = AC1015."
             "\nFile " sfile1
             "\nis saved in the 2000 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A6.
        ((= "AC1014" stext1)
         (alert
           (strcat
             "Header = AC1014."
             "\nFile " sfile1
             "\nis saved in the R14 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A7.
        ((= "AC1012" stext1)
         (alert
           (strcat
             "Header = AC1012."
             "\nFile " sfile1
             "\nis saved in the R13 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A8.
        ((= "AC1009" stext1)
         (alert
           (strcat
             "Header = AC1009."
             "\nFile " sfile1
             "\nis saved in the R11/R12 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A9.
        ((= "AC1006" stext1)
         (alert
           (strcat
             "Header = AC1006."
             "\nFile " sfile1
             "\nis saved in the R10 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A10.
        ((= "AC1004" stext1)
         (alert
           (strcat
             "Header = AC1004."
             "\nFile " sfile1
             "\nis saved in the R9 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A11.
        ((= "AC1003" stext1)
         (alert
            (strcat
             "Header = AC1003."
             "\nFile " sfile1
             "\nis saved in the Version 2.60 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A12.
        ((= "AC1002" stext1)
         (alert
           (strcat
             "Header = AC1002."
             "\nFile " sfile1
             "\nis saved in the Version 2.50 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A13.
        ((= "AC1001" stext1)
         (alert
           (strcat
             "Header = AC1001."
             "\nFile " sfile1
             "\nis saved in the Version 2.22 file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A14.
        (T
         (alert
           (strcat
             "Header = "
             stext1
             "\nFile " sfile1
             "\nis saved in an unknown file format."
           ) ;_ End strcat.
         ) ;_ End alert.
        ) ;_ End condition A15.
      ) ;_ End condition A.
    ) ;_ End progn.
    (prompt "\nNo file selected. ")
  ) ;_ End if.
  (prin1)
) ;_ End C:FMT

Here is an example of the alert message that will be displayed.