Length of vector using quaternions - c#

Is it possible to calculate the length of a vector by rotating it to, and along an axis, and then use that axis to measure the length of the vector? If so, is it less expensive than calculating length by Pythagoras/square-root?
i am working in unity (C#)
Example:
Vector3 myVector(x, y, z);
Vector3 myVectorRealigned = Quaternion.FromToRotation(myVector, Vector3.up) * myVector;
float myVectorLength1 = sqrt(myVector.x^2 + myVector.y^2 + myVector.z^2);
float myVectorLength2 = myVectorRealigned.y;
when i tried this it seemed to work! however which of these methods is the best to use/is the least expensive?

I am no mathematician, so please correct me if I am wrong.
As you have tested, both approaches should work, but I guess that the Quaternion approach is more costly.
The norm approach requires 3 multiplications, 2 additions and 1 sqrt.
In contrast, the first step in the quaternion approach (Quaternion.FromToRotation) alone requires is more costly than calculating the norm. A way of calculating the quaternion describing the rotation from one vector to another is this:
Quaternion q;
vector a = crossproduct(v1, v2)
q.xyz = a;
q.w = sqrt((v1.Length ^ 2) * (v2.Length ^ 2)) + dotproduct(v1, v2)
As you see, this alone is more costly than norm = sqrt(myVector.x^2 + myVector.y^2 + myVector.z^2).
Additionally you use that quaternion to rotate your vector again involves dot products, cross products and several multiplications and additions.
E.g.: rotatedVec = 2 * dot(q.xyz, v) * q.xyz + (q.w^2 - dot(q.xyz, q.xyz)) * v + 2 * q.w * cross(q.xyz, v).
References: from-to-quaternion, rotate vector by quaternion
Side Note: If you are concerned with performance and you don't need the exact length of the vector (e.g. for determining the closest object to position x), you can use Vector3.sqrMagnitude (squared norm) as well.

Related

What are 2D quaternions called (what should I search for more information)?

I stumbled on a working concept for a fast rotation & orientation system today, based on a two-term quaternion that represents either a rotation about the X axis (1,0,0) in the form w + ix, a rotation about the Y axis (0,1,0) in the form w + jy, or a rotation about the Z axis (0,0,1) in the form w + kz.
They're similar to complex numbers, but a) are half-angled and double-sided like all quaternions (they're simply quaternions with two of three imaginary terms zeroed out), and b) represent rotations about one of three 3D axes specifically.
My problem and question is...I can't find any representation of such a system online and have no idea what to search for. What are these complex numbers called? Who else has done something like this before? Where can I find more information on the path I'm headed down? It seems too good to be true and I want to find the other shoe before it drops on me.
Practical example I worked out (an orientation quaternion from Tait-Bryan angles):
ZQuat Y, YQuat P, XQuat R; // yaw, pitch, roll
float w = Y.W * P.W;
float x = -Y.Z * P.Y;
float y = Y.W * P.Y;
float z = Y.Z * P.W;
Quaternion O; // orientation
O.W = x * R.W + w * R.X;
O.X = y * R.W + z * R.X;
O.Y = z * R.W - y * R.X;
O.Z = w * R.W - x * R.X;
Quaternions in 2D would degenerate to just being a single component being no diferrent than an rotation angle. That's propably why you do not find anything. With quaternions you do f.e. not have the problem of gimbal lock, appearing when two rotation axes align because of rotation order. In normal 2D space you do not have more than a single rotation axis, so it has neither order (how do you sort a single element) and there are no axes to align. The lack of rotation axes in 2D is because you get a rotation axis when being perpendicular to two other axes.
This gives 3 axes for 3D:
X&Y=>Z
X&Z=>Y
Y&Z=>X
But only one for 2D:
X&Y=>Z

What's wrong with this XNA RotateVector2 function?

