Posts by retug (36)

About Page Eye Candy - Javascript and Web Dev Misery

With a desire to update my "about" page, I set out to kill two birds with one stone, a fresh new about page and learning 3d graphics in the browser. https://www.re-tug.com/about/ is built with three.js, a package built within Javascript to make 3d graphics. 

About threejs

I followed a tutorial on youtube to create the threejs scene in the background. My code to make the pulsating mesh can be found here. The about page turned out really cool, click and drag on the background and you should be able to rotate the pulsating mesh.

Learning both C# and javascript recently has lead to a lot of mistakes and lost time, but it has been a enjoyable process. Learning how to debug with console.log has become natural at this point.

Trying to tweak the code in the youtube link to my liking lead to some interesting errors, below was my attempt to manually recreate the wireframe pulsating:

Deployment Hell

Through building this website, I have learned that merely getting your code up and running on a development server is usually about 1/4 of the battle. After getting through the tutorial I set out to push this code up to the internet for everyone to see. (I am mostly writing this part so I have something to reference in the future when I am going through deployment pains)

Attempt #1 - Using Script Tags to Load ThreeJS

After some research, it seemed like the easiest and quickest way to get threejs, orbit controls and gsap (the packages used to make the graphics) was through script tags in htlm.

I had something that looked like the following:

<script type="importmap">
{
"imports": {
     "three" : "https://unpkg.com/three@0.126.1/build/three.module.js",
      "orbitcontrols" :      "https://unpkg.com/three@0.126.1/examples/jsm/controls/OrbitControls.js",
"datgui" : "https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.9/dat.gui.min.js"
"gsap" : "https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/gsap.min.js"
}
}
</script>

<script type="module" src="{% static 'blog/threeBackground.js' %}"></script>

This method relies on cdns (content delivery networks) to deliver the code that is used for threejs, orbit controls etc. I was successfully able to deploy my code with this method, but the code did not work on firefox nor any of the iOS web browsers. 

With my whole family and friends using apple products, I had to find a better to make this happen. Apparently script tags are going to be outdated in the future and I think this is why it did not work on firefox and iOS web browsers.

I would get console errors like the following:

At this point I had maybe sunk 20 hours of work into getting this eye candy into the website.

Attempt #2 - Using node.js and NPM

I'll be honest, I still have no idea what node.js and NPM (node package manager) are, but they seemed like the solution to my iOS problems.

My best understanding of these, node.js is a way to write javascript outside of the web browser and NPM is similar to pythons pip command, a way to install packages. 

Using node.js and npm, I could see threejs and other modules being downloaded into a folder called node_modules in my file directory. Naively, I thought this was the ticket, I could just reference these files on my production machiene on heroku. This did not work and I spent way too long going down this route.

Attempt #3 - Using Webpack, A Bundler

Webpack is another javascript package that converts the javascript file I linked on my github into a singular javascript file that embeds all of the threejs code and other javascript libraries referenced and places it into one singular file. A sample of the file created by using webpack is shown below:

if(t.applyMatrix3(this.matrix),t.x<0||t.x>1)switch(this.wrapS){case r:t.x=t.x-Math.floor(t.x);break;case a:t.x=t.x<0?0:1;break;case
 s:1===Math.abs(Math.floor(t.x)%2)?t.x=Math.ceil(t.x)-t.x:t.x=t.x-Math.floor(t.x)}if(t.y<0||t.y>1)switch(this.wrapT){case r:t.y=t.y-Math.floor(t.y);break;case a:t.y=t.y<0?0:1;break;case 
s:1===Math.abs(Math.floor(t.y)%2)?t.y=Math.ceil(t.y)-t.y:t.y=t.y-Math.floor(t.y)}return this.flip

Lots of gibberish to my eyes...

The command to make this all encompassing file on my development server was: npm run build

This file was the ticket to making the "about" page work on all devices, it no longer requires the html code to have script tags and nor the use of CDNs for the packages like three.js

I had to upload this static file to heroku and serve it up and the rest is history. All total, I think I spent about 10 hours on the actual three.js tutorial part and 40 hours on the deployment aspect of the "about" page. I would like to thank and apologize to my wife for her having to listen me scream, laugh and cry during deployment of this page.

DEPLOYMENT IS TERRIBLE!

