I have two perpendicular vectors - v1 and v2. How to rotate v1 around v2 with a given angle?
Unity provides a method that’s really useful here. It’s called Quaternion.AngleAxis. You provide it the axis vector along with the degrees you want to rotate by. It then gives you a quaternion which represents a rotation. You then apply this rotation to the vector that you want to rotate around the axis. Here is some sample code. I briefly tested this so it should work though I can’t guarantee it handles all edge cases. I’ve also included a method which will rotate a point around a line which you can define with 2 other points (this does the work of shifting points to the origin and then shifting back for you). Here’s a tip: you can rotate a vector by a quaternion simply by multiplying the quaternion by the vector. Remember a quaternion is just a way of representing a rotation.
static Vector3 RotateVectorAroundAxis(Vector3 vector, Vector3 axis, float degrees)
{
return Quaternion.AngleAxis(degrees, axis) * vector;
}
static Vector3 RotatePointAroundLine(Vector3 pointToRotate, Vector3 pointOnLine0, Vector3 pointOnLine1, float degrees)
{
Vector3 localVector = pointToRotate - pointOnLine0;
Vector3 axis = pointOnLine1 - pointOnLine0;
Vector3 rotatedVector = RotateVectorAroundAxis(localVector, axis, degrees);
return rotatedVector + pointOnLine0;
}
@David_Simoniants, I must admit this is the close of my day at 3am, and I’ll try to visit sometime on Saturday to clean this up with a test, so I’m not testing this tonight, but here’s the plan:
Fundamentally this is about performing a rotation in a local coordinate system, then transforming that into the global coordinate system (or at least the parent of this one).
Two perpendicular vectors conveniently form their own local coordinate system. The green perpendicular indicator between the two black vectors depicted define the local axes of what I assume to be X and Y coordinates. If these are both normalized vectors, the length of both is 1. In their local coordinate systems, under that condition, one vector has an x of 0 and a y of 1, while the other has a y of 0 and an x of 1. However, if they’re not normalized, then the first of the two black vectors (the one pointing up, which we choose arbitrarily and is somewhat obvious by your diagram) will have an x coordinate of zero, but it’s y is the vector’s length. Likewise, the other vector has an x coordinate of it’s length, and a y of zero. This second vector is what you’re rotating to the red vector.
The rotation to the red vector is, therefore, a rotation in the local XZ plane, which can be determined by typical rotation of a point, as in (where vs is the source vector in local coordinates, it’s x is the vectors length, and v1 will be the local coordinate result)
v1.x = cos(a) * vs.x - sin(a) * vs.y;
v1.z = sin(a) * vs.x + cos(a) * vs.y;
Yet, the y of the source black vector is zero in the local coordinate system (x is the vector’s length), so this can be simplified in code to
v1.x = cos(a) * vs.x;
v1.z = sin(a) * vs.x;
At this point you have v1 in the local xz plane, but you need that to be transformed into the coordinate system implied by the attitude of either source vector. In the local coordinate system the source vectors imply, both would be on either the X axis or the Y axis. The source for v1 is the one on the x axis, and it’s convenient enough to use, but this would work from either of the source vectors because they are defined as perpendiculars in their local coordinate system.
This means that the source vector’s global positions for it’s own coordinates can be used to define the Euler angles of each pertinent plane, from which you could fashion a quaternion via arctan2, and use that to rotate the v1 calculated above (which is in the local coordinate system) into the global coordinate system (your goal).
If the source vectors are normalized, such that their lengths are 1, you’re in luck. You’re not entirely out of luck if they aren’t, it’s just a little more work.
Think of this perpendicular pair as being rotated from an aligned coordinate system (their local), such that both started out on the X and Y axis, but have been rotated into their now global coordinate attitude. Since they are unit vectors, they imply tangents relative to the rotation described. That is, in the global xy plane, their coordinates show the tangent which describes their angle in the xy plane. You can use Mathf.Arctan2( y, x) to find that angle (in radians).
Mathf.Arctan2( y, x ) puts the y coordinate first (other libraries put x first, and this seems odd, but it’s related to the fact that tangent is y/x, whereas arctangent is the angle of that tangent. To convert the return value into degrees (required for Quaternion.Euler), multiply by Mathf.Rad2Deg.
Mathf.Arctan2( y, x) provides the angle on the Z axis, the 3rd parameter required for Quaternion.Euler( x, y, z ). You’ll need to use Mathf.Arctan2 for each axis, selecting xz coordinates for the y axis, etc.
If, however, these are not normalized vectors, so they are not unit vectors, what you require is the deflection of the coordinates. Say the source vector from which you obtained v1 (the one that’s on the X axis in it’s local coordinate system) is length 5. The x coordinate for that vector in it’s local coordinate system is 5, it’s y is zero. Now, let’s say that in the global coordinate system the x coordinate of that vector is 4.5. It’s attitude is such that x was moved from 5 to 4.5. It’s length is 5, and so if this were a unit vector, a length of 1, it would have moved from 1 to ( 4.5 / 5 ), or 0.9. You can now use 0.9 as if this were a normalized vector to continue with Mathf.Arctan2( y, x ). Of course, it is likely that in the global coordinate system, y is no longer zero, and you’ll need to perform the same calculation for y.
Again, doing so for each axis from which to form a quaternion, which you then use to rotate the v1 calculated above with
v1 = q * v1;
Where q is the quaternion you fashion.
If both vectors are in global space, @joshrs926 's answer can be simplified to:
v1 = Quaternion.AngleAxis(aGivenAngle, v2) * v1;
Where v1, v2, and aGivenAngle are from OPs diagram.