I know this is probably a very simple question, but I can't seem to figure it out. First of all, I want to specify that I did look over Google and SO for half an hour or so without finding the answer to my question(yes, I am serious).
Basically, I want to rotate a Vector2 around a point(which, in my case, is always the (0, 0) vector). So, I tried to make a function to do it with the parameters being the point to rotate and the angle(in degrees) to rotate by.
Here's a quick drawing showing what I'm trying to achieve:
I want to take V1(red vector), rotate it by an angle A(blue), to obtain a new vector (V2, green). In this example I used one of the simplest case: V1 on the axis, and a 90 degree angle, but I want the function to handle more "complicated" cases too.
So here's my function:
public static Vector2 RotateVector2(Vector2 point, float degrees)
{
return Vector2.Transform(point,
Matrix.CreateRotationZ(MathHelper.ToRadians(degrees)));
}
So, what am I doing wrong? When I run the code and call this function with the (0, -1) vector and a 90 degrees angle, I get the vector (1, 4.371139E-08) ...
Also, what if I want to accept a point to rotate around as a parameter too? So that the rotation doesn't always happen around (0, 0)...
Chris Schmich's answer regarding floating point precision and using radians is correct. I suggest an alternate implementation for RotateVector2 and answer the second part of your question.
Building a 4x4 rotation matrix to rotate a vector will cause a lot of unnecessary operations. The matrix transform is actually doing the following but using a lot of redundant math:
public static Vector2 RotateVector2(Vector2 point, float radians)
{
float cosRadians = (float)Math.Cos(radians);
float sinRadians = (float)Math.Sin(radians);
return new Vector2(
point.X * cosRadians - point.Y * sinRadians,
point.X * sinRadians + point.Y * cosRadians);
}
If you want to rotate around an arbitrary point, you first need to translate your space so that the point to be rotated around is the origin, do the rotation and then reverse the translation.
public static Vector2 RotateVector2(Vector2 point, float radians, Vector2 pivot)
{
float cosRadians = (float)Math.Cos(radians);
float sinRadians = (float)Math.Sin(radians);
Vector2 translatedPoint = new Vector2();
translatedPoint.X = point.X - pivot.X;
translatedPoint.Y = point.Y - pivot.Y;
Vector2 rotatedPoint = new Vector2();
rotatedPoint.X = translatedPoint.X * cosRadians - translatedPoint.Y * sinRadians + pivot.X;
rotatedPoint.Y = translatedPoint.X * sinRadians + translatedPoint.Y * cosRadians + pivot.Y;
return rotatedPoint;
}
Note that the vector arithmetic has been inlined for maximum speed.
So, what am I doing wrong? When I run the code and call this function with the (0, -1) vector and a 90 degrees angle, I get the vector (1, 4.371139E-08) ...
Your code is correct, this is just a floating point representation issue. 4.371139E-08 is essentially zero (it's 0.0000000431139), but the transformation did not produce a value that was exactly zero. This is a common problem with floating point that you should be aware of. This SO answer has some additional good points about floating point.
Also, if possible, you should stick with radians instead of using degrees. This is likely introducing more error into your computations.

How to do correct polygon rotation? ( in C# though it applies to anything )

Hi I'm using this C# code to rotate polygons in my app - they do rotate but also get skewed along the way which is not what i want to happen. All the polygons are rectangles with four corners defined as 2D Vectors,
public Polygon GetRotated(float radians)
{
Vector origin = this.Center;
Polygon ret = new Polygon();
for (int i = 0; i < points.Count; i++)
{
ret.Points.Add(RotatePoint(points[i], origin, radians));
}
return ret;
}
public Vector RotatePoint(Vector point, Vector origin, float angle)
{
Vector ret = new Vector();
ret.X = (float)(origin.X + ((point.X - origin.X) * Math.Cos((float)angle)) - ((point.Y - origin.Y) * Math.Sin((float)angle)));
ret.Y = (float)(origin.Y + ((point.X - origin.X) * Math.Sin((float)angle)) - ((point.Y - origin.Y) * Math.Cos((float)angle)));
return ret;
}
Looks like your rotation transformation is incorrect. You should use:
x' = x*Cos(angle) - y*Sin(angle)
y' = x*Sin(angle) + y*Cos(angle)
For more information, check various sources on the internet. :)
I don't have any answer to why it's skewing yet, but I do have a suggestion to make the code clearer:
public Vector RotatePoint(Vector point, Vector origin, float angle)
{
Vector translated = point - origin;
Vector rotated = new Vector
{
X = translated.X * Math.Cos(angle) - translated.Y * Math.Sin(angle),
Y = translated.X * Math.Sin(angle) + translated.Y * Math.Cos(angle)
};
return rotated + origin;
}
(That's assuming Vector has appropriate +/- operators defined.)
You may still need a couple of casts to float, but you'll still end up with fewer brackets obfuscating things. Oh, and you definitely don't need to cast angle to float, given that it's already declared as float.
EDIT: A note about the rotation matrices involved - it depends on whether you take the angle to be clockwise or anticlockwise. I wouldn't be at all surprised to find out that the matrix is indeed what's going wrong (I did try to check it, but apparently messed up)... but "different" doesn't necessarily mean "wrong". Hopefully the matrix is what's wrong, admittedly :)
I think your rotation matrix is incorrect. There should be a + instead of - in the second equation:
+cos -sin
+sin +cos
Assuming that origin is 0,0. From your formula I would get:
X' = (X + ((X - 0) * Cos(angle)) - ((Y - 0) * Sin(angle)));
X' = X + (X * Cos(angle)) - (Y * Sin(angle));
Which differs from the initial formula
x' = x * cos angle - y * cos angle
So I think Jon Skeet's answer is correct and clearer.
Just a wild guess - are you sure the aspect ratio of your desktop resolution is the same as of the physical screen? That is, are the pixels square? If not, then rotating your rectangles in an arbitrary angle can make them look skewed.

XNA: adding a force to a vector

i do have 2 points on a 2d plane. one has already an vector that does determine in which direction it will move.
now i want to add a vector to this existing vector. so he accelerates in the direction of the other point.
to be a bit more clear, it is about 2 asteroids flying in space (only 2d) and gravitation should move them a bit closer to each other.
what i did build till now is this:
c = body.position - body2.position;
dist = c.Length();
acc = (body.masse * body2.masse) / (dist * dist);
xDist = body2.position.X - body.position.X;
yDist = body2.position.Y - body.position.Y;
direction = MathHelper.ToDegrees((float)(Math.Atan2((double)yDist, (double)xDist)));
body.velocity.Y = body.velocity.Y + (float)(Math.Sin(direction) * acc);
body.velocity.X = body.velocity.X + (float)(Math.Cos(direction) * acc);
in the moment the direction calculated is completly off. surely i am making just a stupid mistake, but i have no idea.
You need to pass your direction angle in in radians to Math.sin and Math.Cos (rather then in degree as you do in your smaple code).
see also:
http://msdn.microsoft.com/en-us/library/system.math.sin.aspx
The angle, a, must be in radians. Multiply by Math.PI/180 to convert degrees to radians.
My mechanics and linear algebra are a bit rusty but I think you should be able to do it without resorting to trigonometry. These formulae probably need tweaking, I'm not sure if I got u and -u mixed up.
Here it is in pseudo code
T is whatever time period you're iterating over
G is the gravitational constant
body1 starts with a velocity of v1
body2 starts with a velocity of v2
c = body.position - body2.position
c1 is a vector
use the vector c to get a vector of length 1 in the direction of the force
u = c1 / c.Length()
body1 should have an acceleration vector of a1 = G * body2mass/c.Length()^2 * (-u)
body2 should have an acceleration vector of a2 = G * body1mass/c.Length()^2 * (u)
body1 has a new velocity vector of v1 + a1/T
body2 has a new velocity vector of v1 + a2/T
rinse and repeat
Not completely sure what you try to do. Why can't you just use Vector2.Add(v1, v2)?

How to implement Slerp for a Point3?

I am trying to implement a Point3.Slerp method, so was looking for some samples but the ones I found seems like it's a whole brand new class to host all the code.
Is there a simple, straightforward way to implement it? Basically the ones I have seen was using Matrix types. Can I implement Slerp without matrices, or would that be a disadvantage (performance, etc)?
Pseudo codes are fine too.
Spherical linear interpolation typically used with Quaternions, not Points. It's only really meaningful when you are trying to interpolate between two specific rotations.
The only place where I can see this being directly related to Point3 would be if you had 2 points on the surface of a sphere, and you wanted to "slerp" between them around the geodesic path. In that case, your points aren't really points being interpolated - they're being used to compute two axes and angles (which define quaternions) and interpolating between those.
If you're interpolating between two point3 values, direct linear interpolation is probably what you would want to do.
As for using matrices - if you get your points into quaternion form, you can use quaternion math directly - no matrices required.
A point slerp is meaningful if you're working with a spherical space (such as a planet's surface or likewise).
Off the top of my head I would take the cross product of your start and end points (as vectors) to get a rotation axis then you can calculate a rotation for the X, Y, and Z (to avoid having to create your own matrix class, as you seem to want to).
http://en.wikipedia.org/wiki/Rotation_matrix#Dimension_three
That link shows a rotation matrix for an axis/angle pair. Just write out the multiplication for each component and simplify to get a matrix-less tranformation.
edit:
Heres a simple decomposed rotation about an axis (x, y, z):
X' = (x^2 + (1 - x^2) * cos(theta) + (x * y * (1 - cos(theta)) - x * sin(theta)) + (x * z * (1 - cos(theta)) + y * sin(theta))
Y' = (x * y * (1 - cos(theta)) + z * sin(theta)) + (y^2 + (1 - y^2) * cos(theta)) + (y * z * (1 - cos(theta)) - x * sin(theta))
Z' = (x * z * (1 - cos(theta)) - y * sin(theta)) + (y * z * (1 - cos(theta)) + x * sin(theta)) + (z^2 + (1 - z^2) * cos(theta))
Since you want a parameterized rotation as well, make sure you calculate the angle between your vectors (the inverse cosine of the scalar product of the two points) and set your theta as a value between 0 and that angle based on your interpolation parameter.
To Slerp you need quaternions, which is why there is a whole new class.

Categories