Notes on heroku deployment:

  • Disable collectstatic by default, have to run:
    • heroku run python manage.py collectstatic
  • Can ignore double static file reference on deployment
    • heroku defaults to ....\my_website\blog\static
  • added in gitignore file node_modules
  • Within procfile added:
    • web: gunicorn my_website.wsgi npm start

 

Revit Rebar Bender - New and Improved

Above is my first functional C# revit add-in, making bending rebar at any angle an easy task. For those non-structural engineers, the smaller the piece of rebar, the smaller the allowable bend. Being able to quickly sketch rebar to scale with minimum bend radii is often helpful to see if a detail sketch is constructible. This add-in will bend rebar to the ACI minimum allowable bend diameter and will allow for as many points as the user desires.

How to use the add-in

To start the add-in navigate to the add-in tab, click the drop down and select your desired rebar size. Once clicked, you can now start clicking the points where you want to draw the rebar. Once you are done selecting your rebar points (3) minimum, you hit escape to have Revit create a filled region in the location where you clicked. Currently, the filled region defaults to a filled region type of "Solid Black", make sure you have this filled region in your revit project for the add-in to work. The code was written for Revit 2022, I have yet to try the code in any other version of revit.

You can find the code on my github.

Lessons Learned in C#

Given that this was my first real dive in C# and the Revit API, there were a ton of things to learn a few examples.

  • 2/12 does not equal what you think it might, you have to cast your integer 2 and integer 12 to a double as shown below

 

        double x = 2/12; //This actually = 0
        double x = (double)2/(double)12; // 0.16667

 

  • I still do not fully understand FilteredElementCollector (this class gathers revit items from my understanding). Looking at these lines of code is intimidating... The lines below gather all of the filled region types in the revit project and finds the unique ID for "Solid Black"

 // find solid fill type in our revit list
            FilteredElementCollector fillRegionTypes = new FilteredElementCollector(doc).OfClass(typeof(FilledRegionType));
            var patterns = new FilteredElementCollector(doc).OfClass(typeof(FilledRegionType)).FirstElement();
            foreach (Element elem in fillRegionTypes)
            {
                if (elem.Name == "Solid Black")
                {
                    patterns = elem;
                }
            }

  • I made a fun function that tests if user selected points are clicked in clockwise or counterclockwise manner. I was super proud of this, then found out that the Revit API has this function prebuilt into the Curveloop class. I am going to tell myself that my function is way more optimized than the curveloop method to make myself feel better.


public static double ClockWise(List<XYZ> pnts)
        {
            double y = 0;
            for (int i = 0; i <= pnts.Count - 1; i++)
            {
                if (i == pnts.Count - 1)
                {
                    double x = (pnts[0].X - pnts[i].X) * (pnts[0].Y + pnts[i].Y);
                    y = (double)y + (double)x;
                }
                else
                {
                    double x = (pnts[i + 1].X - pnts[i].X) * (pnts[i + 1].Y + pnts[i].Y);
                    y = (double)y + (double)x;
                }
            }
            bool CW_CCW = true;
            if (y <= (double)0)
            {
                CW_CCW = false;
            }
            return y;
        }

Future Work

Currently this add-in will not work in any view other than a drafting view, I would like to come back to this at some point and let the code work in live view. I should have tested this earlier, I started into this task trying to alleviate the issues of the original revit rebar bender and keep this completely detail component based. 

Another item to add in the future is to load the predefined hooked ends, e.g. 90 135, 180 degree hooks. This should be pretty easy if I am thinking about this correctly, but might the necessitate the need for popup window for the user to select hook end directions and hook types

More items off the top of my head, allow for self intersection (currently breaks if the rebar loops cross), update the sloppy code up on Github (could probably be 300 lines instead of 900 lines), learn C# better. Currently I do not know how to have a function return multiple outputs like a list and int. Overall a fun project that yields a semi functional end product, I will never complain about that.

Download the code off of github and try it out, let me know if you have any comments.

 

Revit Addin

Making a revit addin has been a goal of mine for some time. Yesterday I finally succeeded, complete with ridiculous names, images and tasks completed.

The addin is simple and essentially useless, it assigns walls of a certain type to a workset (Github).

Learning C#

I learned C# mostly through Autodesk's tutorial on the Revit API, found here. C# is different enough from python  that it initimiated me.

A few examples:

  • Declare variable types
  • Cast variables
  • Compile your code etc.

 

