Hello I tried a lot diffrent ways to get bending angle in Leap Motion. But I couldn't get true values. I used this method for reading. Thanks in advance.
Bone bone1 = finger.Bone(Bone.BoneType.TYPE_INTERMEDIATE);
Bone bone2 = finger.Bone(Bone.BoneType.TYPE_PROXIMAL);
double angle = 180 - ((bone1.Direction.AngleTo(bone2.Direction) / Math.PI) * 180) * 2;
In this example I'll use array accessors as a quicker way of accessing bones from Finger objects.
For reference, bone indices are: 0 = metacarpal, 1 = proximal, 2 = intermediate, 3 = distal. You can validate this by looking at the definition of BoneType.
(Careful, the thumb has one fewer bone than the other fingers.)
Vector3 bone0Dir = finger.bones[0].Direction.ToVector3();
Vector3 bone1Dir = finger.bones[1].Direction.ToVector3();
float angle = Vector3.Angle(bone0Dir, bone1Dir);
This example retrieves the angle in degrees between the metacarpal bone and the proximal bone of the finger object.
Note that Vector3.Angle returns the unsigned angle between the two bones; if you desire a signed angle, you can use the SignedAngle function instead. You'll need to pass it a reference axis; Vector3.Cross(hand.PalmarAxis(), hand.DistalAxis()) would be suitable for this.
EDIT: Ah, apologies, the answer is a bit different if you're outside of the Unity engine. In that case, Leap.Vector.AngleTo is sufficient, but there's a simpler way to convert radians to degrees:
Vector bone0Dir = finger.bones[0].Direction;
Vector bone1Dir = finger.bones[1].Direction;
float angle = bone0Dir.AngleTo(bone1Dir) * Vector.RAD_TO_DEG;
Again, this will return the unsigned angle, but fingers don't usually bend backwards, so this should be sufficient for your use-case. You can also use Math.Asin((bone0Dir.Cross(bone1Dir).Magnitude)) * RAD_TO_DEG to get a (right-handed) signed angle.
Related
So... I'll try to be as clear as possible, if I let something unclear please let me know.
I have a vector that comes from origin and go to a point in space, and I have an object that I want it's transform.up (Or Y vector) to be colinear with this vector, but the Y rotation of this object is driven by another factor, and I dont want to change it.
So far, what I'm trying to do is project this vector in the local XY and local ZY planes and measure the angles and apply rotation:
float xInclination = Mathf.Atan(Vector3.ProjectOnPlane(orbitMomemtumVector, transform.right).z / Vector3.ProjectOnPlane(orbitMomemtumVector, transform.right).y)*Mathf.Rad2Deg;
float yInclination = Mathf.Atan(initialPos.z / initialPos.x) * Mathf.Rad2Deg;
float zInclination = -Mathf.Atan(Vector3.ProjectOnPlane(orbitMomemtumVector, transform.forward).x / Vector3.ProjectOnPlane(orbitMomemtumVector, transform.forward).y)*Mathf.Rad2Deg;
if (initialPos.x < 0 && initialPos.z > 0)
{
yInclination = 180f - Mathf.Abs(yInclination);
argumentPeriapsis = argumentPeriapsis - yInclination;
}
else if (initialPos.x < 0 && initialPos.z < 0)
{
yInclination = 180f + Mathf.Abs(yInclination);
argumentPeriapsis = argumentPeriapsis - yInclination;
}
else
{
argumentPeriapsis = argumentPeriapsis - yInclination;
}
transform.rotation = Quaternion.Euler(xInclination, (float)argumentPeriapsis, zInclination);
This image shows the problem, I need the Y arrow to be collinear with the blue line
Let me be clear on this, don't use Euler angles in 3d space. In fact, avoid them in 2d games as well. Unless your object truly rotates on a single axis, and you never have to get the angle between two rotations, or lerp your rotations, don't use them.
What you want is Quaternion.LookRotation(A, B).
A being a vector to which Z will be colinear, X being orthogonal to the plane defined by A and B, and Y belonging to that plane.
Followup:
To match other axis to A, there are multiple solutions. First would be to simply apply the lookRotation to a parent object, while the child object is rotated inside to match whatever rotation you want. You can also edit your entire mesh to do so.
The other way is to simply apply another rotation, so that both combined get your desired result, like so:
Quaternion zMatched = Quaternion.LookRotation(zAxisTarget, direction)
Quaternion yMatched = zMatched * Quaternion.AngleAxis(90f, Vector3.right);
transform.rotation = yMatched;
This will rotate the object so that the y axis becomes collinear to the previous z axis.
This is however nor perfect. If you reach this point, you should consider building your own clearer solution based on combining AngleAxis results. But it works well enough.
I have a 3D point and the x,y,z rotations (qInitial) for that point.
I want to rotate that point more (by some degrees that could be 0 up to 360) around y axis (qYextra). How can I calculate the final Euler rotation (qResult.eulerAngles) that is a combination of these 4 rotations (x-y-z-y)?
I have tried calculating the initial quaternion rotation, and the extra rotation to be applied. And then multiply these two quaternions. However, I get weird results (probably gimbal lock).
Code in C#. Unity.
1.Quaternion qX = Quaternion.AngleAxis(rotationFromBvh.x,Vector3.right);
2.Quaternion qY = Quaternion.AngleAxis(rotationFromBvh.y,Vector3.up);
3.Quaternion qZ = Quaternion.AngleAxis(rotationFromBvh.z,Vector3.forward);
4.Quaternion qYextra = Quaternion.AngleAxis(angle,Vector3.up);
Quaternion qInitial = qY * qX * qZ; // Yes. This is the correct order.
qY*qX*qZ has exactly the same Euler x,y,z results as
Quaternion.Euler(rotationFromBvh)
Quaternion qResult = qInitial * qYextra;
return qResult.eulerAngles;
I can confirm that the code works fine (no gimbal lock) when 4th rotation is 0 degrees (qYextra = identity). Meaning that qInitial is correct. So, the error might be due to the combination of those 2 rotations (qInitial and qYextra) OR due to the convertion from Quaternion to Euler.
EXAMPLE: (qYextra angle is 120 degrees)
RESULTS:
qInitial.eulerAngles gives these results: applying_qInitial_rotation
qResult.eulerAngles gives these results: applying_qResult_rotation
EXPECTED RESULTS:
The expected results should be like qInitial but rotated 120 degrees around y.
Any suggestions? I haven't yet found a solution, and probably I won't.
In your question, you write:
How can I calculate the final Euler rotation that is a combination of these 4 rotations (x-y-z-y)?
However, in your code, you write
Quaternion qInitial = qY * qX * qZ; // Multiply them in the correct order.
I don't know unity, but I would have expected that you would want the order of the rotations to match x, y, z, rather than y, x, z.
You stated that it works when the y-rotation is 0, in which case the place of the y-rotation in the order becomes irrelevant.
Do you get the correct result if you instead write the code below?
Quaternion qInitial = qX * qY * qZ; // Multiply them in the correct order.
I am looking to generate some 3D trajectory data for an aircraft simulation.
The idea is that the aircraft takes off at some location x and continues to ascend at some average ascent velocity a_v and angle a_theta until it reaches a maximum altitude m_a. The aircraft would then continue at its m_a until it reaches a certain distance d_d from its destination, at which point it will begin its descent at some angle d_theta with an average descent velocity of d_v. Finally, the aircraft lands at destination y.
I would like the function to return a list of 3D points.
I am looking to implement this in either Python (preferred) or C#.
For illustration purposes:
Does anyone know how I can achieve this? Is there perhaps some open source project which does this? I have been looking for a while now, but have not found anything.
I recommend you to solve the problem in 2 independent steps so that the airplane does not pass through the ground :
Calculate the path on the surface of a sphere.
Interpolate the height along this path.
For 1. you can use the spherical interpolation techniques on Quaternions.
Quaternion slerp(Quaternion v0, Quaternion v1, double t) {
// Only unit quaternions are valid rotations.
// Normalize to avoid undefined behavior.
v0.normalize();
v1.normalize();
// Compute the cosine of the angle between the two vectors.
double dot = dot_product(v0, v1);
const double DOT_THRESHOLD = 0.9995;
if (fabs(dot) > DOT_THRESHOLD) {
// If the inputs are too close for comfort, linearly interpolate
// and normalize the result.
Quaternion result = v0 + t*(v1 – v0);
result.normalize();
return result;
}
// If the dot product is negative, the quaternions
// have opposite handed-ness and slerp won't take
// the shorter path. Fix by reversing one quaternion.
if (dot < 0.0f) {
v1 = -v1;
dot = -dot;
}
Clamp(dot, -1, 1); // Robustness: Stay within domain of acos()
double theta_0 = acos(dot); // theta_0 = angle between input vectors
double theta = theta_0*t; // theta = angle between v0 and result
Quaternion v2 = v1 – v0*dot;
v2.normalize(); // { v0, v2 } is now an orthonormal basis
return v0*cos(theta) + v2*sin(theta);
}
You didn't write any code, so I won't write any either. Python with math package is more than enough to solve this problem.
Required steps:
The plane should fly on a great circle. This means you only need one distance to describe X and Y.
You could place the origin at X and specify Y with a latitude.
Calculate the tangent of the Earth at X, and rotate by a_theta. Find the point where it reaches m_a altitude.
Calculate the tangent of the Earth at Y, and rotate by d_theta. Find the point where it reaches m_a altitude.
Draw an arc between the two previous points, with a radius of EarthRadius + m_a
Every coordinate is known in the 2D of the great circle, you just need to rotate them back to 3D coordinates.
For a list of 3D points, you don't need either a_v, d_v or d_d.
Similar to my recent question only this time I would like to move the object towards a vector and not another object.
Vector3 line = dalekList[i].direction;
float rotationDal = (float)(-Math.Atan2(dalekList[i].position.X, -dalekList[i].position.Z) / (2 * Math.PI));
Matrix dalekTransform = Matrix.CreateScale(GameConstants.DalekScalar) * Matrix.CreateRotationY(rotationDal) * Matrix.CreateTranslation(dalekList[i].position);
So I would need to put the rotation (rotationDal) into the CreateRotationY, only I'm not sure how to calculate that angle.
If the vector you want to "watch" is dalekList[i].direction, you should try to use Atan2 on it, instead of position.
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.