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.
Related
I would like to draw a rectangle made of Mesh.
I enter the A starting point and B ending point.
The width of the rectangle is known in advance and is equal to H.
How to correctly determine the coordinates of corner points? (Everything happens in 3D)
There are a lot of theoretical entries on the net (but mostly for 2D) and trying something like this:
var vAB = B - A;
var P1 = new Vector3(-vAB.z, vAB.y, vAB.x) / Mathf.Sqrt(Mathf.Pow(vAB.x, 2) + Mathf.Pow(vAB.y, 2) + Mathf.Pow(vAB.z, 2)) * 0.5 * H;
But I can't find the correct solution
The simple way should be to use the cross product. The cross product of two vectors is perpendicular to both input vectors. You will need to define the normal of your rectangle, In this I use vector3.up. A-B cannot be parallel to the normal vector, or you will get an invalid result.
var l = B - A;
var s = Vector3.Normalize(Vector3.Cross(l, Vector3.up));
var p1 = A + s * h/2;
var p2 = A - s * h/2;
var p3 = B - s * h/2;
var p4 = B + s * h/2;
Here's a quick explanation of the trig involved. There are other tools which will reduce the boilerplate a bit, but this should give you an understanding of the underlying maths.
I've tweaked your problem statement slightly: I'm just showing the XY plane (there's no Z involved), and I've rotated it so that the line AB forms a positive angle with the horizontal (as it makes explaining the maths a bit easier). A is at (x1, y1), B is at (x2, y2).
The first step is to find the angle θ that the line AB makes with the horizontal. Draw a right-angled triangle, where AB is the hypotenuse, and the other two sides are parallel to the X and Y axes:
You can see that the horizontal side has length (x2 - x1), and the vertical side has length (y2 - y1). The angle between the base and the hypotenuse (the line AB) is given by trig, where tan θ = (y2 - y1) / (x2 - x1), so θ = arctan((y2 - y1) / (x2 - x1)).
(Make sure you use the Math.Atan2 method to calculate this angle, as it makes sure the sign of θ is correct).
Next we'll look at the corner P1, which is connected to A. As before, draw a triangle with the two shorter sides being parallel at the X and Y axes:
This again forms a right-angled triangle with hypotenuse H/2. To find the base of the triangle, which is the X-distance between P1 and A, again use trig: H/2 * sin θ. Similarly, the Y-distance between P1 and A is H/2 cos θ. Therefore P1 = (x1 + H/2 sin θ, y2 - H/2 cos θ).
Repeat the same trick for the other 3 corners, and you'll find the same result, but with differing signs.
My approach requires you to use a Transform.
public Transform ht; // assign in inspector or create new
void calculatePoints(Vector3 A, Vector3 B, float H)
{
Vector3 direction = B - A;
ht.position = A;
ht.rotation = Quaternion.LookRotation(direction, Vector3.Up);
Vector3 P1 = new Vector3(A + ht.right * H/2f);
Vector3 P2 = new Vector3(A + ht.left * H/2f);
Vector3 P3 = new Vector3(B + ht.right * H/2f);
Vector3 P4 = new Vector3(B + ht.left * H/2f);
}
I think it's intuitive to think with "left" and "right" which is why I used the Transform. If you wanted to define a point along the way, you'd be using ht.forward * value added to A, where value would be something between 0 and direction.magnitude.
I have a piece of code that returns the angle between two vectors in the range of [0,360]. For this I used this question: Direct way of computing clockwise angle between 2 vectors. Now I need to create a function that takes a vector and an angle as input and returns a vector, that has the specified angle with the inputvector. The length of this vector doesn't matter. For this, I need to know how to reverse the effect of Atan2. The rest is pretty simple math.
internal virtual double AngleWith(Vector2 direction, Vector2 location)
{
Vector2 normDir = Vector2.Normalize(direction);
Vector2 normLoc = Vector2.Normalize(location);
double dot = (normDir.X * normLoc.X) + (normDir.Y * normLoc.Y);
double det = (normDir.X * normLoc.Y) - (normDir.Y * normLoc.X);
return Math.Atan2(-det, -dot) * (180 / Math.PI) + 180;
}
Any help is appreciated.
I don't know what you need this for, but arguably there is merit in transforming your vectors from the x,y-coordinate system to the polar coordinate system, in which points in a plane are given by their distance from the origin and the angle to a reference vector (for instance the x-axis in the explanation below), or
To convert from (x, y) to (r, t) with r being the distance between (x,y) and (0,0) and t being the angle in radians between the x-axis and the line connecting (0, 0) and (x, y), you use this:
(r, t) = (sqrt(x^x+y^y), atan(y/x))
The result can be stored in Vector2, just like with x and y. You just have to remember that the values inside don't signify x and y.
If you want the difference in angle, you can just subtract t2 and t1 of your polar coordinates (in radians, still need to convert to degrees).
If you need to add a certain angle in degrees, just add or subtract it to the t value of your polar coordinate.
To convert back to x and y, use
(x, y) = (r cos(t), r sin(t))
The typical way to do this is with a rotation matrix.
RotatedX = x * sin ϴ - y * sin ϴ
RotatedY = x * sin ϴ + y * cos ϴ
Or use System.Numerics.Matrix3x2.CreateRotation(angle) and use it to transform your vector. Note that 'Clockwise' may depend on what coordinate conventions are used. So you might need to adjust the formula depending on your convention.
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.
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
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.