Truss script in python

Hello,

Im kinda new to scripting with python and I would like to convert my messy GH script into python. What I want to do is to convert truss geometry below into python script.

I’ve spend some time searching and trying but it seems I got stuck pretty much in the first steps :slight_smile:

import rhinoscriptsyntax as rs

#import start & end point of truss, height of truss, segmentation count
pt1 = rs.coerce3dpoint(start)
pt2 = rs.coerce3dpoint(end)

segments = division

height = -height

#create bottom chord line
line = rs.AddLine(pt1,pt2)

#divide bottom chord
division_points = rs.DivideCurve(line,segments,True,True)

#copy points to defined height of truss
height_point = rs.CreatePoint(0,0,height)
translation_top = pt1-height_point
top_chord = rs.CopyObjects(division_points,translation_top)



a = line
b = division_points
c = top_chord

I have no idea how to proceed and I would like to ask you guys for some help. Anybody willing to share some thougths on how to make such geometry? Ive tried to search for references but with no results. Thank to anybody willing to help!

EDIT:

GH files attached. (Original one and python one)
Truss_Python_v00.gh (6.1 KB)
Truss_v04.gh (28.0 KB)

Dan.

Hi,
Does this help with the next step?

for top_pt, bot_pt in zip(top_chord, division_points):
    # Add the verticales

Hey Graham,

Thank you for the input! Im not sure how to implement this. Do I need to use loop to create mutliple lines between lists of points? If so, Im not sure how. Is this correct?

for top_chord, division_points in zip(top_chord, division_points):
    verticals = rs.AddLine(division_points,top_chord)

it seems to be halsfway there but it makes only one line in the mid.

Hi @Daniel,

This should work:

import rhinoscriptsyntax as rs

#import start & end point of truss, height of truss, segmentation count
pt1 = rs.coerce3dpoint(start)
pt2 = rs.coerce3dpoint(end)

segments = division

#height = -height

#create bottom chord line
line = rs.AddLine(pt1,pt2)


#divide bottom chord
bottom_points = rs.DivideCurve(line, segments, True, True)
top_points = [] # empty list for the top chord points

# Create the top chord points
for i in range(len(bottom_points)):
    x = bottom_points[i].X # new x-coordinate
    y = bottom_points[i].Y + height # new y-coordinate
    z = bottom_points[i].Z # new z-coordinate
    top_point = rs.AddPoint((x, y, z)) # new top point as GUID
    top_points.append(top_point) # save new top point into list

# Create the lines
bottom_chord_members = [] # empty list for the bottom chord line segements
top_chord_members = [] # empty list for the top chord line segements
vertical_members = [] # empty list for the vertical line segements
diagonal_members = [] # empty list for the diagonal line segements

for i in range(len(bottom_points)):
    # Create the vertical line segments
    vertical_line = rs.AddLine(top_points[i], bottom_points[i])
    vertical_members.append(vertical_line)
    if i > 0:
        # Create the bottom and top chord line segments
        bottom_line = rs.AddLine(bottom_points[i-1], bottom_points[i])
        bottom_chord_members.append(bottom_line)
        top_line = rs.AddLine(top_points[i-1], top_points[i])
        top_chord_members.append(top_line)
        
        # Create the diagonal line segments
        if len(bottom_points) % 2 == 1: # case for even amount of horizontal chord segments
            mid_point_index = int(len(bottom_points) / 2)
            # Create top right to bottom left diagonals
            if i <= mid_point_index:
                diagonal_line_a = rs.AddLine(top_points[i-1], bottom_points[i])
                diagonal_members.append(diagonal_line_a)
            # Create bottom left to top right diagonals
            if i >= mid_point_index and i < len(bottom_points) - 1:
                diagonal_line_b = rs.AddLine(bottom_points[i], top_points[i+1])
                diagonal_members.append(diagonal_line_b)
        
        else: # case for odd amount of horizontal chord segments
            mid_point_index_a = int(len(bottom_points) / 2) - 1
            mid_point_index_b = int(len(bottom_points) / 2)
            # Create bottom left to top right diagonals
            if i <= mid_point_index_a + 1:
                diagonal_line_a = rs.AddLine(top_points[i-1], bottom_points[i])
                diagonal_members.append(diagonal_line_a)
            # Create bottom left to top right diagonals
            if i >= mid_point_index_b - 1 and i < len(bottom_points) - 1:
                diagonal_line_b = rs.AddLine(bottom_points[i], top_points[i+1])
                diagonal_members.append(diagonal_line_b)
            

