Rotating Instanced tube to point at the next tube instanced

Hi!

I have a table of data points for segments of a tree branch. Each point has position data for itself and I’ve been able to recreate the tree by instancing tubes at each point, finding the length with vector math for scale, and using the ROTATE TO parameter for rotation.

The problem is I really need to know the rotation in degrees for each node and not have touch do it for me.

So if I have two points in world space, and I need an instanced object to “point” from one point to the other, how can I find the X,Y,Z angles for rotation. I’ve tried this and the results are not great:
dir = target.vector - instance.vector)
angleToX = (math.acos(dir.x / length) * 180)/ math.pi
angleToY = (math.acos(dir.y / length) * 180)/ math.pi
angleToZ = (math.acos(dir.z / length) * 180)/ math.pi

on 2 axis:
(math.atan2(op(‘pointA’)[‘ty0’]-op(‘pointB’)[‘ty1’],op(‘pointA’)[‘tx0’]-op(‘pointB’)[‘tx1’]))*180/math.pi


arctan.tox (1.1 KB)

1 Like

This topic gets quite a bit more complicated when moving to 3 dimensions, and euler angles have some hidden edge cases that cause the general formula to break down.


My suggestion if it’s applicable to your use case, is to leverage Derivative’s matrix, vector, and quaternion classes. they are truly amazing.

(NOTE: transform CHOP apparently does a lot of this now too, I haven’t gotten around to trying it out so can’t comment on the workflow there)

From the wiki pages:
q = tdu.Quaternion(tdu.Vector(1, 0, 0), tdu.Vector(0, 1, 0))
r = q.eulerAngles(order='xyz')

where r == [ rx , ry , rz ] in degrees

Ah damn, I was worried you were going to say that. This project requires plenty of finding vectors in one world space, using matrix transformations to bring it back down to object or joint space and back up again. I was hoping this was going to be an easier step.

Any chance someone could confirm exactly what process is behind the Rotate to function in Instance 2 tab? I would like to make sure I’m not heading in a different direction than that

1 Like

are you unable to use python in your setup? I think you should be able to replicate the rotate to functionality in a similar way using quaternions.

Let me see if I can put a simple example together

1 Like

I can definitely use python! Just lost on the functions needed.

Here’s an example:
EulerAnglesBetweenVectors_usingQuaternions.1.toe (5.1 KB)

Here’s the code:

inChopA = scriptOp.inputs[0]
inChopB = scriptOp.inputs[1]

x1 = float(inChopA['tx'])
y1 = float(inChopA['ty'])
z1 = float(inChopA['tz'])
x2 = float(inChopB['tx'])
y2 = float(inChopB['ty'])
z2 = float(inChopB['tz'])


A = tdu.Vector(x1,y1,z1)
B = tdu.Vector(x2,y2,z2)

A.normalize()
B.normalize()

q = tdu.Quaternion(A,B)
eulerAngles = q.eulerAngles(order='xyz')

rx_ = scriptOp.appendChan('rx')
ry_ = scriptOp.appendChan('ry')
rz_ = scriptOp.appendChan('rz')

rx_[0] = eulerAngles[0]
ry_[0] = eulerAngles[1]
rz_[0] = eulerAngles[2]

I’ve implemented that code, and validated that my results are getting the same as it would in yours, but around ~98% of the rotation amounts are between -2 and 2 degrees in the x y and z. So I have a bunch of straight flat lines.

I used yours to point a tube at the moving point and I “think” its doing the right thing. At least when the lfos are moving the results can go over 2 degrees.

So I’m not sure why if I input two distinct points in space, normalized, the results could always end up so tiny.

Is it possible to post a simplified example illustrating the issue you’re having related to -2:2 degree rotations?

The only thing I can think of off the top of my head is radians to degrees or vice versa being left out of the equation somewhere.

Ha not fantastic simplification, but here is a shot! Currently I’m working in dats, but the real project will work off chops or tops if I can figure it out.

The nested for loops are reading the raw tree data and building a simple table of points to instance tubes.

-realizing…you may need the initial file. I think I got rid of that requirement and saved the data in the tox, but not sure

simplified_tree_example.2.toe (1.7 MB)

ok you were close!
So the way the quaternion class should be used in this case is a tiny bit different -
(you prob already know most of this, but just expanding on it for any others)

You were feeding the quat class point A, and point B, which defines a line from A to B, but what the quat class actually needed was two vectors first for your initial rotation of your tube, and then a second vector for the new direction the tube should face.

For the first Vector (A)
Since your tube points down z in the positive axis, this vector should be (0,0,1)
A = tdu.Vector( 0 , 0 , 1 )

For the second Vector (B)
you want a vector that points from point to target, which you had already in there for calculating the distance:
B = (target - point)

Then, you want to normalize both vectors as you did
A.normalize()
B.normalize()

Next everything you had was exactly right:
q = tdu.Quaternion( A , B )
eulerAngles = q.eulerAngles(order='xyz')

From there, the instances point in the right direction:

simplified_tree_example.4.toe (1.7 MB)

1 Like

Wow, that worked! Thank you so much…I’ve been working on different angles of that problem for well over 2 weeks…just avoiding actually finishing it lol

I feel like there’s a pun in there somewhere about angles haha - no problem! happy to help.
Shout out to Derivative for putting these fundamentals in there, I’ve really come to rely on them - the math glue that holds a lot of other stuff together in my projects.