85

I have a 3D point (point_x,point_y,point_z) and I want to project it onto a 2D plane in 3D space which (the plane) is defined by a point coordinates (orig_x,orig_y,orig_z) and a unary perpendicular vector (normal_dx,normal_dy,normal_dz).

How should I handle this?enter image description here

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
George
  • 5,808
  • 15
  • 83
  • 160
  • From the answers below it seems there is confusion about what result you're looking for out of this projection: Is it the 3D point on the plane nearest to your point of interest? Is it a 2D point in the coordinate system of the plane? Something else? – tmpearce Mar 07 '12 at 17:13
  • 3
    It seems clear to me that he wants to find the point on the plane that is nearest to (point_x, point_y, point_z); that is the point labeled (planar_x, planar_y, planar_z) in the diagram. (All coordinates in the global coordinate system.) Therefore I believe the answer from @tmpearce is correct. – aldo Mar 07 '12 at 18:31
  • Consult the "orthographic projection" section on: http://en.wikipedia.org/wiki/3D_projection – Edward Loper Mar 07 '12 at 16:48

7 Answers7

125
  1. Make a vector from your orig point to the point of interest:

v = point-orig (in each dimension);

  1. Take the dot product of that vector with the unit normal vector n:

dist = vx*nx + vy*ny + vz*nz; dist = scalar distance from point to plane along the normal

  1. Multiply the unit normal vector by the distance, and subtract that vector from your point.

projected_point = point - dist*normal;

Edit with picture: I've modified your picture a bit. Red is v. dist is the length of blue and green, equal to v dot normal. Blue is normal*dist. Green is the same vector as blue, they're just plotted in different places. To find planar_xyz, start from point and subtract the green vector.

Projection of point onto plane

nooj
  • 578
  • 5
  • 13
tmpearce
  • 12,523
  • 4
  • 42
  • 60
  • Wrong. Imagine the point is exactly "above" the plane origin. Then the outcome of (1) points exactly as the normal vector. Dot-ing it with the normal vector produces non-zero result. But this is wrong! 3D point floating "above" the plane origin is projected into (0,0) – valdo Mar 07 '12 at 16:51
  • 1
    What's needed is the cross product, not dot. But this is also not enough. Please read my answer – valdo Mar 07 '12 at 16:52
  • 4
    @valdo Depends if you want the nearest distance to the plane, or a vertical point. I interpreted his post as wanting the nearest point. – tmpearce Mar 07 '12 at 16:55
  • 1
    Ok, if so - you're right. But I interpret it other way. The word "projection" usually means obtaining 2 coordinates on the plane. Nevertheless let's the question author decide what he/she meant. – valdo Mar 07 '12 at 17:01
  • @tmpearce:My point is above the plane,so i guess i need cross vector. – George Mar 07 '12 at 17:11
  • You project the vector onto the normal, multiply it by the normal, and subtract this from the vector. This gives you the component of your vector that is orthogonal to the normal, hence it lies in the plane. Well done, but IMHO this is not called "projection". You get a 3D vector which is guaranteed to lie in the plane. OTOH "projection" means finding a 2D vector in the plane coordinates. Nevermind. I upvoted anyway (since this is correct), it's just the question formulation is inaccurate. – valdo Mar 07 '12 at 21:25
  • 2
    You need to incorporate `d` to get the perpendicular distance from a point to a plane, otherwise you are assuming the plane passes through the origin. – bobobobo Jul 15 '13 at 18:40
  • @bobobobo You should try this method with the example you gave. A plane can be defined by `point,normal`; it doesn't have to be in the form `Ax+By+Cz+d=0` - it is easy to convert one form to the other. When you're given `point,normal`, my answer is a natural way to calculate the point. In fact, if you do the math, you can prove it is exactly equivalent to your method (without requiring conversion to the other notation for defining a plane). – tmpearce Jul 16 '13 at 10:18
  • By saying normal to the plane, you assume it to be the unit normal, right? I got confused because I thought this was valid for any normal, in which case it should be divided by length of n. – dev_nut Apr 07 '14 at 19:27
  • 1
    @dev_nut Yes, the `normal` vector in the question was already defined as unit length, and I continued to use it that way. Thanks for your comment though - it made me realize this is worth clarifying in the answer. I edited the answer to make it clear that the unit normal is the appropriate vector to use. – tmpearce Apr 07 '14 at 20:12
47

This is really easy, all you have to do is find the perpendicular (abbr here |_) distance from the point P to the plane, then translate P back by the perpendicular distance in the direction of the plane normal. The result is the translated P sits in the plane.

