13

What is a formula to get a three dimensional vector B lying on the plane perpendicular to a vector A?

That is, given a vector A, what is a formula f(angle,modulus) which gives a vector that is perpendicular to A, with said modulus and rotated through an angle?

MaiaVictor
  • 51,090
  • 44
  • 144
  • 286
  • Two things: first, are we operating in two dimensions? Three? `n`? Secondly, your title says "perpendicular" but the question body says "rotated through an angle" - will this angle ever be other than ninety degrees? – AakashM Jun 21 '12 at 07:56
  • 1
    In 3 dimensions, there are infinitely many different vectors (a 2-dimensional vector space) perpendicular to a given vector. There is no single vector that a formula would generate. –  Jun 21 '12 at 11:45

6 Answers6

19

If the two vectors are perpendicular then their dot product is zero.

So: v1(x1, y1, z1), v2(x2, y2, z2).

=> x1 * x2 + y1 * y2 + z1 * z2 = 0

You know (x1, y1, z1). Put arbitrary x2 andy2 and you will receive the corresponding z2:

z1 * z2 = -x1 * x2 - y1 * y2
=> z2 = (-x1 * x2 - y1 * y2) / z1

Be aware if z1 is 0. Then you are in the plane.

Petar Minchev
  • 46,889
  • 11
  • 103
  • 119
  • 4
    Yes. You have one vector given `v1(x1, y1, z1)`. – Petar Minchev Jun 21 '12 at 06:36
  • 1
    As you mention, this fails if `z1` is 0, however the question remains perfectly mathematically valid in such a case. Find a vector perpendicular to [1,0,0], for instance. `z1` is 0, but [0,1,0] is very definitely a vector that is nevertheless perpendicular to [1,0,0]. See my answer for an alternate method. – sircolinton Apr 17 '17 at 15:33
15
function (a,b,c)
{
    return (-b,a,0)
}

But this answer is not numerical stable when a,b are close to 0.

To avoid that case, use:

function (a,b,c) 
{
    return  c<a  ? (b,-a,0) : (0,-c,b) 
}

The above answer is numerical stable, because in case c < a then max(a,b) = max(a,b,c), then vector(b,-a,0).length() > max(a,b) = max(a,b,c) , and since max(a,b,c) should not be close to zero, so is the vector. The c > a case is similar.

golopot
  • 10,726
  • 6
  • 37
  • 51
  • 3
    Just for reference, I looped through 10,000 random unit vectors and made sure that `dot(vec, above_func(vec)) == 0` for all of the vectors (I was too lazy to try to validate the stability analytically, so this was my next best option). It worked perfectly. – Steve Nov 27 '16 at 21:09
  • 4
    If the vector is for example (a=0, b=0, c=-1), c – Giovanni Funchal Dec 15 '16 at 10:30
  • 3
    @GiovanniFunchal Indeed, the statement "max(a,b,c) should not be close to zero" can be false even for "good" vectors, as the one you mention. The explanation should be corrected using the L-infinity norm instead of max, and c – Fabio Mar 29 '17 at 07:51
10

Calculate the cross product AxC with another vector C which is not collinear with A.

There are many possible directions in the plane perpendicular to A. If you don't really care, which one to pick, just create an arbitrary vector C not collinear with A:

if (A2 != 0 || A3 != 0)
    C = (1, 0, 0);
else
    C = (0, 1, 0);
B = A x C; 
Henrik
  • 23,186
  • 6
  • 42
  • 92
  • 1
    There is only one vector, I want a formula that gives a vector that is perpendicular to it in function of it's angle and length. – MaiaVictor Jun 21 '12 at 06:33
  • 1
    Dokkat, the reason you keep seing TWO vectors in the description is because given the first vector V1, there are many vectors V2 that are perpendicular to V1. In 2D space there are at least two such vectors with length 1. In 3D space there are infinitely many vectors perpendicular to V1! What you want to find is either one arbitrary V2 (perp to V1) or you want to detect if (V1,V2) are perpendicular. – Anders Forsgren Jun 21 '12 at 07:04
5

I believe that this should produce an arbitrary vector that is perpendicular to the given vector vec while remaining numerically stable regardless of the angle of vec (assuming that the magnitude of vec is not close to zero). Assume that Vec3D is a three dimensional vector of arbitrary numerical type.

Vec3D arbitrary_orthogonal(Vec3D vec)
{
  bool b0 = (vec[0] <  vec[1]) && (vec[0] <  vec[2]);
  bool b1 = (vec[1] <= vec[0]) && (vec[1] <  vec[2]);
  bool b2 = (vec[2] <= vec[0]) && (vec[2] <= vec[1]);

  return cross(vec, Vec3D(int(b0), int(b1), int(b2)));
}

Informal explanation

Exactly 1 and only 1 of the bools get set; bN gets set if dimension N has magnitude strictly less than all subsequent dimensions and not greater than all previous dimensions. We then have a unit vector with a single non-zero dimension that corresponds to a dimension of minimum magnitude in vec. The cross product of this with vec is orthogonal to vec by defn of cross product. Consider now that the cross product is numerically unstable only when the two vectors are very closely aligned. Consider that our unit vector is large in only a single dimension and that that dimension corresponds to the dimension where vec was small. It's thus guaranteed to be loosely orthogonal to vec before taking the cross product, with least orthogonality in the case where all dimensions of vec are equal. In this least-orthogonal case, we're still quite orthogonal given that our unit vector has all but one dimension 0 whereas vec has all equal. We thus avoid the unstable case of taking the cross product of two nearly-aligned vectors.

sircolinton
  • 6,536
  • 3
  • 21
  • 19
3

q4w56's is almost there for a robust solution. Problems: 1) Doesn't take in account scaling. 2) Doesn't compare the magnitude between two variables when it should.

scale = |x| + |y| + |z|

if scale == 0:
  return (0,0,0)

x = x/scale
y = y/scale
z = z/scale

if |x| > |y|:
  return (z, 0,-x)
else:
  return (0, z,-y)

Scaling is important when dealing with very large or very small numbers. Plus in general you are better off doing floating point operations on values between 0 and 1.

lessthanoptimal
  • 2,722
  • 2
  • 23
  • 25
2

One way would be to find a rotation transform from the positive z-axis (or any other axis) to your given vector. Then transform <modulus * cos(angle), modulus * sin(angle), 0> using this transform.

def getPerpendicular(v1,modulus,angle):
    v2 = vector(0,0,1)
    v1_len = v2.length()

    axis = v1.cross_product(v2)
    sinAngle = axis.length() / v1_len       # |u x v| = |u| * |v| * sin(angle)
    cosAngle = v1.dot_product(v2) / v1_len  # u . v = |u| * |v| * cos(angle)
    axis = axis.normalize()
    # atan2(sin(a), cos(a)) = a, -pi < a < pi
    angle = math.atan2(sinAngle, cosAngle)

    rotationMatrix = fromAxisAngle(axis, angle)

    # perpendicular to v2
    v3 = vector(modulus*cos(angle),modulus*sin(angle),0)

    return rotationMatrix.multiply(v3);

To calculate the rotation matrix, see this article: WP: Rotation matrix from axis and angle

Another method would be to use quaternion rotation. It's a little more to wrap your head around, but it's less numbers to keep track of.

Markus Jarderot
  • 86,735
  • 21
  • 136
  • 138