Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a function to rotate vector with a quaternion #195

Closed
bakwc opened this issue Feb 18, 2022 · 6 comments
Closed

Add a function to rotate vector with a quaternion #195

bakwc opened this issue Feb 18, 2022 · 6 comments

Comments

@bakwc
Copy link

bakwc commented Feb 18, 2022

I want to rotate a vector with a quaternion. And currently there is no builtin function for that. I suggest to add a builtin function to make a rotation with a single function, and not a 4-step operations.

@moble
Copy link
Owner

moble commented Feb 18, 2022

def rotate_vectors(R, v, axis=-1):
"""Rotate vectors by given quaternions
This function is for the case where each quaternion (possibly the only input
quaternion) is used to rotate multiple vectors. If each quaternion is only
rotating a single vector, it is more efficient to use the standard formula
vprime = R * v * R.conjugate()
(Note that `from_vector_part` and `as_vector_part` may be helpful.)
Parameters
----------
R : quaternion array
Quaternions by which to rotate the input vectors
v : float array
Three-vectors to be rotated.
axis : int
Axis of the `v` array to use as the vector dimension. This
axis of `v` must have length 3.
Returns
-------
vprime : float array
The rotated vectors. This array has shape R.shape+v.shape.
Notes
-----
For simplicity, this function converts the input quaternion(s) to matrix form,
and rotates the input vector(s) by the usual matrix multiplication. As noted
above, if each input quaternion is only used to rotate a single vector, this is
not the most efficient approach. The simple formula shown above is faster than
this function, though it should be noted that the most efficient approach (in
terms of operation counts) is to use the formula
v' = v + 2 * r x (s * v + r x v) / m
where x represents the cross product, s and r are the scalar and vector parts
of the quaternion, respectively, and m is the sum of the squares of the
components of the quaternion. If you are looping over a very large number of
quaternions, and just rotating a single vector each time, you might want to
implement that alternative algorithm using numba (or something that doesn't use
python).
"""
R = np.asarray(R, dtype=np.quaternion)
v = np.asarray(v, dtype=float)
if v.ndim < 1 or 3 not in v.shape:
raise ValueError("Input `v` does not have at least one dimension of length 3")
if v.shape[axis] != 3:
raise ValueError("Input `v` axis {0} has length {1}, not 3.".format(axis, v.shape[axis]))
m = as_rotation_matrix(R)
tensordot_axis = m.ndim-2
final_axis = tensordot_axis + (axis % v.ndim)
return np.moveaxis(
np.tensordot(m, v, axes=(-1, axis)),
tensordot_axis, final_axis
)

@moble moble closed this as completed Feb 18, 2022
@bakwc
Copy link
Author

bakwc commented Feb 18, 2022

Oh, there is already exists this function. It should be probably added to readme. Sorry, i should read docs more carefully.

@moble
Copy link
Owner

moble commented Feb 18, 2022

README != docs

@bakwc
Copy link
Author

bakwc commented Feb 18, 2022

Ok, thank you. Cool library!

@MoffKalast
Copy link

MoffKalast commented Jun 2, 2022

For anyone else that finds this and is wondering why the hell that workaround returns a quaternion array, this should be the actual code:

# v is a numpy array of [x, y, z]
def quaternion_mult(q, v):
    vout = (q * v * q.conjugate())
    return np.array([vout[0].w, vout[1].w, vout[2].w])

I think it would be helpful to add something of the sort to the object class, so that one can just do quat_instance.mult(vector) and get a vector back as is fairly standard in most game engine transform libraries, instead of doing it this unintuitive roundabout way.

Edit: Nevermind, this doesn't seem to be working. I'm open to suggestions.

@moble
Copy link
Owner

moble commented Jun 3, 2022

A. Quaternion multiplication is a thing, and it's not the same as rotation.
B. What workaround are you talking about?
C. The function quaternion.rotate_vectors already exists, and does what you're trying to do — including returning a plain numpy array of vectors rather than quaternions — but does it correctly and usually far more efficiently.
D. A term like vout[2].w takes the scalar part of the 2 element of vout. I have no idea what you're trying to do here, but I don't think that's what you want.
E. Here's a suggestion: Read what's written above and try it. And maybe don't be a jerk.

Repository owner locked and limited conversation to collaborators Jun 3, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants