Calculating Angles in Houdini
So I find myself doing things that involves angles and basic trigonometry way too much, so I figured I’d write up a little document that I could copy and paste from every time I needed to calculate some angles.
Let’s get started.
First, get some points into your scene. If you’re working with existing geometry, great. If you’re working with a blank scene, put down a pig head or something. To make your life easier, put down an Add SOP and then check “Delete Geometry But Keep the Points.” This way you can actually see what’s going on. Or not. Whatever floats your boat.
Now let’s set the groundwork for our calculations. Throw down a null node, since all we’ll be doing is math of our own. It’s time to make a bunch of parameters. To make life easier for ourselves, we’ll call the points Point A, Point B, and Point C. First three parameters: Point A (ch(“pointA”)), Point B (ch(“pointB”)), and Point C (ch(“pointC”)). These are all integer values, since you can’t exactly have half a point in Houdini.
Second three parameters: these are float vector3, and hold the positions of the points we defined in the first three parameters. I call them Point A Location (ch(“pointALoc”)), Point B Location (ch(“pointBLoc”)), and Point C Location (ch(“pointCLoc”)).
Next three parameters: more floats. Let’s call these Line AB Length (ch(“lenAB”)), Line BC Length (ch(“lenBC”)), and Line AC Length (ch(“lenAC”)).
Last three parameters: more floats. You could also use the angle option in the parameter interface, but I have no idea of what the difference is. I used the angle option for funsies. Unless you want to make more. These are called Angle A (ch(“angleA”)), Angle B(ch(“angleB”)), and Angle C (ch(“angleC”)). Names should all be self explanatory if you paid attention in math class.
You now have a bunch of parameters with no values in them. Let’s fill them out. The Point A/B/C parameters are where your points go.
Here’s what goes into the second three parameters (the location of the points):
//pointALocx: //parameter name
point(0,ch("pointA"),"P",0) //parameter value
//pointALocy:
point(0,ch("pointA"),"P",1)
//pointALocz:
point(0,ch("pointA"),"P",2)
//pointBLocx:
point(0,ch("pointB"),"P",0)
//pointBLocy:
point(0,ch("pointB"),"P",1)
//pointBLocz:
point(0,ch("pointB"),"P",2)
//pointCLocx:
point(0,ch("pointC"),"P",0)
//pointCLocy:
point(0,ch("pointC"),"P",1)
//pointCLocz:
point(0,ch("pointC"),"P",2)
Next three parameters (distance between each point):
//lenAC
sqrt((ch("pointALocx")-ch("pointBLocx"))^2+(ch("pointALocy")-ch("pointBLocy"))^2+(ch("pointALocz")-ch("pointBLocz"))^2)
//lenBC
sqrt((ch("pointBLocx")-ch("pointCLocx"))^2+(ch("pointBLocy")-ch("pointCLocy"))^2+(ch("pointBLocz")-ch("pointCLocz"))^2)
//lenAC
sqrt((ch("pointALocx")-ch("pointCLocx"))^2+(ch("pointALocy")-ch("pointCLocy"))^2+(ch("pointALocz")-ch("pointCLocz"))^2)
Last three parameters (angle of each point, if we pretended that the lines made up a triangle):
//angleA
acos((ch("lenAC")^2+ch("lenAB")^2-ch("lenBC")^2) / ( 2* ch("lenAC")+ ch("lenAB")))
//angleB
acos((ch("lenAB")^2+ch("lenBC")^2-ch("lenAC")^2) / ( 2* ch("lenAB")+ ch("lenBC")))
//angleC
180-ch("angleA")-ch("angleB")
In case you’re wondering why the third angle is different, it’s because of the rule of triangles. In any triangle, the three angles will add up to 180 degrees, so if you have two of the three angles, you can subtract them from 180 to get the final angle. Super handy.
There, boom. We’ve calculated the angles that we were looking for. Now for some other fun stuff.
Perhaps you want to actually look at your triangle instead of just imagining it? Pop this into a detail wrangle under your null:
addprim(0,"poly",`ch("../nullName/pointA")`,`ch("../nullName/pointB")`,`ch("../nullName/pointC")`);
//nullName is whatever you've named your null node
Perhaps you want to save your line lengths or your angles as attributes rather than as referenced values. Throw these into a detail wrangle wired into your null:
//line length
@lineABLength = ch("../nonRightTriangle/lenAB");
@lineBCLength = ch("../nonRightTriangle/lenBC");
@lineACLength = ch("../nonRightTriangle/lenAC");//angle length
@angleA = ch("../nonRightTriangle/angleA");
@angleB = ch("../nonRightTriangle/angleB");
@angleC = ch("../nonRightTriangle/angleC");
Hopefully this was helpful. I use this mostly to calculate roof pitch, but I’m sure it has plenty of other uses as well.
Update: VEX has a handy little function called distance() that does exactly what is mentioned above, but this method is still useful for only working with two axis instead of three.
Tangentially related - if you have a vector that you want to convert to an angle (only taking two components into consideration) to feed into a transform sop or something similar elsewhere in your node network when you can’t rely on @N or @orient or any of a number of similar attributes:
//@N = {0.3,0,0.5}
@extracted_rotation_value_in_degrees = degrees(atan(@N.x/@N.z));