0
$\begingroup$

I am trying to find a way to rotate a vector with python so that the angle between the z axis and this vector is exactly _x° from the right ortho view and the angle between the z axis and this vector is exactly _y° from the front ortho view.

[![Front ortho][1]][1][![Right ortho][2]][2]

Using the XYZ Euler rotation system dont look a good way to perform such a rotation since an y rotation will modify the initial x rotation, and rotating around the local y axis after the first x rotation will keep a valid x rotation but give an invalid _y angle from the global point of view.

What i am trying to find is an Axis angle, (or quaternion) were the rotation axis is a combination of global x and y vector ([1,0,0] and [0,1,0]) and a corresponding angle.

In this case if _x==_y , the resulting rotation vector will be [0.5,0.5,0.0] and the corresponding angle will be a little more than _x. But i have some difficulties to figure out the maths to find this vector and this angle for any _x and y between 0 and 89.99° (90 would be a problem for sure), or a built in function in Blender that could give this exact result.

NB: The example given here is based on blender global axis but my problem is actualy to rotate around any two perpendicular vectors (local axis of an object). A basic solution in global referential would be enough to find the solution to my initial problem.

$\endgroup$
  • $\begingroup$ I asked a pretty similar question once: plus.google.com/102817843024907935934/posts/HLxXBU7x6fu You have to claculate the rotation on the 3rd axis to compensate the other two to your desired angle $\endgroup$ – knekke Jun 20 '17 at 11:36
  • $\begingroup$ Wouldn't moving the origin to the base fix this problem? $\endgroup$ – Aoradon Jun 20 '17 at 12:29
  • $\begingroup$ Aoradon: Moving the origin wont solve the problem since i'm stritcly talking about vectors, and a vector has no origin. The origin problem when making rotations will only have an impact on the translation part of a transformation . I dont care about that i only need a rotation ;) knekke: i will take a look it can be interrestiong but i'm not sure it's the same problem. I '(maybe) managed to find a solution for now, by computing two vectors, one for the x rotation, oneother for the y rotation and combining them: xVector.x = yVector.x*xVector.z/yVector.z return xVector.normalized() $\endgroup$ – Pyros Jun 20 '17 at 13:03
  • $\begingroup$ yes, it's a slightly different problem. I might have a solution to your problem in a couple of minutes $\endgroup$ – knekke Jun 20 '17 at 13:25
  • $\begingroup$ maybe not.... I have to leave now :/ $\endgroup$ – knekke Jun 20 '17 at 13:55
2
$\begingroup$

The angle between a vector and the Z axis and a vector in the right ortho view appears to be such that tan(θ)=y/z. The angle between a vector and the Z axis in the front ortho view appears to be such that tan(φ)=x/z.

As long as θ and φ aren't going to be π/2 you can pretty easily set z=1 and then y=tan(θ) and x=tan(θ) . If the π/2 is a possibility you'll want to have code to check for that case and use an alternate formula like tan(π/2-θ) = z/y and similar for φ.

So after following that procedure we have an <x,y,z>, but since you talk about rotations, I assume you have an object with some feature you want to align with this vector.

To accomplish this requires an understanding of quaternions, or you just copy the code from http://web.purplefrog.com/~thoth/blender/python-cookbook/rotate-to-match.html

import bpy

from mathutils import *
from math import *

def compute_rotation(src_vec, dest_vec):

    a = src_vec.normalized()
    b = dest_vec.normalized()

    c = a.cross(b)

    theta = asin(c.magnitude)

    if (c.magnitude>0):
        d = c.normalized()
        st2 = sin(theta/2)
        q = Quaternion( [cos(theta/2), st2*d.x, st2*d.y, st2*d.z] )
    else:
        q = Quaternion( [1,0,0,0] )

    return q

obj = bpy.context.active_object

obj.rotation_quaternion = compute_rotation(Vector([1,1,1]), Vector([1,0,0]))

The src_vec is a vector on the object (it might be the X, Y or Z axis, or some other important feature like an antenna) and dest_vec is the world space vector we want that source vector to be rotated to match.

Another thing to consider is that there are many orientations that are compatible with dest_vec = q*src_vec (just imagine spinning the object around src_vec), but the q calculated by this procedure will have the minimum θ.

To see how messy things can get, read http://web.purplefrog.com/~thoth/blender/python-cookbook/narratives/animate-random-spin.html

$\endgroup$
  • $\begingroup$ Thanks for this answer. Your solution is close to mine but you explanation is more accurate. Actually i use this vector as a z component of a base, and i use them to create à transformation matrix. I can easily find two vectors for x and y based on the z and my other contraints, then i only have to create a transformation matrix with mathutils.Matrix([x,y,z]).transposed().to_4x4(). $\endgroup$ – Pyros Jun 22 '17 at 7:37
  • $\begingroup$ Creating a transformation matrix reminds me of blender.stackexchange.com/a/30809/660 $\endgroup$ – Mutant Bob Jun 22 '17 at 20:50
1
$\begingroup$

I think a found a solution that looks to work pretty well: I compute two vectors, on for the y rotation and onother rotation. Then I combine them this way:

xVector = V.copy()
xVector.rotate(xRotation)
yVector = V.copy()
yVector.rotate(yRotation)
xVector.x = yVector.x*xVector.z/yVector.z

I'm not sure this solution is correct but the results looks good. The ratio y/z will define the angle between z axis and the vector from x view, and The ratio y/z will define the angle between z axis and the vector from y view. If i define an x value in the x vector with the same x/z ratio as in the y vector, i now have my two angles defined. Is it correct?

$\endgroup$
0
$\begingroup$

Adding my answer to the mix:

from math import *
import mathutils

# Define the angles
x_angle = 20
y_angle = 30


# Get the directions
xdir_x = cos(radians(90-x_angle))
xdir_z = sin(radians(90-x_angle))
xz_vec = mathutils.Vector((xdir_x, 0, xdir_z))

ydir_y = cos(radians(90-y_angle))
ydir_z = sin(radians(90-y_angle))
yz_vec = mathutils.Vector((0, ydir_y, ydir_z))


# Get the normals
xz_normal = xz_vec.cross((0,1,0))
yz_normal = yz_vec.cross((1,0,0))


# The cross product of the normals gives us the final direction vector
dir_vector = -xz_normal.cross(yz_normal)

# And the euler for this vector
rotation = dir_vector.to_track_quat('Z', 'X').to_euler()


# Print the results
print('Direction Vector: %s' %dir_vector)
print('Rotation: %.2f, %.2f, %.2f' %tuple(degrees(a) for a in rotation))
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.