Those familiar with C# probably know all of these items, it was fun to learn on my end coming from python. This video guided me in making the ribbon found in the user interface of revit.

You can see in the code below, if you know what a for loop is, you can piece together what these lines are doing, there are just a lot more { } everywhere:

foreach (Workset workset in worksetList)
{
   if (workset.Name.Contains("Concrete"))
       {
           ConcreteWalls = workset.Id.IntegerValue;
       }
}
TaskDialog.Show("Revit", ConcreteWalls.ToString());
// note we have now identified the Concrete shearwall workset

Learning C# sharp was relatively quick. My mind remembered the pains of learning python, but most of the struggles with python were due to never learning about object oriented programming. Classes, objects and everything that went along with an object oriented programming were easy enough to map from python to C#.

Learning C# taught me an important skill, the use of debugger. Why I never used this while coding in Python I will never know. Debuggers allow you to not have to print variables out all the time and appear to have a lot more power that I have not yet unleashed. In the code example above, I was able to watch the debugger stepping through the foreach loop for each workset in the project, and if the workset contains "Concrete" it steps out of the for loop. I look forward to learning more about debugging in both C# and python.

As with any new skill or programming language, intimidation and coding tutorials are the obstacles to progress. Once I actually started coding, debugging, and yes, print screening still, making the addin started to click. 

A negative of C#

To debug the program you have to start and close revit all the time, this is super time consuming! The ability to quickly whip up a revit idea in dynamo and python is really unparalleled. Getting instant feed back on the items in a list of variable is definitely valuable. If you know of a way to get around revit having to fire up each time a change is made to the code, please let me know.

C# Moving forward

C# appears powerful, but will only be reserved for projects that warrant the slow development process. One that comes to my mind is the revit rebar bender. Knowing C# will help me alleviate the limitations that the rebar adaptive component faced. C# is also supported by the ETABs API and I believe you can even have your very own button in the UI of ETABs like Revit. More work in the future...

Time Histories to Response Spectrums

I have inherited an awesome project through the most unfortunate circumstances, the loss of a great engineer moving onto bigger and brighter pastures.

The project is going to be an ASCE 41 non-linear dynamic analysis and has had geotech engineers involved in picking out ground motion that represent similar ground motions that the faults near this building may produce.

Converting from time history to response spectrum can be done in ETABs, but post processing the data is kinda difficult, I can't seem to find a way to programmatically access the response spectrum curves that ETABs produces.

It's also never a bad day when you have to break out the Chopra Book, so I decided to try to code the process.

The Results

 

You can find the code to make plots like these  on my github:
The required data format is a .txt file that is separated with returns and tabs. I have heard not all geotechs provide their time history data in this manner, so if you want to use this, you may have to edit.

Starting into this, I thought this process involved using fourier transforms to transition from the time domain to the frequency domain, I clearly had forgotten my structural dynamics. Reading chapter 5 of Chopra and getting some guidance from fellow engineer Bryant, I was able to recreate the Newmark method outlined in the Chopra book. Adding a whole bunch of for loops later, some awesome plots popped out.

Comparing the BSE-2E plots (the big, rare earthquakes in ASCE 41) to the code generated response spectrum yielded a ~1.3 increase in peak accelerations at the typical plateau of the response spectrum (time histories were produced high accelerations). 

The BSE-1E time history plots were much closer to the code generated BSE-1E response spectrum.

Diaphragm Slicer - ETABs API

The Tool

One feature that I like to use in ETABs is the section cut tool, but it is quite cumbersome to utilize in practice:

Notice how you have to enter (4) valid points to define a section cut... a time consuming process. I love to use this tool to analyze my diaphragm forces, but you are only left with snapshots of your total diaphragm moment and shear. Defining lots of these section cuts along the length of a diaphragm is tedious.

This tool allows structural engineers to select their diaphragm in ETABs, define the slicing direction and define the number of sections to generate, plotting shear and moment at each section cut. It yields awesome plots like the following that you can use to determine diaphragm shear force and moments. The example shown below is that of a (3) sided diaphragm, 40ft long, with a 1kip/ft load applied to edge of the diaphragm.

Diaphragm Shear - Total Shear at shear wall is 40 kips

Diaphragm Moment

