Let's say that I have two vectors around a unit sphere, like the black vectors in the figure. I would like to apply the same rotation on another vector that is always [1, 0, 0] (blue one in the figure).
I already got quite a lot of code for doing that, however it's not perfectly. working.
It seems to work when the distance between the two vector is <45 degree, for example:
which looks ok.
However, with another pair of vectors, I get:
Which is wrong. Why do I think it's wrong? I would think that applying the rotation between the two black vectors to the blue vector would result in a new rotation that is on the same latitude. However, this is clearly not the case
This is the code I am using. Sorry for the length, but most of it it's used for plotting: (you need the pyquaternion library: http://kieranwynn.github.io/pyquaternion/ )
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
from pyquaternion import Quaternion
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
plt.close('all')
fig = plt.figure(figsize=(10, 10))
ax = fig.gca(projection='3d')
# draw sphere
u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]
x = np.cos(u)*np.sin(v)
y = np.sin(u)*np.sin(v)
z = np.cos(v)
ax.plot_wireframe(x, y, z, color=[1, 0, 0, 0.2])
# draw a point
ax.scatter([0], [0], [0], color="g", s=100)
# draw a vector
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import proj3d
class Arrow3D(FancyArrowPatch):
def __init__(self, xs, ys, zs, *args, **kwargs):
FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
self._verts3d = xs, ys, zs
def draw(self, renderer):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
FancyArrowPatch.draw(self, renderer)
def norm_v(v):
return v / np.linalg.norm(v)
def add_norm_vector(u, col="k", norm=True):
if norm:
u = norm_v(u)
vh = Arrow3D([0, u[0]], [0, u[1]], [0, u[2]], mutation_scale=20,
lw=1, arrowstyle="-|>", color=col)
ax.add_artist(vh)
return vh
def rotation_v_respect_to_u(v, u):
rot = Quaternion(axis=np.cross(v, u), angle=np.arccos(np.dot(u, v))).normalised
vv = rot.rotate([1, 0, 0])
return vv
## THIS ONE IS CORRECT
a=[0, 1, 0]
b = [-0.671868, 2.399499 , -0.04294401]
distance = rotation_v_respect_to_u(a, b)
add_norm_vector([1, 0, 0], 'b')
add_norm_vector(a)
add_norm_vector(b)
add_norm_vector(distance, 'r')
## THIS ONE IS NOT CORRECT!
a=[0, 1, 0]
b = [-3.7956853 , -3.90564173, -1.713661]
distance = rotation_v_respect_to_u(a, b)
add_norm_vector([1, 0, 0], 'b')
add_norm_vector(a)
add_norm_vector(b)
add_norm_vector(distance, 'r')
The important bit is in the function rotation_v_respect_to_u
. The formula is taken from this post: Finding quaternion representing the rotation from one vector to another