Taking an easy example (that we can verify by inspection) :

Set n=(0,1,0), and P=(10,20,-5).

enter image description here

The projected point should be (10,10,-5). You can see by inspection that Pproj is 10 units perpendicular from the plane, and if it were in the plane, it would have y=10.

So how do we find this analytically?

The plane equation is Ax+By+Cz+d=0. What this equation means is "in order for a point (x,y,z) to be in the plane, it must satisfy Ax+By+Cz+d=0".

What is the Ax+By+Cz+d=0 equation for the plane drawn above?

The plane has normal n=(0,1,0). The d is found simply by using a test point already in the plane:

(0)x + (1)y + (0)z + d = 0

The point (0,10,0) is in the plane. Plugging in above, we find, d=-10. The plane equation is then 0x + 1y + 0z - 10 = 0 (if you simplify, you get y=10).

A nice interpretation of d is it speaks of the perpendicular distance you would need to translate the plane along its normal to have the plane pass through the origin.

Anyway, once we have d, we can find the |_ distance of any point to the plane by the following equation:

enter image description here

There are 3 possible classes of results for |_ distance to plane:

  • 0: ON PLANE EXACTLY (almost never happens with floating point inaccuracy issues)
  • +1: >0: IN FRONT of plane (on normal side)
  • -1: <0: BEHIND plane (ON OPPOSITE SIDE OF NORMAL)

Anyway,

enter image description here

Which you can verify as correct by inspection in the diagram above

bobobobo
  • 64,917
  • 62
  • 258
  • 363
  • 6
    @bobobobo: You got me really confused there by writing "...just _add_ -10...". You _inexplicitly_ multiplied the result of __n__ ⋅ __p__ + _d_ by -1, just to write _add_ instead of _subtract_. :P I spent some time interpreting your answer, so I'll summarize it for others. Given a plane defined by normal __n__ and scalar _d_, the point __p__' on the plane closest to the given point __p__ can be found by: 1) __p__' = __p__ - (__n__ ⋅ __p__ + _d_) * __n__ If the plane is instead defined by normal __n__ and a point on the plane __o__ you suggest using: 2) _d_ = -__n__ ⋅ __o__ – Mr.H Jan 27 '17 at 14:34
24

This answer is an addition to two existing answers. I aim to show how the explanations by @tmpearce and @bobobobo boil down to the same thing, while at the same time providing quick answers to those who are merely interested in copying the equation best suited for their situation.

Method for planes defined by normal n and point o

This method was explained in the answer by @tmpearce.

Given a point-normal definition of a plane with normal n and point o on the plane, a point p', being the point on the plane closest to the given point p, can be found by:

  1. p' = p - (n ⋅ (p - o)) × n

Method for planes defined by normal n and scalar d

This method was explained in the answer by @bobobobo.

Given a plane defined by normal n and scalar d, a point p', being the point on the plane closest to the given point p, can be found by:

  1. p' = p - (np + d) × n

If instead you've got a point-normal definition of a plane (the plane is defined by normal n and point o on the plane) @bobobobo suggests to find d:

  1. d = -no

and insert this into equation 2. This yields:

  1. p' = p - (np - no) × n

A note about the difference

Take a closer look at equations 1 and 4. By comparing them you'll see that equation 1 uses n ⋅ (p - o) where equation 2 uses np - no. That's actually two ways of writing down the same thing:

  1. n ⋅ (p - o) = np - no = np + d

One may thus choose to interpret the scalar d as if it were a 'pre-calculation'. I'll explain: if a plane's n and o are known, but o is only used to calculate n ⋅ (p - o), we may as well define the plane by n and d and calculate np + d instead, because we've just seen that that's the same thing.

Additionally for programming using d has two advantages:

  1. Finding p' now is a simpler calculation, especially for computers. Compare:
  • using n and o: 3 subtractions + 3 multiplications + 2 additions
  • using n and d: 0 subtractions + 3 multiplications + 3 additions.
  1. Using d limits the definition of a plane to only 4 real numbers (3 for n + 1 for d), instead of 6 (3 for n + 3 for o). This saves ⅓ memory.
Mr.H
  • 676
  • 5
  • 9
  • 1
    This will be extremely helpful, except I'm not sure what . means vs * Could you show in simple terms what the operations boil down to on a per component basis for the vectors? – M2tM Jul 15 '22 at 05:30
  • 2
    Hi @M2tM. It's been a while since I wrote this answer. ⋅ means the [dot product](https://en.wikipedia.org/wiki/Dot_product). With * I meant to indicate the [cross product](https://en.wikipedia.org/wiki/Cross_product). (The latter was not immediately obvious to myself as well, but can be deduced from the fact that __p__ and __p'__ are in the same plane of which __n__ is the normal. This means that __p'__ = __p__ + some vector _orthogonal_ to __n__. Orthogonal vectors are a hint in the direction of the cross product.) I replaced any * in my answer with × for future readers. Thanks for noticing! – Mr.H Jul 17 '22 at 21:20
  • Awesome, thank you! Great thoughts, I figured . was dot product, but I wasn't getting what I expected so I wanted to dig in a bit and ask and it turns out indeed, I was treating the * as multiply instead of cross. Thanks for re-visiting this! – M2tM Jul 18 '22 at 22:05
  • 3
    @Mr.H how can we make a cross product between a scalar and a vector? A cross product is defined between two vectors if I'm not mistaken? (https://en.wikipedia.org/wiki/Cross_product) eqs. 2 and 4 contain (**n⋅p** + d) and (**n⋅p** - **n⋅o**), which are both scalar quantities. Am I misunderstanding? – Glxblt76 Sep 29 '22 at 08:25
13

It's not sufficient to provide only the plane origin and the normal vector. This does define the 3d plane, however this does not define the coordinate system on the plane.

Think that you may rotate your plane around the normal vector with regard to its origin (i.e. put the normal vector at the origin and "rotate").

You may however find the distance of the projected point to the origin (which is obviously invariant to rotation).

Subtract the origin from the 3d point. Then do a cross product with the normal direction. If your normal vector is normalized - the resulting vector's length equals to the needed value.

EDIT

A complete answer would need an extra parameter. Say, you supply also the vector that denotes the x-axis on your plane. So we have vectors n and x. Assume they're normalized.

The origin is denoted by O, your 3D point is p.

Then your point is projected by the following:

x = (p - O) dot x

y = (p - O) dot (n cross x)

valdo
  • 12,632
  • 2
  • 37
  • 67
  • :You are right , the point is above the plane ,and vertical to it.So , you say do "point-orig" ,then cross product of the previous vector with normal and the result is what i want? – George Mar 07 '12 at 17:03
  • Just to clarify for future readers: **n** cross **x** results in **y**, in case you have **y** given instead of **n**. The projection to 2D is just the dot-product projection on the normalized axis directions. – Tim Kuipers Feb 10 '15 at 09:25
2

Let V = (orig_x,orig_y,orig_z) - (point_x,point_y,point_z)

N = (normal_dx,normal_dy,normal_dz)

Let d = V.dotproduct(N);

Projected point P = V + d.N

Manlio
  • 10,768
  • 9
  • 50
  • 79
PermanentGuest
  • 5,213
  • 2
  • 27
  • 36
1

I think you should slightly change the way you describe the plane. Indeed, the best way to describe the plane is via a vector n and a scalar c

(x, n) = c

The (absolute value of the) constant c is the distance of the plane from the origin, and is equal to (P, n), where P is any point on the plane.

So, let P be your orig point and A' be the projection of a new point A onto the plane. What you need to do is find a such that A' = A - a*n satisfies the equation of the plane, that is

(A - a*n, n) = (P, n)

Solving for a, you find that

a = (A, n) - (P, n) = (A, n) - c

which gives

A' = A - [(A, n) - c]n

Using your names, this reads

c = orig_x*normal_dx + orig_y*normal_dy+orig_z*normal_dz;
a = point_x*normal_dx + point_y*normal_dy + point_z*normal_dz - c;
planar_x = point_x - a*normal_dx;
planar_y = point_y - a*normal_dy;
planar_z = point_z - a*normal_dz;

Note: your code would save one scalar product if instead of the orig point P you store c=(P, n), which means basically 25% less flops for each projection (in case this routine is used many times in your code).

bartgol
  • 1,703
  • 2
  • 20
  • 30
0

Let r be the point to project and p be the result of the projection. Let c be any point on the plane and let n be a normal to the plane (not necessarily normalised). Write p = r + m d for some scalar m which will be seen to be indeterminate if their is no solution. Since (p - c).n = 0 because all points on the plane satisfy this restriction one has (r - c).n + m(d . n) = 0 and so m = [(c - r).n]/[d.n] where the dot product (.) is used. But if d.n = 0 there is no solution. For example if d and n are perpendicular to one another no solution is available.

Peter
  • 393
  • 3
  • 15