26
\$\begingroup\$

Let's say I have a line defined by two points, A and B, both in the form (x, y, z). These points represent a line in 3D space.

I also have a point P, defined in the same format, that isn't on the line.

How would I calculate the projection of that point on to the line? I'm aware of how to do this in 2D but 3D seems to have bugger all resources on it.

\$\endgroup\$
0

4 Answers 4

40
\$\begingroup\$

You simply need to project vector AP onto vector AB, then add the resulting vector to point A.

Here is one way to compute it:

A + dot(AP,AB) / dot(AB,AB) * AB

This formula will work in 2D and in 3D. In fact it works in all dimensions.

\$\endgroup\$
5
  • \$\begingroup\$ thank you mr Sam - how did you derive the above formula? \$\endgroup\$
    – BenKoshy
    May 4, 2017 at 5:31
  • 3
    \$\begingroup\$ I did not derive it, it is a well known formula you can find in many handbooks. \$\endgroup\$ May 4, 2017 at 12:13
  • \$\begingroup\$ Is there a sample on how to write that in a programming language like C++? \$\endgroup\$ Jul 5, 2019 at 14:47
  • 1
    \$\begingroup\$ @ViniciusdeMeloRocha dot would be a.x*b.x+a.y*b.y+a.z*b.z ... everything else is as straightforward as per-coordinate operation between vectors. \$\endgroup\$
    – Ocelot
    Aug 12, 2019 at 1:26
  • 2
    \$\begingroup\$ Sort of obvious, but maybe worth mentioning that the scalar dot(AP,AB) / dot(AB,AB) represents a parameterization along the AB vector. So if the points actually define a line segment, you can quickly determine if the projection lies within the segment (or maybe you need to clamp the projection to one of the endpoints) by comparing the scalar to range [0,1]. \$\endgroup\$
    – mcmcc
    Jun 10, 2021 at 15:41
6
\$\begingroup\$

Here's a fast and easy way to do it in python:

from numpy import *
def ClosestPointOnLine(a, b, p):
    ap = p-a
    ab = b-a
    result = a + dot(ap,ab)/dot(ab,ab) * ab
    return result

Use floats; If your vectors contain integers the division will be an integer division, and the results will be incorrect.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ /dot(ab,ab) is redundant \$\endgroup\$ Jul 4, 2017 at 23:20
  • 3
    \$\begingroup\$ Answering to Waldo, /dot(ab,ab) is ONLY redundant if ab has norm 1, so that dot(ab,ab) = 1. So unless you are 100% sure that is the case and you are looking for super hyper performance (assuming this function would have to be run 1M times / second), don't remove it... \$\endgroup\$ Mar 27, 2020 at 15:56
2
\$\begingroup\$

Just for an explanation about the formula from Sam Hocevar:

  1. If A,B are on the line, then the vector

$$\vec{u} = \vec{AB} / \left\|\vec{AB}\right\| $$

is the unit vector for this line (don't forget that \$\left\|\vec{AB}\right\| = \sqrt {\vec{AB} \cdot \vec{AB}} \$ or respectively \$\vec{AB} \cdot \vec{AB} = \left\|\vec{AB}\right\|^2\$).

The line equation follows:

$$k \cdot{\vec{u}} + A $$ for any \$k\$ scalar.

Then when projecting P on this line, the projected point \$I\$ is defined as perpendicular to the line's unit vector, that is:

$$ \vec{IP} \cdot \vec{u} = 0 $$ and $$ I = \vec{u} \cdot \left\|\vec{AI}\right\| + A $$

Since projecting P on the line is the dot product of \$\vec{AP}\$ and \$\vec{AB}\$, we can compute:

$$ \left\|\vec{AI}\right\| = \vec{AP} \cdot \vec{AB} / \left\|\vec{AB}\right\| $$

since \$\vec{AP} = \vec{AI} + \vec{IP}\$ and \$\vec{IP} \cdot \vec{u} = \vec{IP} \cdot \vec{AB} = 0\$, then:

$$ \vec{AP} \cdot \vec{AB} = \vec{AI} \cdot \vec{AB}=\vec{AI}\cdot\vec{u} *\left\|\vec{AB}\right\| = \left\|\vec{AI}\right\| * \left\|\vec{AB}\right\|$$

That means that only the colinear component of \$\vec{AP}\$ to \$\vec{AB}\$ is participating to the scalar result

Which finally gives:

$$ \begin{matrix} I_{x} \\ I_{y} \\ I_{z} \end{matrix} = \begin{matrix} A_{x} \\ A_{y} \\ A_{z} \end{matrix} + \begin{pmatrix} P_{x} - A_{x} \\ P_{y} - A_{y} \\ P_{z} - A_{z} \end{pmatrix} \cdot \begin{pmatrix} B_{x} - A_{x} \\ B_{y} - A_{y} \\ B_{z} - A_{z} \end{pmatrix} \cdot \begin{pmatrix} u_{x} \\ u_{y} \\ u_{z} \end{pmatrix} / \left\|\vec{AB}\right\|$$

This is the formula from Sam above.

\$\endgroup\$
0
\$\begingroup\$

on this one (i cannot cooment)

    ap = p-a
    ab = b-a
    result = a + dot(ap,ab)/dot(ab,ab) * ab
    return result

If you first make the "versor" of ab you do not need dot(ab,ab) right? or you just do the normal somehow you can rewrite as:

    ap = p-a
    ab = normal(b-a)
    result = a + dot(ap,ab) * ab
    return result

(I learned it with versors (normalized vectors) so was kind of weird.

\$\endgroup\$
2
  • \$\begingroup\$ This looks more like a question, not an answer. Try posting it as a question instead if you'd like feedback on whether you can do away with dot(ab,ab) this way. (Hint: consider how would you compute the versor without computing this dot product under the hood, or an equivalent/more expensive operation) \$\endgroup\$
    – DMGregory
    Jul 30 at 0:56
  • \$\begingroup\$ This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From Review \$\endgroup\$
    – liggiorgio
    Jul 30 at 10:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .