Monday, December 6, 2010

Project 2: Integration of API programing in Revit Model

Decorative wall


Background:


In the project one, roof family has created and different parameters have created within the family, which are dependent on each other. There were mathematical applications involved along with materials and visibility parameters. I but this was a purely structural family whose parameters were known to me. I wanted to incorporate an image in the Revit project in such a way that if I change the image the instance attached to it will also change. I came up with an idea of a black & white image attached to the wall.


See Video: 


Concept:

Step 1
In project1, I have an outdoor swimming pool next to a big retaining wall. Since it was a blank wall, I wanted to add some aesthetic value to it. The original Idea was to take a jpg image, which has only black and white color, and connect it to the retaining wall using API program in such a way that length of wall is 10 times of length of image, height of wall is 10 times of width of image. API program will create depression into the wall where there is black color in the image. However, this would need much more complicated coding and time. Prof Wei Yan simplified the concept and suggested to have csv file instead of jpg image.

Step 2
Revised concept now was to define length and height of the wall based on the number of rows and columns in the csv file and create array to voids, which will be either visible or invisible depending on what data is entered in the csv file. When I tried to create multiple voids in the wall in Revit, it was difficult to select, move and copy them. Thus, I came up with more simplified form of the concept.

Step 3
This is the final stage of concept development. Instead of creating multiple voids, I thought it is more convenient to create multiple wall instances, and will be visible or invisible depending on the data of cvs file. For this, I created several walls whose length and height were 1ft each. In addition, arranged them 10 in a row and 10 in column as if they are rows and columns of the csv file. However, there was possibility that code will import the other walls of project if there is any mistake in detecting correct ID number. Thus, instead of having multiple walls I decided to have a family instance, which will have visibility parameter.
Finally, in the project there is a csv file instead of jpg image and a family instance of solid extrusion instead of void. There are multiple instances, which have the ID numbers in a sequence and can be associated with the rows and columns of csv file.

Array of family instances that acts as a base wall for the project:

Development of the logic:

A cell in the cvs file will be associated to the family instance. If cell A1 is selected, then API program should select the most top left instance to make it either visible or invisible. When the next cell in a row is selected, API should go to next instance. When certain number of columns are passed, it should automatically go to next row. Finally, after certain number of rows, it should stop the process.

The algorithm has a loop that will keep doing the same process for desired number of times to that the desired number of instances. It has a if statement to make the instance visible or invisible.

To create the code, following variables are needed to be defined in the code:
1.       ElementID for the particular instance in Revit
2.       FamilyInstance for particular Family
3.       Parameter for visibility parameter
4.       Integers for row and column of csv file
5.       Variable for Cell value of csv file
6.       Integer count to increase the count

Algorithm for 100 objects: 

Writing the code:
There were several issues while writing the code while assigning functions to the variables. First issue was how to make object visible or invisible depending on the cell value. There is a parameter created visible but it is a yes/no parameter. If cell value = 1 then parameter visible = no, else parameter visible= yes. API code uses numbers 0, 1 instead of yes, no or true, false.

While creating the loop, it was little difficult to state when to stop the operation and how to return to a particular line within the code. Initially I was trying to use ‘return’ command or ‘goto’ command, but it has become much simpler with ‘for’ command. If one right clicks in the C# and then clicks on the Surround with tab, it shows up several commands that can be used to surround the function code.

‘For’ command is used to create two loops one inside the other. Outer loop defines integer for row and inner defined integer for the column. When column count increases, more than desired number the loop operation will complete and go to external loop. In the external loop the row number will increase by 1 and continues the operation of inner loop till desired number of row is achieved.

Column number is defined as an integer but to access the cell from the csv file, column number has to be a string variable, thus whenever cell value is required, column number should be converted into the string. There are several ways to do that but most convenient way is to use ‘Tostring’ command.

After successfully writing the code, next step is to check whether code works in the Revit project. Though the code had no errors, it didn’t work and showed an error in Revit that ‘cellvalue’ is not accessible. To troubleshoot this issue we tried to enter different values of row and column integer. Code was working for few and not working for few numbers. We tried to add a message box function that shows up ‘cellvalue’ and continues the loop. Message box function was working correctly. Thus it was clear that there was no error in the code but something is wrong with the csv file, and when searched in csv file, there was no number entered in one of the cell. Thus the code shows error when there is no input given. In addition code gives error if there is any text entered in the cell instead of number or numbers are entered in the cells that are not being used in the code.

There is one unresolved issue. Whenever the code works successfully in the Revit, the invisible instances disappear. If the file is closed and opened again, the remaining object acquire the ID number of disappeared instances, the ID numbers are shuffled among the visible instances and code cannot work in the same way it worked before. Thus, it works only once, and if I want it to work again and again, I have to undo the previous operation all the time.

Using this code, one can simplify black & while image into the csv file, since the pixels of the image are actually either rectangular or square blocks. If we manage to figure out how to create desired form using square blocks, any image is convertible to row and column format. I took few simple images that take vary less time to convert into csv format and used as input files for code.

Images converted into Excel file:
Following are the images which are converted into Excel file tracing color of the each pixel. One can go to much bigger size and complex geometry, also it is possible to write a code to import image colors into Excel cells depending on each pixel color, but because of time constraints, I wanted to limit the scope of the coding. Right now the code has been written for 32 columns and 20 rows. The number of columns and rows can be easily increased and number of objects in Revit too.
Wherever there is gray in the image number 1 is assigned in the Excel cell. When code is run in the Revit file, it will create the same image on the wall by making few of the instances invisible. 





Code:
Part of the code is given below that shows how the flow chart above is converted into the syntax.

#region Assigning variables
            ElementId deco;
            Parameter visible;
            FamilyInstance wallpart;
            double Cellvalue;
            int count = 0;
#endregion
          
            for (int r = 0; r < 20; r++)
                  {
                      for (int c = 0; c < 32; c++)
                      {
                   
                          Cellvalue = CsvDB(r, c.ToString());

                    deco = new ElementId(476804 + count);
                    wallpart = doc.get_Element(deco) as FamilyInstance;


                              #region Get parameters
                              visible = wallpart.get_Parameter("visible");
                              #endregion*/


                              if (Cellvalue == 1)
                              {
                                    visible.Set(0);
                              }

                              else
                              {
                                    visible.Set(1);
                              }
                              count++;
                      }
                  }
            transaction.Commit();
     
            retRes = Result.Succeeded;
            return retRes;
        }

       private double CsvDB(int row, string col)
        {
            string csvSourceFile = typeof(Csv).Assembly.Location.Replace("CommonLibrary.dll", "walldesign1.csv");
            CsvDoc csv = Csv.Load(csvSourceFile, false);
            return csv.Get<double>(row, col); 
        }






Continuation of the project using bmp image:
It is possible to use bmp image instead of jpg. The principle code and loop remains same but this time there is a pixel instead of cell value. This is the sample image with only 100 pixels and created the following code to make it work exactly as it does in the previous part of the project.

Code:
ElementId deco;
string bmpSourceFile =
typeof(Csv).Assembly.Location.Replace("CommonLibrary.dll","SampleData1.bmp");
            Bitmap image = new Bitmap(bmpSourceFile);
            System.Drawing.Color pixelColor = new System.Drawing.Color();

            //double Cellvalue;
            int count = 0;
            Parameter visible;
            FamilyInstance wallpart;
            for (int r = 0; r < 10; r++)
                  {
                      for (int c = 0; c < 10; c++)
                      {
                    //string col = Convert.ToString(c);
                    //string col = "" + c + "";
                    //Cellvalue = CsvDB(r, c.ToString());

                    deco = new ElementId(179920 + count);
                    wallpart = doc.get_Element(deco) as FamilyInstance;

                    #region Get parameters
                    visible = wallpart.get_Parameter("visible");//Get instance parameter (Visibility)of the family
                    #endregion*/

                    pixelColor = image.GetPixel(c, r);
                    double grayscale = 255 - ((pixelColor.R + pixelColor.G + pixelColor.B) / 3);
                    if (grayscale == 0)
                    {
                        visible.Set(1);
                    }

                    else
                    {
                        visible.Set(0);
                    }
                    count++;
                      }
                  }

Result for the bmp image: 





 The results of code using bmp image are the same as cvs file. only important thing is one should know how many pixels are there in the image in its length and width.

Scope for the future development of the concept:

Since it is possible to work with an image the next goal will be to have 3-4 colors in the image and depending on the color of the pixel, object will move little bit forward or behind. This can give an additional aesthetic value to the building facades