# Outputs
a = top_chord_members
b = bottom_chord_members
c = vertical_members
d = diagonal_members # don't forget to add this component output

Please note that for an even number of bottom and top chord segments, the diagonals meet in V-shape in the middle of the truss. For an odd number of chord segments, they form an X in the middle.

Hi Daniel,
The zip function zips the 2 lists together so as you iterate over the zipped list it gives you pairs of points. You have reused your list names for the first items out of the list so when it reaches the second the lists no longer exist. You have to use different names as in my example.

Hello, Im back!
Firstly, Thank you @Dancergraham & @p1r4t3b0y trying to help me! I used youre hints & codes to make it. It took me quite long since Im begineer with python, but Im back here to share my first script which is finally working as intended! File attached at the end.

I know it is simple code but for me its a huge achievement :smiley:

heres my coda:

__author__ = "Dan"
__version__ = "2019.04.19"

#import libraries
import rhinoscriptsyntax as rs
import math

#start point & end point of truss
pt1 = rs.coerce3dpoint(start)
pt2 = rs.coerce3dpoint(end)

#calculation of span length & division count & segment length = length increment
span = rs.Distance(pt1, pt2)
segments = int(segments)
length_increment = span/segments

#slope calculation from percentage to degrees
slope_perc = slope
slope_ang = math.atan(slope/100)

#calculation of height increment
height_increment = (span/segments)*math.tan(slope_ang)
n = height
height_step = height_increment
length_step = length_increment

#empty variables
a = []
b = []
c = []
d = []
e = []

#slope calculation
slope_perc = slope
slope_ang = math.atan(slope/100)

#generation of bot chord points
j = 0
for i in range(segments+1):
    j = j + length_step
    bot = rs.AddPoint(j-length_step,0,0)
    b.append(bot)

#generation of top chord points
j = 0
for i in range(segments+1):
#    print (n)
    if i <= segments/2:
        n = n + height_step
        j = j + length_step
        top = rs.AddPoint(j - length_step,0,n - height_step)
    else:
        n = n - height_step
        top = rs.AddPoint(j,0,n - height_step)
        j = j + length_step
    a.append(top)

#assigning variables
top_pt = a
bot_pt = b

#generation of diagonals_1
j = 0
for i in range(segments):
    diagonal = rs.AddLine(bot_pt[j],top_pt[j+1])
    j = j + 1
    c.append(diagonal)

#generation of diagonals_2
j = 0
for i in range(segments):
    diagonal = rs.AddLine(bot_pt[j+1],top_pt[j])
    j = j + 1
    d.append(diagonal)

#generation of bottom chord and top chords
bot_ch = rs.AddLine(bot_pt[0],bot_pt[segments])
top_ch = [rs.AddLine(top_pt[0],top_pt[int(segments/2)]), rs.AddLine(top_pt[int(segments/2)],top_pt[segments])]

#generation of verticals
j = 0
for i in range(segments+1):
    vertical = rs.AddLine(bot_pt[j],top_pt[j])
    j = j + 1
    e.append(vertical)

#assigning variables
dia_1 = c
dia_2 = d
vert = e

print(truss_type)

#Warren
if truss_type == 0:
    if segments/2 % 2 == 1:
        dia_11 = dia_2[slice(1, int(segments/2),2)] + dia_1[slice(int(segments/2+1), int(segments),2)]
        dia_12 = dia_1[slice(0, int(segments/2),2)] + dia_2[slice(int(segments/2), int(segments),2)]
    else:
        dia_11 = dia_2[slice(1, int(segments/2),2)] + dia_1[slice(int(segments/2), int(segments),2)]
        dia_12 = dia_1[slice(0, int(segments/2),2)] + dia_2[slice(int(segments/2+1), int(segments),2)]
