Align object to vertex with python / track_to

Hi everyone,

Hope you can help me on this one. I have made two example files:

The first one is showing what i want to achieve.
It is done with two constraints:

  • copying the location
  • copying the orientation of the mesh with a neighboring point

If you rotate the mesh of object_a in edit-mode, object_b will follow.

The second one is showing my approach in python.
copying the location is easy of course.
But how can i copy the orientation?

I have already tried a lot of different approaches.
Most of my trials are ending up in only copying two directions of course.
(If i copy the normal of the vertex in the middle for example.
So i guess i will need the neighboring point as little helper)

Is there an easy approach? Maybe the question is:
How is the build in track_to, available in the gui under constraints, exactly working?
It looks like a very clever solution.

Best,
Chris

Attachments

track-to_scripting.blend (455 KB)track-to_example.blend (445 KB)

Of course i have searched in addvance. This post for example:

But it is rotating the object in different directions.
Do i have to square-root the quaternion or something else complicated?

import bpy
import mathutils

shortcut to the objects

a = bpy.data.objects[“object_a”]
b = bpy.data.objects[“object_b”]

copy the location of object_b to the vertex in the middle of object_a

b.location = a.data.vertices[3].co

create vector for direction

direction = mathutils.Vector(a.data.vertices[3].co - a.data.vertices[0].co)

#apply rotation
bpy.context.object.rotation_mode = ‘QUATERNION’
b.rotation_quaternion = direction.to_track_quat(‘Z’,‘Y’)

For everyone interested in one solution. This is how i solved it now:

  • Calculate z-rotation with atan2 via the mid-point and vertex-0
  • Calculate y-rotation with atan2 via the mid-point and vertex-0
  • Calculate x-rotation with atan2 via vertex-1 and vertex 2
  • apply rotation to the object

Guess that there are much better ways to achieve it.
It’s also not precise in x-rotation. But it works for me!

import bpy
from math import atan2

function to evaluate distance in 2d

def distance (ax, ay, bx, by):
dist = ((bx - ax)**2 + (by - ay)**2)**0.5
return dist

shortcut to the objects

a = bpy.data.objects[“object_a”]
b = bpy.data.objects[“object_b”]

copy the location of object_b to the vertex in the middle of object_a

b.location = a.data.vertices[3].co

z-rotation

atan2_1 = a.data.vertices[3].co[0] - a.data.vertices[0].co[0]
atan2_2 = a.data.vertices[3].co[1] - a.data.vertices[0].co[1]
z_rot = atan2(atan2_2, atan2_1)

y-rotation

atan2_1 = distance(a.data.vertices[0].co[0], a.data.vertices[0].co[1], a.data.vertices[3].co[0], a.data.vertices[3].co[1])
atan2_2 = a.data.vertices[3].co[2] - a.data.vertices[0].co[2]
y_rot = atan2(atan2_2, atan2_1)

x-rotation

atan2_1 = distance(a.data.vertices[1].co[0], a.data.vertices[1].co[1], a.data.vertices[2].co[0], a.data.vertices[2].co[1])
atan2_2 = a.data.vertices[2].co[2] - a.data.vertices[1].co[2]
x_rot = atan2(atan2_2, atan2_1)

b.rotation_euler[2] = z_rot
b.rotation_euler[1] = y_rot *(-1)
b.rotation_euler[0] = x_rot

Attachments

track-to_solution.blend (463 KB)

Maybe it would work without the condition but at least it’s precise :wink:



import bpy
import mathutils
from math import atan2, pi


# shortcut
obj = bpy.data.objects


# function to evaluate distance
def distance(ax,ay,az, bx,by,bz):
    dist = ((bx-ax)**2 + (by-ay)**2 + (bz-az)**2)**0.5
    print(dist)
    return dist


# function to apply triangle to hexa
def track_to(a, b, mid, first):
    # apply location
    b.location = a.data.vertices[mid].co


    # get direction and angle
    direction = a.data.vertices[mid].normal
    angle = a.data.vertices[mid].co - a.data.vertices[first].co


    # apply direction
    b.rotation_mode = 'QUATERNION'
    b.rotation_quaternion = direction.to_track_quat('X','Y')


    # update scene because of matrix_local
    bpy.data.scenes[0].update()
    angle_local = angle * b.matrix_local
    rotation = atan2(angle_local[2], angle_local[1])


    # apply rotation
    b.rotation_mode = 'XYZ'
    b.rotation_euler[0] = rotation


    # update scene
    bpy.data.scenes[0].update()


    # correction
    a_0 = a.data.vertices[first].co
    b_0 = b.matrix_world * b.data.vertices[0].co


    if distance(a_0[0], a_0[1], a_0[2], b_0[0], b_0[1], b_0[2]) > 0.5:
        print("distance higher than 0.5, going to fix this")
        b.rotation_mode = 'XYZ'
        b.rotation_euler[0] = rotation + pi


track_to(obj["object_a"], obj["object_b"], 3, 0)


Attachments

track-to_solution-02.blend (485 KB)

Hi chris,

couple of things, you can use vector math for the distance, and angle

v1 and v2 are vectors


distance = (v1 - v2).length
angle_between_vectors = v1.angle(v2)

Also, pays to get into the habit of working with context. context.scene rather than bpy.data.scenes[0]

Hi batFINGER,

thank you very much for your reply!
I havent known this way to do it.
It’s much clearer.

Best,
Chris