Quaternions are of the form q = w + x*i + y*j + z*k where i, j & k are the vectors which represent dimensions and w is the scalar. Given 4 values that represent the w, x, y, z components, I need to find the value of q.
I have looked through many MATLAB functions but most of them deal with quaternion multiplication, normalization etc. Can anyone please tell me how I can find a single value q for a given quaternion?
(w,x,y,z) is the value of the quaternion.
When you say a single value for q - do you mean the norm of the quaternion? In this case the norm is:
Math.sqrt(w*w+x*x+y*y+z*z).
Related
I have segments of lines and ellipses (NOT planes and ellipsoids) transformed into 3d space and need to calculate whether two given segments intersect and where.
I'm using Java but haven't yet found a library which solves my problem, nor came across some algorithms that I could use for my own implementation.
For line-line intersection test there are several ways to solve. The classic way is using linear algebra (i.e., solving a linear matrix system) but from software development point of view I like more the Geometric-Algebra way, in the form of Plucker Coordinates, which only requires to implement vector algebra operations (i.e., cross-product and dot-product) which are simpler to code than matrix operations for solving linear systems.
I will show both for the sake of comparison then you decide:
Linear Algebra Way
Given line segment P limited by points P1 and P2 and line segment Q limited by points Q1 and Q2.
The parametric form of the lines is given by:
P(t) = P1 + t (P2 - P1)
Q(t) = Q1 + t (Q2 - Q1)
Where t is a real number in the interval [0 1].
If two lines intersect then the following equation holds:
P(t0) = Q(t1)
Provided that the two unknown numbers t0 and t1 exist. Expanding the above equation we get:
t0 (P2 - P1) - t1 (Q2 - Q1) = Q1 - P1
We can solve for t0 and t1 by expressing the above equation in matrix algebra:
A x = B
Where A is a 3x2 matrix with coordinates of vector (P2 - P1) in the first column and coordinates of the vector (Q2 - Q1) in the second column; x is a 2x1 column vector of unknowns t0 and t1 and B is a 3x1column vector with coordinates of vector (Q1 - P1).
Classically the system can be solved calculating the pseudo-inverse of matrix A, denoted by A^+:
A^+ = (A^T A)^-1 A^T
See:
https://en.m.wikipedia.org/wiki/Generalized_inverse
Fortunately any matrix package in Java should be able to compute the above calculations very easily and perhaps very efficiently too.
If multiplying A with its pseudo-inverse A^+ is equal to the identity matrix I, i.e., (A A^+) == I, then there IS a unique answer (intersection) and you can get it computing the following product:
x = A^+ B
Of course if you cannot compute the pseudo-inverse in the first place, e.g., because (A^T A) is singular (i.e. determinant is zero), then no intersection exists.
Since we are dealing with segments, the intersection point is at point P(x0) or Q(x1) iff x0 and x1 are both in the interval [0 1].
OTHER METHOD OF SOLUTION
To avoid dealing with matrix algebra we can try to solve the system using vector algebra and substitution method:
t0 (P2 - P1) - t1 (Q2 - Q1) = Q1 - P1
t0 = a + t1 b
t1 = C • (Q1 - (1 - a) P1 - a P2) / |C|^2
Where:
a = (P2 - P1) • (Q1 - P1) / |P2 - P1|^2
b = (P2 - P1) • (Q2 - Q1) / |P2 - P1|^2
C = b (P2 - P1) - (Q2 - Q1)
I cannot provide a geometric intuition of the above results yet.
Plucker Coordinates way
Given line segment P limited by points P1 and P2 and line segment Q limited by points Q1 and Q2.
The Plucker Coordinates of line P is given by a pair of 3d vectors (Pd, Pm):
Pd = P2 - P1
Pm = P1 x P2
Where Pm is the cross-product of P1 and P2. The Plucker Coordinates (Qd, Qm) of line Q can be calculated in exactly the same way.
The lines P and Q only can intersect if they are coplanar. Thr lines P and Q are coplnanar iif:
Pd • Qm + Qd • Pm = 0
Where (•) is the dot-product. Since machines have finite precision a robust test should check not for zero but for a small number. If Pd x Qd = 0 then lines are parallel (here 0 is the zero vector). Again a robust test should be for instamce that the squared length of (Pd x Qd) is small.
If the lines are not parallel then they are coplanar and their intersection (called "meet" in Plucker's jargon) will be the point:
x = ((Pm • N) Qd - (Qm • N) Pd - (Pm • Qd) N) / (Pd x Qd) • N
Where N is any coordinate axis vector (i.e., (1,0,0) or (0,1,0) or (0,0,1)), such that (Pd x Qd) • N is non-zero.
If the neither P nor Q pass through the origin, then their Plucker coordinate Pm and Qm respectively will be non-zero and the following sinpler formula will work
x = Pm x Qm / Pd • Qm
For an introduction to Plucker Coordinates see:
https://en.m.wikipedia.org/wiki/Plücker_coordinates
http://www.realtimerendering.com/resources/RTNews/html/rtnv11n1.html#art3
For a general intersection formula see "Corollary 6" of:
http://web.cs.iastate.edu/~cs577/handouts/plucker-coordinates.pdf
Transforming Ellipses to Circles Forth and Back
We can always transform an ellipse into a circle. An ellipse has two "radius", called semi-axes, which you can visualize in your mind as two orthogonal vectors, one big called major semi-axes and one small called minor semi-axes. You can apply a non-uniform scaling transformation to both semi-axes vectors for making them of equal size, so you get a circle.
We define an ellipse "E" by its center O, which is a 3d point and its two semi-axes A1 and A2, which are also 3d vectors. A normal vector N to the ellipse's plane can be computed by the cross product of its semi-axes N = A1 x A2 and then normalizing it to have unit length.
For now suppose there is a linear function M that you can use to transform (scale) your ellipse E into a circle C, with radius equal to the minor semi-axes, by applying it to your ellipse's semi-axes A1 and A2 and to the ellipse's center O.
Notice that the eliipse's center O and ellipse's normal vector N are not changed by M. So M(N) = N and M(O) = O. That means that the circle is in the same plane and has the same position C than the ellipse. The linear function M has a corresponding inverse function M^-1 so we can transform back the vectors of the circle to get the original ellipse E.
Of course we can transform the endpoints of lines P and Q also using function M for sending them to the "circle space" and we can send them back to "ellipse space" using M^-1.
Using M we can compute the intersection of the lines P and Q with the ellipse E in the circle space. So now we can focus on the line-circle intersection.
Line-Plane Intersection
Given a plane with normal vector N and distance D such that:
N • x + D = 0
For every point x in the plane. Then the intersection with line P with Plucker Coordinates (Pd, Pm) is given by:
x = (N x Pm - D Pd) / N • Pd
This works only if the line P is not in the plane i.e.,:
(N • P1 + D) != 0 and (N • P2 + D) != 0
And for our ellipse E we have:
N = (A1 x A2)/|A1 x A2|
D = -N • O
Line-Circle and Point-Circle Intersection
Now having x, the point-in-circle check is easy:
|O - x| <= |A2|
Equality holds only when x is in circle boundary.
If line P is in circle's plane then the following sinple check will give you the answer:
https://stackoverflow.com/a/1079478/9147444
How to Compute the Linear Function M
A simple way to compute M is the following. Use the Ellipse's normal vector N and semi-axes A1 and A2 to create a 3x3 matrix U. Such that U has the vectors A1, A2 and N as columns.
Then scale the major semi-axes A1 to have the same length to the minor semi-axes A2. Then create the matrix V auch that V has the new vector A1 and A2 and N as columns.
Then we define the linear matrix system:
R U = V
Where R is a 3x3 (non-uniform-)scaling-rotation matrix.
We can solve for R by multiplying both sides of the equation by the inverse of U which is denoted by U^-1
R U U^-1 = V U^-1
Since U U^-1 is the identity matrix we get:
R = V U^+
Using R we define the affine transformation
M(x) = R (x - O) + O
We can use M to transform points to circle space, such as O, P1, P2, Q1 and Q2. But if we need to transform vectors such as A1, A2, N, Pd and Qd. We need to use the simpler M:
M(x) = R x
Since A1, A2 and N are eigenvectors of R then R is not singular and has an inverse. We define the inverse M as:
M^-1(x) = R^-1 (x - O) + O
And for vectors:
M^-1(x) = R^-1 x
Update: Ellipse-Ellipse intersection
Two intersecting non-coplanar 3d-ellipses have their intersection points on the line formed by the intersection between their planes. So you first find the line formed by the intersection of the planes containig the ellipses (if planes do not intersect i.e., they are parallel, then neither the ellipses intersect).
The line of intersection belong to both planes, so it is perpendicular to both normals. The direction vector V is the cross product of the plane normals:
V = N1 × N2
To fully determine the line we also need to find a point on the line. We can do that solving the linear equations of the planes. Given the 2x3 matrix N = [N1^T N2^T] with the normals N1 and N2 as rows, and the 2x1 column vector b = [N1 • C1, N2 • C2], where C1 and C2 are the centers of the ellipses, we can form the linear matrix system:
N X = b
Where X is some point satifying both plane equations. The system is underdetermined since there are infinite number of points in the line satifying the system. We can still find a particular solution closer to the origin by using the pseudo-inverse of matrix N, denoted by N^+.
X = N^+ b
The line equation is
L(t) = X + t V
For some scalar t.
Then you can apply the method described earlier to test the line-ellipse intersection i.e., scaling the ellipse to a circle and intersect with the coplanar line. If both ellipses intersect the line in the same points then they intersect.
Now, the case in which the ellipses actually lie on the same plane is more complex. You can ceck the approach taken by Dr Eberly in his excellent book "Geometric Tools" (also available online):
https://www.geometrictools.com/Documentation/IntersectionOfEllipses.pdf
And also you can check the C++ source code which is open source:
https://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrEllipse2Ellipse2.h
I've been trying to make a code that goes through all array elements that are between two specified points, but I am stuck.
Let's suppose it's an array like that:
int[][] new_array = new int[100][100];
And how do I get all elements that are in straight line between let's say
new_array[17][2];
and
new_array[5][90];
This is what I want to achieve:
Let's imagine that your array is a first quadrant of a cartesian coordinates system. With a first column lying on axis Y and last row lying on axis X.
Having that assumption you could find a function that describes a straight line between any of two points in your array.
You need to solve the function:
y = ax + b
It's a standard linear function. You have two points, solving that you'll find your equation (values of a and b).
When you know equation you need to evaluate points in the array for each x value. Doing that you'll find all y values that are below/on/above the line.
Following #Marcin Pietraszek's answer the function can be obtained this way:
Given the two points (a,b) and (c,d) the straight line that passes through both points is given by
a + K * (x - a) = c AND b + K (y - b) = d
where K is a scalar number.
And this resolves to:
y = ( (d - b) * x - (d - b) * a + (c - a) * b ) / (c - a)
So any point (x, y) that meets this condition will be on the straight line.
You will need go through the matrix, checking one by one to see which points meet the condition.
If you want only the point inside the segment, then aditionally you need to check the boundaries.
I don't think I need code here, but just so you can see what I'm looking at:
public class Valuation {
//line is a monotonic (non-decreasing. Could be constant at points)
//line in 2D space where x=0 -> y=0 and x=1 -> y=1
//the gradient cannot be infinite
//line is only defined between x=0 and x=1. Can catch when arguments to
//functions are unacceptable given this.
LineEquation line;
float cut(float from, float value){
//Using 'from' as x, return the least value x' where 'value' is the difference
//between the y value returned by x and the y value returned by x'
}
float eval(float from, float to){
//require to > from
//return the difference between the y value returned by 'to'
//and the y value returned by 'from'
}
The question I have is how do I represent a line/curve like this in Java? I can verify the lines given fit the requirements that I have, but I want to have this LineEquation class to be able to handle essentially any line that fits these requirements. These could be quadratic curves or lines where we have something like, when x is between 0 and 0.5, the equation is a, and then when x is between 0.5 and 1, the equation is b. I got frustrated thinking of all the ways you could describe a line that meets the specifications, and then how I would go through them all, and how I would have to deal with all the different types in different ways. Unfortunately I do not have the vocabulary to find a library that has what I want.
If you're using Java 8, then probably the simplest thing to do would be to store the curve as a Function<Float,Float>, which can implement any kind of equation for any kind of curve, provided y is single-valued for any given x, and x always falls within range for a float.
Your class might look like this.
public class Valuation {
final Function<Float,Float> curve;
public Valuation(final Function<Float,Float> curve) {
this.curve = curve;
}
float eval(float from, float to){
return curve.apply(to) - curve.apply(from);
}
}
Then you can create these with calls such as
new Valuation( x -> ( x * x + 2 * x + 3 ))
for a typical monotonic quadratic, or
new Valuation( x -> ( x > 0.5 ? 3 * x : 1 + x ))
for a piecewise function consisting of two linear sections.
I haven't shown the code for cut. Had to leave something up to you!
I would like to use a HashMap
to map (x, y) coordinates to values.
What is a good hashCode() function definition?
In this case, I am only storing integer coordinates of the form (x, y)
where y - x = 0, 1, ..., M - 1 for some parameter M.
To get unique Value from two numbers, you can use bijective algorithm described in here
< x; y >= x + (y + ( (( x +1 ) /2) * (( x +1 ) /2) ) )
This will give you unquie value , which can be used for hashcode
public int hashCode()
{
int tmp = ( y + ((x+1)/2));
return x + ( tmp * tmp);
}
I generally use Objects.hash(Object... value) for generating hash code for a sequence of items.
The hash code is generated as if all the input values were placed into an array, and that array were hashed by calling Arrays.hashCode(Object[]).
#Override
public int hashCode() {
return Objects.hash(x, y);
}
Use Objects.hash(x, y, z) for 3D coordinates.
If you wish to handle it manually, you could do compute hashCode using:-
// For 2D coordinates
hashCode = LARGE_PRIME * X + Y;
// For 3D coordinates
hashCode = LARGE_PRIME^2 * X + LARGE_PRIME * Y + Z;
To calculate a hash code for objects with several properties, often a generic solution is implemented. This implementation uses a constant factor to combine the properties, the value of the factor is a subject of discussions. It seems that a factor of 33 or 397 will often result in a good distribution of hash codes, so they are suited for dictionaries.
This is a small example in C#, though it should be easily adabtable to Java:
public override int GetHashCode()
{
unchecked // integer overflows are accepted here
{
int hashCode = 0;
hashCode = (hashCode * 397) ^ this.Hue.GetHashCode();
hashCode = (hashCode * 397) ^ this.Saturation.GetHashCode();
hashCode = (hashCode * 397) ^ this.Luminance.GetHashCode();
return hashCode;
}
}
This scheme should also work for your coordinates, simply replace the properties with the X and Y value. Note that we should prevent integer overflow exceptions, in DotNet this can be achieved by using the unchecked block.
Have you considered simply shifting either x or y by half the available bits?
For "classic" 8bit thats only 16 cells/axis, but with todays "standard" 32bit it grows to over 65k cells/axis.
#override
public int hashCode() {
return x | (y << 15);
}
For obvious reasons this only works as long as both x and y are in between 0 and 0xFFFF (0-65535, inclusive), but thats plenty of space, more than 4.2bio cells.
Edit:
Another option, but that requires you to know the actual size, would be to do x + y * width (where width ofc is in the direction of x)
That depends on what you intend on using the hash code for:
If you plan on using it as a sort of index, E.g. knowing x and y will hash into an index where (x, y) data is stored, it's better to use a vector for such a thing.
Coordinates[][] coordinatesBucket = new Coordinates[maxY][maxX];
But if you absolutely must have a unique hash for every (x, y) combination, then try applying the coordinates to a decimal table (rather than adding or multiplying). For example, x=20 y=40 would give you the simple and unique code xy=2040.
I understand that the dot (or inner) product of two quaternions is the angle between the rotations (including the axis-rotation). This makes the dot product equal to the angle between two points on the quaternion hypersphere.
I can not, however, find how to actually compute the dot product.
Any help would be appreciated!
current code:
public static float dot(Quaternion left, Quaternion right){
float angle;
//compute
return angle;
}
Defined are Quaternion.w, Quaternion.x, Quaternion.y, and Quaternion.z.
Note: It can be assumed that the quaternions are normalised.
The dot product for quaternions is simply the standard Euclidean dot product in 4D:
dot = left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w
Then the angle your are looking for is the arccos of the dot product (note that the dot product is not the angle): acos(dot).
However, if you are looking for the relative rotation between two quaternions, say from q1 to q2, you should compute the relative quaternion q = q1^-1 * q2 and then find the rotation associated withq.
Just NOTE: acos(dot) is very not stable from numerical point of view.
as was said previos, q = q1^-1 * q2 and than angle = 2*atan2(q.vec.length(), q.w)
Should it be 2 x acos(dot) to get the angle between quaternions.
The "right way" to compute the angle between two quaternions
There is really no such thing as the angle between two quaternions, there is only the quaternion that takes one quaternion to another via multiplication. However, you can measure the total angle of rotation of that mapping transformation, by computing the difference between the two quaternions (e.g. qDiff = q1.mul(q2.inverse()), or your library might be able to compute this directly using a call like qDiff = q1.difference(q2)), and then measuring the angle about the axis of the quaternion (your quaternion library probably has a routine for this, e.g. ang = qDiff.angle()).
Note that you will probably need to fix the value, since measuring the angle about an axis doesn't necessarily give the rotation "the short way around", e.g.:
if (ang > Math.PI) {
ang -= 2.0 * Math.PI;
} else if (ang < -Math.PI) {
ang += 2.0 * Math.PI;
}
Measuring the similarity of two quaternions using the dot product
Update: See this answer instead.
I assume that in the original question, the intent of treating the quaternions as 4d vectors is to enable a simple method for measuring the similarity of two quaternions, while still keeping in mind that the quaternions represent rotations. (The actual rotation mapping from one quaternion to another is itself a quaternion, not a scalar.)
Several answers suggest using the acos of the dot product. (First thing to note: the quaternions must be unit quaternions for this to work.) However, the other answers don't take into account the "double cover issue": both q and -q represent the exact same rotation.
Both acos(q1 . q2) and acos(q1 . (-q2)) should return the same value, since q2 and -q2 represent the same rotation. However (with the exception of x == 0), acos(x) and acos(-x) do not return the same value. Therefore, on average (given random quaternions), acos(q1 . q2) will not give you what you expect half of the time, meaning that it will not give you a measure of the angle between q1 and q2, assuming that you care at all that q1 and q2 represent rotations. So even if you only plan to use the dot product or acos of the dot product as a similarity metric, to test how similar q1 and q2 are in terms of the effect they have as a rotation, the answer you get will be wrong half the time.
More specifically, if you are trying to simply treat quaternions as 4d vectors, and you compute ang = acos(q1 . q2), you will sometimes get the value of ang that you expect, and the rest of the time the value you actually wanted (taking into account the double cover issue) will be PI - acos(-q1 . q2). Which of these two values you get will randomly fluctuate between these values depending on exactly how q1 and q2 were computed!.
To solve this problem, you have to normalize the quaternions so that they are in the same "hemisphere" of the double cover space. There are several ways to do this, and to be honest I'm not even sure which of these is the "right" or optimal way. They do all produce different results from other methods in some cases. Any feedback on which of the three normalization forms above is the correct or optimal one would be greatly appreciated.
import java.util.Random;
import org.joml.Quaterniond;
import org.joml.Vector3d;
public class TestQuatNorm {
private static Random random = new Random(1);
private static Quaterniond randomQuaternion() {
return new Quaterniond(
random.nextDouble() * 2 - 1, random.nextDouble() * 2 - 1,
random.nextDouble() * 2 - 1, random.nextDouble() * 2 - 1)
.normalize();
}
public static double normalizedDot0(Quaterniond q1, Quaterniond q2) {
return Math.abs(q1.dot(q2));
}
public static double normalizedDot1(Quaterniond q1, Quaterniond q2) {
return
(q1.w >= 0.0 ? q1 : new Quaterniond(-q1.x, -q1.y, -q1.z, -q1.w))
.dot(
q2.w >= 0.0 ? q2 : new Quaterniond(-q2.x, -q2.y, -q2.z, -q2.w));
}
public static double normalizedDot2(Quaterniond q1, Quaterniond q2) {
Vector3d v1 = new Vector3d(q1.x, q1.y, q1.z);
Vector3d v2 = new Vector3d(q2.x, q2.y, q2.z);
double dot = v1.dot(v2);
Quaterniond q2n = dot >= 0.0 ? q2
: new Quaterniond(-q2.x, -q2.y, -q2.z, -q2.w);
return q1.dot(q2n);
}
public static double acos(double val) {
return Math.toDegrees(Math.acos(Math.max(-1.0, Math.min(1.0, val))));
}
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
var q1 = randomQuaternion();
var q2 = randomQuaternion();
double dot = q1.dot(q2);
double dot0 = normalizedDot0(q1, q2);
double dot1 = normalizedDot1(q1, q2);
double dot2 = normalizedDot2(q1, q2);
System.out.println(acos(dot) + "\t" + acos(dot0) + "\t" + acos(dot1)
+ "\t" + acos(dot2));
}
}
}
Also note that:
acos is known to not be very numerically accurate (given some worst-case inputs, up to half of the least significant digits can be wrong);
the implementation of acos is exceptionally slow in the JDK standard libraries;
acos returns NaN if its parameter is even slightly outside [-1,1], which is a common occurrence for dot products of even unit quaternions -- so you need to bound the value of the dot product to that range before calling acos. See this line in the code above:
return Math.toDegrees(Math.acos(Math.max(-1.0, Math.min(1.0, val))));
According to this cheatsheet Eq. (42), there is a more robust and accurate way of computing the angle between two vectors that replaces acos with atan2 (although note that this does not solve the double cover problem either, so you will need to use one of the above normalization forms before applying the following):
ang(q1, q2) = 2 * atan2(|q1 - q2|, |q1 + q2|)
I admit though that I don't understand this formulation, since quaternion subtraction and addition has no geometrical meaning.