The shear graph above makes sense 1 kip/ft * 40ft = 40 kips to be resisted by singular shear wall in the line of the applied force; while on the other hand, the moment in the diaphragm is a little counter intuitive. This graph will make me revisit my other three sided diaphragm post at a later date.

The Goods

You can find the code to make this happen on my github as well as the sample ETABs model utilized to generate all of the plots. The code is written in python and utilizes the ETABs API to do the leg work.

A big thanks to CSI for making this possible in ETABs v19 and above. The new API of ETABs allows engineers to programmatically access any of the available tables in the interactive database. This bit of code makes the section cuts for the diaphragm:


def make_quad_etabs(name_sect,point):
    name = str(name_sect)
    final = []
    for i in range(4):
        if i == 0:
            test = [name, 'Quads', 'All', 'Analysis', 'Default', '', '', '', '0','0','0','','Top or Right or Positive3','1', '1', '1', str(point[0][0]), str(point[0][1]), str(point[0][2]), '1']
            final.append(test)
        elif i == 1:
            test = [name, '', '','', '', '', '', '', '','','','','','', '1', '2', str(point[1][0]), str(point[1][1]), str(point[1][2]), '']
            final.append(test)
        elif i == 2:
            test = [name, '', '','', '', '', '', '', '','','','','','', '1', '3', str(point[2][0]), str(point[2][1]), str(point[2][2]), '']
            final.append(test)
        elif i == 3:
            test = [name, '', '','', '', '', '', '', '','','','','','', '1', '4', str(point[3][0]), str(point[3][1]), str(point[3][2]), '']
            final.append(test)
    return final

etabs_data_sect = []
for i,(etabs_quad,sec_name) in enumerate(zip(global_quad,name)):
    etabs_data_sect.append(make_quad_etabs(sec_name, etabs_quad))

flat_etabs_data = []
for point in etabs_data_sect:
    temp = []
    for data in point:
        for sing_data in data:
            temp.append(sing_data)
    flat_etabs_data.append(temp)
mega_data = []
for point in flat_etabs_data:
    for ind_pnt in point:
        mega_data.append(ind_pnt)

TableKey = 'Section Cut Definitions'
TableVersion = 1
FieldsKeysIncluded = ['Name', 'Defined By', 'Group','Result Type', 'Result Location', 'Location X', 'Location Y', 'Location Z', 'Rotation About Z','Rotation About Y', 'Rotation About X', 'Axis Angle', 'Element Side', 'Number of Quads', 'Quad Number', 'Point Number', 'Quad X', 'Quad Y', 'Quad Z', 'GUID']
NumberRecords = len(flat_etabs_data)
     

y = SapModel.DatabaseTables.SetTableForEditingArray(TableKey, TableVersion, FieldsKeysIncluded, NumberRecords,mega_data)              

FillImport = True
z= SapModel.DatabaseTables.ApplyEditedTables(FillImport)
model_has_run = SapModel.Analyze.RunAnalysis()

To use the code, select your floor and change any of the input that I have noted with the #UI note in the python code. 

Typical user input includes:

  • Selecting the diaphragm you want to slice up, this is done in the ETABs model
  • Select the vector direction you want to your diaphragm to be sliced up, (1,1,0) for example is going up and to the right at a 45 degree angle, in the example below, (1,0,0) tells the program to create vertical slices along the length of the diaphragm, moving from left to right.
    • #UI
      vector = [1,0,0]
  • Define the number of slices you want to make
    • # UI Number of slices to make along the diahragm
      n_cuts = 100
  • Set the hieght, in feet, that your diaphragm exists at in ETABs
    • #UI
      height = 10 
  • Set the starting location of you diaphragm
    • #UI
      ref_pnt=[0,0,0]
  • Hit the run button on your favorite IDE

After hitting run, the program will generate the number of section cuts you told it to create and matplotlib is utilized to plot the results.

In my example code, I had the program generate 100 cuts, the 100th cut corresponds to the section cut 0099 noted below.

A nice plot is generated at the beginning as well so you can see how you are slicing your diaphragm to make sure you made your user input correctly. The red dots below correspond to where your diaphragm exists, while the blue lines corresponds to your section cuts.

Overall the tool works well and I look forward to using it on more complicated diaphragms. Test it out and let me know what you think!

UPDATE: A much more stable version of the slicer is shown here, written in C#.

First Previous 4 5 6 7 8 Next Last

Sidebar

Site Info

  • Latest Comments