#Warren Flipped
if truss_type == 1:
    if segments/2 % 2 == 1:
        dia_11 = dia_2[slice(0, int(segments/2),2)] + dia_1[slice(int(segments/2), int(segments),2)]
        dia_12 = dia_1[slice(1, int(segments/2),2)] + dia_2[slice(int(segments/2+1), int(segments),2)]
    else:
        dia_11 = dia_2[slice(0, int(segments/2),2)] + dia_1[slice(int(segments/2+1), int(segments),2)]
        dia_12 = dia_1[slice(1, int(segments/2),2)] + dia_2[slice(int(segments/2), int(segments),2)]
#Howe
if truss_type == 2:
    if edge_diagonals < segments/2:
        dia_11 = dia_1[slice(0, int(edge_diagonals))] + dia_2[slice(int(segments-edge_diagonals), int(segments))]
        dia_12 = dia_1[slice(int(edge_diagonals), int(segments/2))] + dia_2[slice(int(segments/2), int(segments-edge_diagonals))]
    else:
        dia_11 = dia_1[slice(0, int(segments/2))] + dia_2[slice(int(segments/2), int(segments))]
        dia_12 = []
#Pratt
if truss_type == 3:
    if edge_diagonals < segments/2:
        dia_11 = dia_2[slice(0, int(edge_diagonals))] + dia_1[slice(int(segments-edge_diagonals), int(segments))]
        dia_12 = dia_2[slice(int(edge_diagonals), int(segments/2))] + dia_1[slice(int(segments/2), int(segments-edge_diagonals))]
    else:
        dia_11 = dia_2[slice(0, int(segments/2))] + dia_1[slice(int(segments/2), int(segments))]
        dia_12 = []

looks like this:

previously looked like this mess:

This is how it WORKS:

The reason of me sharing it is to get some ideas how to improve the script from you people. If you are interested to share some ideas please hit me up in PM or post in the comments.

Next step is to connect it to the @karamba3d to make optimization with #galapagos. Should be easier part for me.

Dan.

Truss_Python_v02.gh (15.6 KB)

2 Likes

:partying_face:
This makes me very happy :smiley:

Nice, to see your progress, Daniel! You’ve come far!
Please, keep us posted about the further progress with Karamba. The entire project is quite interesting. :slight_smile:

1 Like

Hello,

I thought it would be interesting to share some of my updated scripts for trusses. Main difference is that I changed it to function so you can apply truss between mutliple start-end points.

FILE: Truss_Python_v03.gh (185.8 KB)

Ive been playing with karamba autodesign + galapagos a bit (optimization for weight and limit deflection). This is just a show off of pratt truss, there are other types included:

Next step is to apply scripts + karamba to different warehouse layouts (still work in progress):

Enjoy.

3 Likes

Great, elaborate work, @daniel.c! Makes me happy to see this animated demo. :heart_eyes:

I’m curious, is it technically and computationally possible to evaluate the entire warehouse truss with karamba! or are you limited to optimising one unit/module/cross section at a time?
Don’t get me wrong, for this, your approach is statically totally sound (at least in my humble opinion).

Are you interested in spacial frames/trusses too? I guess that would maybe be the next step?

Hey,

Are you related to structural engineering?

I think it is possible to compute whole warehouse, Im gonna try it and post back about results. That was one of the reasons why I wanted to start scripting with python. Another reason is that I can export grasshopper model into another structural design software to verify results (which I tried several times successfully) .

Regarding structural design, you need to be concise of supports if you design by elements (such as single truss). You can either overestimate or underestimate design just in relation to supports of that element. Reality is in between and it can be evaluated by modelling whole structural system with its stiffness. But to get correct geometrical shape (height, number of segments and so on) of element such as truss in regard to lowest possible self weight you can use design by element easily and it gives perfect results fast.

Regarding spatial structures and large models. Ive tried several grasshopper scripted structures (without python) and I was able to calculate and evaluate them pretty easily with Karamba for instance. Ive also tried some mesh relaxations for roof membranes but its mostly WIP as it is more difficult. See following my examples:

My random example of spatial structure (plan view):

Example of prestressed oval cable roof with larger span cca 80m:

Deflection:

Forces:

Another example of roof:
Deflection


Forces:

No, but I know a little about the basics. I’m an architecture student and work part-time in a mixed practice, where architecture and engineering are done in house.

Cool, I’m keen on hearing about the results. Are you also planning to implement the testing, computing, calculating of forces, material deformations, etc. yourself in Python?

Yes, I can get behind that!

Thanks for posting the example!