What is the best (fastest) way to compute two vectors that are perpendicular to the third vector(X) and also perpendicular to each other?
This is ho
What I have done, provided that X<>0
or Y<>0
is
A = [-Y, X, 0]
B = [-X*Z, -Y*Z, X*X+Y*Y]
and then normalize the vectors.
[ X,Y,Z]·[-Y,X,0] = -X*Y+Y*X = 0
[ X,Y,Z]·[-X*Z,-Y*Z,X*X+Y*Y] = -X*X*Z-Y*Y*Z+Z*(X*X+Y*Y) = 0
[-Y,X,0]·[-X*Z,-Y*Z,X*X+Y*Y] = Y*X*Z+X*Y*Z = 0
This is called the nullspace of your vector.
If X=0
and Y=0
then A=[1,0,0]
, B=[0,1,0]
.
I think the minimum maximum magnatude out of all element in a unit vector is always greater than 0.577, so you may be able to get away with this:
-> Reduce the problem of finding a perpendicular vector to a 3D vector to a 2D vector by finding any element whose magnatude is greater than say 0.5, then ignore a different element (use 0 in its place) and apply the perpendicular to a 2D vector formula in the remaining elements (for 2D x-axis=(ax,ay) -> y-axis=(-ay,ax))
let x-axis be represented by (ax,ay,az)
if (abs(ay) > 0.5) {
y-axis = normalize((-ay,ax,0))
} else if (abs(az) > 0.5) {
y-axis = normalize((0,-az,ay))
} else if (abs(ax) > 0.5) {
y-axis = normalize((az,0,-ax))
} else {
error("Impossible unit vector")
}
This is the way to do it.
It's also probably the only way to do it. Any other way would be mathematically equivalent.
It may be possible to save a few cycles by opening the crossProduct computation and making sure you're not doing the same multiplications more than once but that's really far into micro-optimization land.
One thing you should be careful is of course the HELPER vector. Not only does it has to be not parallel to X but it's also a good idea that it would be VERY not parallel to X. If X and HELPER are going to be even somewhat parallel, your floating point calculation is going to be unstable and inaccurate. You can test and see what happens if the dot product of X and HELPER is something like 0.9999.
There is a method to find a good HELPER (really - it is ready to be your y_axis).
Let's X = (ax, ay, az). Choose 2 elements with bigger magnitude, exchange them, and negate one of them. Set to zero third element (with the least magnitude). This vector is perpendicular to X.
Example:
if (ax <= ay) and (ax <= az) then HELPER = (0, -az, ay) (or (0, az, -ay))
X*HELPER = 0*0 - ay*az + az*ay = 0
if (ay <= ax) and (ay <= az) then HELPER = (az, 0, -ay)
For a good HELPER vector: find the coordinate of X with the smallest absolute value, and use that coordinate axis:
absX = abs(X.x); absY = abs(X.y); absZ = abs(X.z);
if(absX < absY) {
if(absZ < absX)
HELPER = vector(0,0,1);
else // absX <= absZ
HELPER = vector(1,0,0);
} else { // absY <= absX
if(absZ < absY)
HELPER = vector(0,0,1);
else // absY <= absZ
HELPER = vector(0,1,0);
}
Note: this is effectively very similar to @MBo's answer: taking the cross-product with the smallest coordinate axis is equivalent to setting the smallest coordinate to zero, exchanging the larger two, and negating one.