Rhino Python: Side Side Side Triangle
The digifab project has been completed, I will post that later. So this script was written when we though we would have to scale the lines running from the outside of the octagon to the center point. The intent of this was to have it implemented with the length match script I wrote about previously. In the end a tabbing script was all that was used, which is a main motivation for writting these tutorials; So I dont feel like I wasted my time.
I wanted a way to take a triangle that was in any orientation and flatten it. This was like trying to do an unroll function with just a single triangle. I could not think of the best way to do it, but decided on trying to recreate the triangle wherever i wanted. I also thought it would be useful to have all the information about the triangle accessible to me. Since we would be doing this to eight triangles and trying to form an octagon, having the angles was something of value. The end logic was using the Side Side Side rule we all learned so long ago. Does it look familiar?
The first thing to remember is that although it is written as cos^-1(), it means the Arc Cosine, not cosine to the power of something, such as cos^3(). Im not sure why this is, but it is. The initial variables on the left of the = represents the angles of the triangle. The letter of the angle is the opposite of the letter of the side.
Lets take a step back and start from the beggining of the code. First we need the lines to be input. We import the UserInput function written before that prompts the user for curves. We then need to send the curves to our Triangle() function and assign the returned value to our curveinfo variable. We then take the curves we selected, and the curve info we got and send it to create our triangle. The functions may seem confusing now but we will go over them soon. This is done in our TriMain() function as:
def TriMain(): curves=ui.TriangleSelect() curveinfo=Triangle(curves) triangle=TriangleCreate(curves,curveinfo,curveinfo) return triangle
The first function we use is called Triangle(). This function takes the list of curves and puts them into their own own variable. This is done like:
crv_1=curves crv_2=curves crv_3=curves
Now we want the lengths of the curves. This is because we need to know which one is the long side and which is the short. We make a variable crv_lengths and we call the SideFind() function passing our three curves to it:
Lets go over the SideFine() before moving on. The SideFind function will take in three curves. We then want to find the length of each curve. We will use the rhino command rs.CurveLength() to get this information as so:
crv_1=rs.CurveLength(crv_1) crv_2=rs.CurveLength(crv_2) crv_3=rs.CurveLength(crv_3)
Now to sort the curves we need to check their lengths. This would be really easy with the min max and median function with the math library, but currently rhino python does not have it implemented. So if you are in a similar situation this is the way I did it, which would only work for three curves, and would be much easier using a linear or bubble sort. This is with alot of if statements, and is pretty basic. It asks if the first curve is larger than the second, and the first is larger than the third, the crv_lg is the first curve. This is repeated for all possibilities, this also only happens if all curves are different.
if crv_1!=crv_2 and crv_1!=crv_3 and crv_2!=crv_3: if crv_1 > crv_2 and crv_1 > crv_3: crv_lg=crv_1 if crv_2 > crv_1 and crv_2 > crv_3: crv_lg=crv_2 if crv_3 > crv_1 and crv_3 > crv_2: crv_lg=crv_3 #find mid crv if crv_1 < crv_2 and crv_1 > crv_3: crv_md=crv_1 if crv_1 > crv_2 and crv_1 < crv_3: crv_md=crv_1 if crv_2 > crv_1 and crv_2 < crv_3: crv_md=crv_2 if crv_2 < crv_1 and crv_2 > crv_3: crv_md=crv_2 if crv_3 > crv_1 and crv_3 < crv_2: crv_md=crv_3 if crv_3 < crv_1 and crv_3 > crv_2: crv_md=crv_3 #find small crv if crv_1 < crv_2 and crv_1 < crv_3: crv_sm=crv_1 if crv_2 < crv_1 and crv_2 < crv_3: crv_sm=crv_2 if crv_3 < crv_1 and crv_3 < crv_2: crv_sm=crv_3
If there are two curves that have the same length we want to find which ones are the same and which is the odd one.
if crv_1==crv_2 or crv_1==crv_3 or crv_2==crv_3: if crv_1==crv_2 and crv_1 < crv_3: crv_sm=crv_1 crv_md=crv_2 crv_lg=crv_3 if crv_2==crv_3 and crv_2 < crv_1: crv_sm=crv_2 crv_md=crv_3 crv_lg=crv_1 if crv_1==crv_2 and crv_1 > crv_3: crv_md=crv_1 crv_lg=crv_2 crv_sm=crv_3 if crv_2==crv_3 and crv_2 > crv_1: crv_md=crv_2 crv_lg=crv_3 crv_sm=crv_1 if crv_1==crv_3 and crv_1 < crv_2: crv_sm=crv_1 crv_md=crv_3 crv_lg=crv_2 if crv_1==crv_3 and crv_1 > crv_2: crv_sm=crv_2 crv_md=crv_1 crv_lg=crv_3 crv_lengths=[crv_sm,crv_md,crv_lg]
Now that we have everything in our crv_lengths array we can return it to the Triangle() function. Our next step in the Triangles() function is to find the angles. While this is the heart of the code, it happens to be the shortest. That is something that comes out of beautiful mathematics. Lets break down the first angle, and the rest are the same, just relative to the size of the angle. Heres what we are trying to code:
For clarity I use the variable name lg_sm_angle. It means that we want to find the angle between the large and the small lengths. It is really the opposite of the medium side, but this is how I like to remember it. The direct translation of this formula becomes:
lg_sm_angle=(math.acos( (sm_pow+lg_pow-md_pow) / ( 2*(crv_sizes*crv_sizes) ) ) )
However, in rhino we are given radians as a return from the acos. To get our information into degrees we need to multiply 180/PI(3.14) . We tack this onto the front and get:
lg_sm_angle=( (180/math.pi) * (math.acos( (sm_pow+lg_pow-md_pow) / ( 2*(crv_sizes*crv_sizes) ) ) ) )
To finish off this function we complete the other angles and put all of our angles into an array and return back to our Triangle() Function.
def AngleFind(crv_sizes): #Make formula look nicer sm_pow=crv_sizes**2 md_pow=crv_sizes**2 lg_pow=crv_sizes**2 lg_sm_angle=( (180/math.pi) * (math.acos( (sm_pow+lg_pow-md_pow) / ( 2*(crv_sizes*crv_sizes) ) ) ) ) lg_md_angle=( (180/math.pi) * (math.acos( (lg_pow+md_pow-sm_pow) / ( 2*(crv_sizes*crv_sizes) ) ) ) ) sm_md_angle=( (180/math.pi) * (math.acos( (md_pow+sm_pow-lg_pow) / ( 2*(crv_sizes*crv_sizes) ) ) ) ) crv_angles=[lg_sm_angle,lg_md_angle,sm_md_angle] return crv_angles
Now in our Triangle() function we take the angles and lengths and put them into our variable curveinfo, the one we saw before in our main function. We then return this to our main function.
def Triangle(curves): crv_1=curves crv_2=curves crv_3=curves crv_lengths=SideFind(crv_1,crv_2,crv_3) crv_angles=AngleFind(crv_lengths) curveinfo=[crv_angles,crv_lengths] return curveinfo
Back at the TriMain() function we are creating a variable triangle that will hold the triangle created by passing our curveinfo to the TriangleCreate function. Due to the curveinfo being an array, we will separate the information to angles and lengths by calling the index of each ie: curveinfo and curveinfo. This is sent along with the curves that were selected.
This part is the longest and most confusing. In TriangleCreate() I needed to take all the curves, know which one is which, and then reconstruct it into a triangle. The first step was putting my small medium and large curve into a separate variable. If you are looking for super inefficient coding practice, you are about to find it. As this was a rushed script I didnt think to long about how to pass the variables around. In doing so, I recall the SideFind() function with my curves to get the Small Medium and Large again. I think I passed them correctly so 0 should be small and 2 should be large, but I am done with the script and it works, so im not going back. If someone figures out a faster way please post it in comments. This is done in the beginning section as so:
def TriangleCreate(curves,crv_angles,crv_lengths): crv_1=curves crv_2=curves crv_3=curves curves=SideFind(crv_1,crv_2,crv_3) crv_S=curves crv_M=curves crv_L=curves
Yet again the code looks to find the order of lengths of the curves, although these are written a bit differently, im sure they can be combined, maybe this is a good test for anyone trying to learn programming. The size of the curves are created this way:
if crv_S!=crv_M and crv_S!=crv_L: if rs.CurveLength(crv_1) == crv_S: crv_sm=crv_1 if rs.CurveLength(crv_2) == crv_S: crv_sm=crv_2 if rs.CurveLength(crv_3) == crv_S: crv_sm=crv_3 if crv_S!=crv_M and crv_M!=crv_L: if rs.CurveLength(crv_1) == crv_M: crv_md=crv_1 if rs.CurveLength(crv_2) == crv_M: crv_md=crv_2 if rs.CurveLength(crv_3) == crv_M: crv_md=crv_3 if crv_S!=crv_L and crv_M!=crv_L: if rs.CurveLength(crv_1) == crv_L: crv_lg=crv_1 if rs.CurveLength(crv_2) == crv_L: crv_lg=crv_2 if rs.CurveLength(crv_3) == crv_L: crv_lg=crv_3 if crv_S==crv_M and crv_S < crv_L: crv_sm=crv_1 crv_md=crv_2 crv_lg=crv_3 if crv_M==crv_L and crv_M < crv_S: crv_sm=crv_2 crv_md=crv_3 crv_lg=crv_1 if crv_S==crv_M and crv_S > crv_L: crv_md=crv_1 crv_lg=crv_2 crv_sm=crv_3 if crv_M==crv_L and crv_M > crv_S: crv_md=crv_2 crv_lg=crv_3 crv_sm=crv_1 if crv_S==crv_L and crv_S < crv_M: crv_sm=crv_1 crv_md=crv_3 crv_lg=crv_2 if crv_S==crv_L and crv_S>crv_M: crv_sm=crv_2 crv_md=crv_1 crv_lg=crv_3
Okay, now that the crazy code for finding sides is out of the way, lets get to the real tricky part. Like most of the functions I have posted about, this one uses a fair amount of rhinoscript, so it would be more difficult to apply this to other python based programs. The first step is to set the rotation axis for me to rotate my lines based on their angle info.
Now I want to take the curves and align them to be coplanar. I do this so i can use my angle information easily without doing all operations at once. I take the start point of one curve and move it to the end point of the other curve.