So I'm very new to quaternions, but I understand the basics of how to manipulate stuff with them. What I'm currently trying to do is compare a known quaternion to two absolute points in space. I'm hoping what I can do is simply convert the points into a second quaternion, giving me an easy way to compare the two.
What I've done so far is to turn the two points into a unit vector. From there I was hoping I could directly plug in the i j k into the imaginary portion of the quaternion with a scalar of zero. From there I could multiply one quaternion by the other's conjugate, resulting in a third quaternion. This third quaternion could be converted into an axis angle, giving me the degree by which the original two quaternions differ by.
Is this thought process correct? So it should just be [ 0 i j k ]. I may need to normalize the quaternion afterwards, but I'm not sure about that.
I have a bad feeling that it's not a direct mapping from a vector to a quaternion. I tried looking at converting the unit vector to an axis angle, but I'm not sure this would work, since I don't know what angle to give as an input.
Notation
Quaternions are defined in a four-space with bases {1, i, j, k}. Hamilton famously carved the fundamental relationship into the stone of the Brougham Bridge in Dublin:
i2 = j2 = k2 = i j k = -1.
There are many equivalent quaternion parameterizations, but here I'll use a {scalar, vector} form.
1.) A = {a0, a} and B = {b0, b}, where A and B are quaternions, a0 and b0 are scalars, and a and b are three-vectors.
2.) X = { 0, x } is a vector quaternion.
3.) The (non-commutative) quaternion product derives directly from the properties of i, j and k above, A⊗B = {a0 b0 - a.b, a0 b + b0 a + a x b}
4.) The quaternion conjugate is A* = {a0, - a}
5.) The conjugate of a quaternion product is the product of the conjugates in reverse order.
(A⊗B)* = B*⊗A*
6.) The conjugate of a vector quaternion is its negative. X* = {0, -x } = -X
7.) The quaternion norm is |A| = √(A⊗A*) = √( a0² + a.a )
8.) A unit quaternion is one that has a norm of 1.
9.) A unit three-vector x = {x1, x2, x3} with x . x = 1 is expressible as a unit vector quaternion X = { 0, x }, |X| = 1.
10.) The spherical rotation of a quaternion vector X by an angle θ about a unit vector axis n is Q⊗X⊗Q*,
where Q is the quaternion {cos(θ/2), sin(θ/2) n }. Note that |Q| = 1.
Notice the form of the quaternion vector product. Given vector quaternions X1 = { 0, x1 ) and X2 = { 0, x2 }, the quaternion product is X2⊗X1* = { x1.x2, x1 × x2 }. The quaternion reunites the dot product as the scalar part and cross product as the vector part, divorced over a hundred years ago. Neither of these products is invertible, but the quaternion is in the way described below.
Inversion
Find the spherical transform quaternion Q12 to rotate vector X1 to align with vector X2.
From above
X2 = Q12⊗X1⊗Q12*
Multiplying both sides by X1*,
X2⊗X1* = Q12⊗X1⊗(Q12*⊗X1*)
Remember that the rotation axis n derives from the cross product x1×x2, so n . x1 = 0. and Q*⊗X* = (X⊗Q)* = X*⊗Q, leaving
X2⊗X1* = Q12⊗X1⊗X1*⊗Q12 = Q12⊗Q12
So the quaternion transform can be solved directly as
Q12 = √(X2⊗X1*)
You're on your own for the quaternion square root. There are lots of ways to do it, and the best will depend on your application, considering speed and stability.
--hth,
Fred Klingener
Related
I have a position in space called X1. X1 has a velocity called V1. I need to construct an orthogonal plane perpendicular to the velocity vector. The origin of the plane is X1.
I need to turn the two edges from the plane into two vectors, E1 and E2. The edges connect at the origin. So the three vectors form an axis.
I'm using the GLM library for the vector mathematics.
One way to create a frame from a vector is to use Householder transformations. This may seem complicated but the code is in quite short, at least as efficient as using cross products, and less prone to rounding error. Moreover exactly the same idea works in any number of dimensions.
The ideas is, given a vector v, find a Householder transformation that maps v to a multiple of (1,0,0), and then apply the inverse of this to (0,1,0) and (0,0,1) to get the other frame vectors. Since a Householder transformation is it's own inverse, and since they are simple to apply, the resulting code is fairly efficient. Below is C code that I use:
static void make_frame( const double* v, double* f)
{
double lv = hypot( hypot( v[0], v[1]), v[2]); // length of v
double s = v[0] > 0.0 ? -1.0 : 1.0;
double h[3] = { v[0] - s*lv, v[1], v[2]}; // householder vector for Q
double a = 1.0/(lv*(lv + fabs( v[0]))); // == 2/(h'*h)
double b;
// first frame vector is v normalised
b = 1.0/lv;
f[3*0+0] = b*v[0]; f[3*0+1] = b*v[1]; f[3*0+2] = b*v[2];
// compute other frame vectors by applying Q to (0,1,0) and (0,0,1)
b = -v[1]*a;
f[3*1+0] = b*h[0]; f[3*1+1] = 1.0 + b*h[1]; f[3*1+2] = b*h[2];
b = -v[2]*a;
f[3*2+0] = h[0]*b; f[3*2+1] = b*h[1]; f[3*2+2] = 1.0 + b*h[2];
}
In general you can define a plane in 3D using four numbers, e.g., Ax+By+Cz=D. You can think of the triple of numbers (A,B,C) as a vector that sticks out perpendicularly to the plane (called the normal vector).
The normal vector n = (A,B,C) only defines the orientation of the plane, so depending on the choice of the constant D you get planes at different distance from the origin.
If I understand your question correctly, the plane you're looking for has normal vector (A,B,C) = V1 and the constant D is obtained using the dot product: D = (A,B,C) . X1, i.e., D = AX1.x + BX1.y + C*X1.z.
Note you can also obtain the same result using the geometric equation of a plane n . ((x,y,z) - p0) = 0, where p0 is some point on the plane, which in your case is V1 . ( (x,y,z) - X1) = 0.
I have 3D-vertices that are read from a model which up-direction is defined by unit-vector u. I need to translate all those vertex points to another space which up-direction is defined by unit-vector v.
I know that if original up direction is (0,1,0) and target is (1,0,0), I can use rotation matrix somewhat like this for all the points:
x' = x*cos(-90) - y*sin(-90) = y
y' = x*sin(-90) + y*cos(-90) = -x
z' = z
where -90 is the angle from (0,1,0) to (1,0,0).
...but so far my tryouts to generalize this approach for the case mentioned in the begining have proven futile.
Any suggestions how to solve this?
To generalize this, you can use an angle/axis rotation specification where the rotation angle is given by θ = cos-1(u∙v / |u||v|) and the axis is given by r = (u/|u|)×(v/|v|) where ∙ is the dot product, × is the cross product and | | is the Euclidean (L2) norm. You can either use a quaternion formulation or a rotation matrix to actually generate the linear transformation.
Why this works: The dot product of two unit length vectors is the cosine of the angle between those two vectors. The cross product of two unit length 3D vectors is one of two unit length 3D vectors orthogonal (perpendicular) to the other two. The two vectors differ only in their sign such that u×v = -v×u, so the formulation above may have a sign issue if I got the order wrong in the cross product.
if your up vector is [ Ux, Uy, 0 ]:
x' = Ux*x + Uy*y
y' = Uy*x - Ux*y
z' = z
You could generalize this better and cover all arbitrary angles if you used Matrices. For example:
[ Ux, Uy, 0,
Uy,-Ux, 0,
0, 0, 1 ]
and simply multiply the vector by the matrix.
Or use three vectors for your orientation Up Right and Back then your matrix is:
[ Rx, Ry, Rz,
Ux, Uy, Uz,
Bx, By, Bz ]
and multiplying the vector by the matrix will be like this:
x' = x*Rx + y*Ry + z*Rz
y' = x*Ux + y*Uy + z*Uz
z' = x*Bx + y*By + z*Bz
In your particular case you have R=[1,0,0], U=[0,1,0] and B=[0,0,1] which you then reoriented to R=[0,-1,0], U=[1,0,0] and B=[0,0,1]
What is a formula to get a three dimensional vector B lying on the plane perpendicular to a vector A?
That is, given a vector A, what is a formula f(angle,modulus) which gives a vector that is perpendicular to A, with said modulus and rotated through an angle?
If the two vectors are perpendicular then their dot product is zero.
So: v1(x1, y1, z1), v2(x2, y2, z2).
=> x1 * x2 + y1 * y2 + z1 * z2 = 0
You know (x1, y1, z1). Put arbitrary x2 andy2 and you will receive the corresponding z2:
z1 * z2 = -x1 * x2 - y1 * y2
=> z2 = (-x1 * x2 - y1 * y2) / z1
Be aware if z1 is 0. Then you are in the plane.
function (a,b,c)
{
return (-b,a,0)
}
But this answer is not numerical stable when a,b are close to 0.
To avoid that case, use:
function (a,b,c)
{
return c<a ? (b,-a,0) : (0,-c,b)
}
The above answer is numerical stable, because in case c < a then max(a,b) = max(a,b,c), then vector(b,-a,0).length() > max(a,b) = max(a,b,c) , and since max(a,b,c) should not be close to zero, so is the vector. The c > a case is similar.
Calculate the cross product AxC with another vector C which is not collinear with A.
There are many possible directions in the plane perpendicular to A. If you don't really care, which one to pick, just create an arbitrary vector C not collinear with A:
if (A2 != 0 || A3 != 0)
C = (1, 0, 0);
else
C = (0, 1, 0);
B = A x C;
I believe that this should produce an arbitrary vector that is perpendicular to the given vector vec while remaining numerically stable regardless of the angle of vec (assuming that the magnitude of vec is not close to zero). Assume that Vec3D is a three dimensional vector of arbitrary numerical type.
Vec3D arbitrary_orthogonal(Vec3D vec)
{
bool b0 = (vec[0] < vec[1]) && (vec[0] < vec[2]);
bool b1 = (vec[1] <= vec[0]) && (vec[1] < vec[2]);
bool b2 = (vec[2] <= vec[0]) && (vec[2] <= vec[1]);
return cross(vec, Vec3D(int(b0), int(b1), int(b2)));
}
Informal explanation
Exactly 1 and only 1 of the bools get set; bN gets set if dimension N has magnitude strictly less than all subsequent dimensions and not greater than all previous dimensions. We then have a unit vector with a single non-zero dimension that corresponds to a dimension of minimum magnitude in vec. The cross product of this with vec is orthogonal to vec by defn of cross product. Consider now that the cross product is numerically unstable only when the two vectors are very closely aligned. Consider that our unit vector is large in only a single dimension and that that dimension corresponds to the dimension where vec was small. It's thus guaranteed to be loosely orthogonal to vec before taking the cross product, with least orthogonality in the case where all dimensions of vec are equal. In this least-orthogonal case, we're still quite orthogonal given that our unit vector has all but one dimension 0 whereas vec has all equal. We thus avoid the unstable case of taking the cross product of two nearly-aligned vectors.
q4w56's is almost there for a robust solution. Problems: 1) Doesn't take in account scaling. 2) Doesn't compare the magnitude between two variables when it should.
scale = |x| + |y| + |z|
if scale == 0:
return (0,0,0)
x = x/scale
y = y/scale
z = z/scale
if |x| > |y|:
return (z, 0,-x)
else:
return (0, z,-y)
Scaling is important when dealing with very large or very small numbers. Plus in general you are better off doing floating point operations on values between 0 and 1.
One way would be to find a rotation transform from the positive z-axis (or any other axis) to your given vector. Then transform <modulus * cos(angle), modulus * sin(angle), 0> using this transform.
def getPerpendicular(v1,modulus,angle):
v2 = vector(0,0,1)
v1_len = v2.length()
axis = v1.cross_product(v2)
sinAngle = axis.length() / v1_len # |u x v| = |u| * |v| * sin(angle)
cosAngle = v1.dot_product(v2) / v1_len # u . v = |u| * |v| * cos(angle)
axis = axis.normalize()
# atan2(sin(a), cos(a)) = a, -pi < a < pi
angle = math.atan2(sinAngle, cosAngle)
rotationMatrix = fromAxisAngle(axis, angle)
# perpendicular to v2
v3 = vector(modulus*cos(angle),modulus*sin(angle),0)
return rotationMatrix.multiply(v3);
To calculate the rotation matrix, see this article: WP: Rotation matrix from axis and angle
Another method would be to use quaternion rotation. It's a little more to wrap your head around, but it's less numbers to keep track of.
How to find a bisecor b = (bx, by) of two vectors in general (we consider two non–zero vectors u = (ux, uy), v = (vx, vy), that may be collinear ).
For non-collinear vector we can write:
bx = ux/|u| + vx / |v|
by = uy/|u| + vy / |v|
But for collinear vectors
bx = by = 0.
Example:
u = (0 , 1)
v = (0, -1)
b = (0, 0)
A general and uniform approach is to get the angle of both vectors
theta_u = math.atan2(ux, uy)
theta_v = math.atan2(vx, vy)
and to create a new vector with the average angle:
middle_theta = (theta_u+theta_v)/2
(bx, by) = (cos(middle_theta), sin(middle_theta))
This way, you avoid the pitfall that you observed with opposite vectors.
PS: Note that there is an ambiguity in what the "bisector" vector is: there are generally two bisector vectors (typically one for the smaller angle and one for the larger angle). If you want the bisector vector inside the smaller angle, then your original formula is quite good; you may handle separately the special case that you observed for instance by taking a vector orthogonal to any of the two input vectors (-uy/|u|, ux/|u|) if your formula yields the null vector.
To find the unit bisection vectors of u and v.
if u/|u|+v/|v| !=0
first calculate the unit vector of u and v
then use the parallelogram rule to get the bisection (just add them)
since they both have unit of 1, their sum is the bisector vector
then calculate the unit vector of the calculated vector.
else (if u/|u|+v/|v| ==0):
(if you use the method above, it's like a indintermination: 0*infinity=?)
if you want the bisector of (u0v) if u/|u| = (cos(t),sin(t))
take b=(cost(t+Pi/2),sin(t+Pi/2)) = (-sin(t),cos(t) )as the bisector
therefore if u/|u|=(a1,a2) chose b=(-a2,a1)
Example:
u=(0,1)
v=(0,-1)
the bisector of (u0v):
b=(-1,0)
I want to draw a plane which is given by the equation: Ax+By+Cz+D=0.
I first tried to draw him by setting x,y and then get z from the equation. this did not work fine because there are some planes like 0x+0y+z + 0 = 0 and etc...
my current solution is this:
- draw the plane on ZY plane by giving 4 coordinates which goes to infinity.
- find out to rotation that should be done in order to bring the normal of the given plane(a,b,c) to lay
on z axis.
- find the translation that should be done in order for that plane be on x axis.
- make the exactly opposite transformation to those rotation and to this translation hence i will get the
plane in his place.
ok
this is a great thing but I can make the proper math calculations(tried a lot of times...) with the dot product and etc....
can someone help me in understanding the exact way it should be done OR give me some formula in which I will put ABCD and get the right transformation?
You will want the following transformation matrix:
[ x0_x y0_x z0_x o_x ]
M = [ x0_y y0_y z0_y o_y ]
[ x0_z y0_z z0_z o_z ]
[ 0 0 0 1 ]
Here, z0 is the normal of your plane, and o is the origin of your plane, and x0 and y0 are two vectors within your plane orthogonal to z0 that define the rotation and skew of your projection.
Then any point (x,y) on your XY plane can be projected to a point (p_x, p_y, p_z) your new plane with the following:
(p_x, p_y, p_z, w) = M * (x, y, 0, 1)
now, z0 in your transformation matrix is easy, that's the normal of your plane and that is simply n = normalize(a,b,c).
In choosing the rest however you have distinctly more freedom. For the origin you could take the point that the plane intersects the Z axis, unless of course the plane is parallel to the Z axis, in which case you need something else.
So e.g.
if (c != 0) { //plane intersects Z axis
o_x = 0;
o_y = 0;
o_z = -d/c;
}
else if (b != 0) { // plane intersects Y axis
o_x = 0;
o_y = -d/b;
o_z = 0;
}
else { // plane must intersect the X axis
o_x = -d/a;
o_y = 0;
o_z = 0;
}
In practice you may want to prefer a different test than (c != 0), because with that test it will succeed even is c is very very small but just different from zero, leading your origin to be at say, x=0, y=0, z=10e100 which would probably not be desirable. So some test like (abs(c) > threshold) is probably preferable. However you could of course take an entirely different point in the plane to put the origin, perhaps the point closest to the origin of your original coordinate system, which would be:
o = n * (d / sqrt(a^2 + b^2 + c^2))
Then finally we need to figure out an x0 and y0. Which could be any two linearly independent vectors that are orthogonal to z0.
So first, let's choose a vector in the XY plane for our x0 vector:
x0 = normalize(z0_y, -z0_x, 0)
Now, this fails if your z0 happens to be of the form (0, 0, z0_z) so we need a special case for that:
if (z0_x == 0 && z0_y == 0) {
x0 = (1, 0, 0)
}
else {
x0 = normalize(z0_y, -z0_x, 0)
}
Finally let's say we do not want skew and choose y0 to be orthogonal to both x0, and y0, then, using the crossproduct
y0 = normalize(x0_y*y0_z-x0_z*y0_y, x0_z*y0_x-x0_z*y0_z, x0_x*y0_y-x0_y*y0_x)
Now you have all to fill your transformation matrix.
Disclaimer: Appropriate care should be taken when using floating point representations for your numbers, simple (foo == 0) tests are not sufficient in those cases. Read up on floating point math before you start implementing stuff.
Edit: renamed some variables for clarity
Is this what you're asking?
Transforming a simple plane like the xy plane to your plane is fairly simple:
your plane is Ax+By+Cz+D=0
the xy plane is simply z=0. i.e. A=B=D=0, while C=whatever you want. We'll say 1 for simplicity's sake.
When you have a plane in this form, the normal of the plane is defined by the vector (A,B,C)
so you want a rotation that will take you from (0,0,1) to (A,B,C)*
*Note that this will only work if {A,B,C} is unitary. so you may have to divide A B and C each by sqrt(A^2+B^2+C^2).
rotating around just two of the axes can get your from any direction to any other direction, so we'll pick x and y;
here are the rotation matrices for rotations by a about the x axis, and b about the y axis.
Rx := {{1, 0, 0}, {0, Cos[a], Sin[a]}, {0, -Sin[a], Cos[a]}}
Ry := {{Cos[b], 0, -Sin[b]}, {0, 1, 0}, {Sin[b], 0, Cos[b]}}
if we do a rotation about x, followed by a rotation about y, of the vector normal to the xy plane, (0,0,1), we get:
Ry.Rx.{0,0,1} = {-Cos[a] Sin[b], Sin[a], Cos[a] Cos[b]}
which are your A B C values.
i.e.
A = -Cos[a]Sin[b]
B = Sin[a]
C = Cos[a]Cos[b]
From here, it's simple.
a = aSin[B]
so now A = -Cos[aSin[B]]Sin[b]
Cos[aSin[x]] = sqrt(1-x^2)
so:
A = -Sqrt[1-B^2] * Sin[b]
b = aSin[-A/sqrt[1-B^2]]
a = aSin[B] (rotation about x axis)
b = aSin[-A/sqrt[1-B^2]] (rotation about y axis)
So we now have the angles about the x and y axes we need to rotate by.
After this, you just need to shift your plane up or down until it matches the one you already have.
The plane you have at the moment, (after those two rotations) is going to be Ax+By+Cz=0.
the plane you want is Ax+Bx+Cz+D=0. To find out d, we will see where the z axis crosses your plane.
i.e. Cz+D=0 -> z = -D/C
So we transform your z in Ax+By+Cz=0 by -D/C to give:
Ax+By+C(z+D/C) = Ax+By+Cz+D=0. Oh would you look at that!
It turns out you don't have to do any extra maths once you have the angles to rotate by!
The two angles will give you A,B, and C. To get D you just copy it from what you had.
Hope that's of some help, I'm not entirely sure how you plan on actually drawing the plane though...
Edited to fix some horrible formatting. hopefully it's better now.