[Math] How to find the coordinates where the altitude of a triangle intersects the base in 3 dimensions

geometrytrigonometry

Assuming I know three completely random coordinates in 3d space that correspond with vertices of a triangle, how can I then find the point at which the altitude intersects the base? I know how to calculate the side lengths of the triangle and have an idea of how to solve my problem, but I become stuck when challenged with finding the height of the altitude.

Best Answer

Let $\vec{p}_0$ and $\vec{p}_1$ be the vertices at the base of the triangle, with $\vec{p}_2$ the vertex at apex.

Define $\hat{b}$ as the base unit vector, $$\hat{b} = \frac{\vec{p}_1 - \vec{p}_0}{\left\lVert\vec{p}_1 - \vec{p}_0\right\rVert}$$ where $\lVert\vec{p}_1 - \vec{p}_0\rVert = \sqrt{(\vec{p}_1-\vec{p}_0)\cdot(\vec{p}_1-\vec{p}_0)}$.

If we project the vector from first base vertex to apex vertex, $(\vec{p}_2 - \vec{p}_0)$, to the base unit vector $\hat{b}$ (using vector dot product), we get the (signed) distance on the base to a point directly below the apex vertex. The distance between that point and the apex is the height $h$ of the triangle: $$h = \left\lVert (\vec{p}_2 - \vec{p}_0) - \hat{b} ( \hat{b} \cdot (\vec{p}_2 - \vec{p}_0) \right\rVert$$ Substituting $\hat{b}$ we get $$h = \left\lVert \vec{p}_2 - \vec{p}_0 - \frac{\left(\vec{p}_1 - \vec{p}_0\right)\left(\left( \vec{p}_1 - \vec{p}_0 \right)\cdot\left(\vec{p}_2 - \vec{p}_0 \right)\right)}{\left(\vec{p}_1 - \vec{p}_0\right)\cdot\left(\vec{p}_1 - \vec{p}_0\right)} \right\rVert$$

In pseudocode, a function that calculates the height given the base vertex coordinates x0,y0,z0 and x1,y1,z1 and the apex vertex coordinates x2,y2,z2, is

function triangle_height(x0,y0,z0, x1,y1,z1, x2,y2,z2):
    # x0,y0,z0   First base vertex 
    # x1,y1,z1   Second base vertex
    # x2,y2,z2   Apex vertex

    tmp0 = x1 - x0
    tmp1 = y1 - y0
    tmp2 = z1 - z0
    tmp3 = x2 - x0
    tmp4 = y2 - y0
    tmp5 = z2 - z0

    tmp6 = tmp0*tmp0 + tmp1*tmp1 + tmp2*tmp2
    if (tmp6 <= 0) then
        # Degenerate triangle; (x0,y0,z0) = (x1,y1,z1).
        # It is not a triangle, but a line (or a point).
        # This returns the length of the line,
        # or 0 if (x0,y0,z0) = (x1,y1,z1) = (x2,y2,z2).
        hh = tmp3*tmp3 + tmp4*tmp4 + tmp5*tmp5

    else
        tmp7 = (tmp0*tmp3 + tmp1*tmp4 + tmp2*tmp5) / tmp6

        # tmpx,tmpy,tmpz is the apex vector, perpendicular to base
        tmpx = tmp3 - tmp7 * tmp0
        tmpy = tmp4 - tmp7 * tmp1
        tmpz = tmp5 - tmp7 * tmp2

        hh = tmpx*tmpx + tmpy*tmpy + tmpz*tmpz
    end if

    return sqrt(hh)

The above pseudocode is hand-tuned from Maple codegen module output from the vector formula, adding the check. Although math says tmp6 cannot be negative, it is better practice to check if it is nonpositive instead. You see, any floating-point comparison against an exact value is always suspect, even if the target value is zero.


The Wolfram Mathworld article on 3D Point-Line Distance mentions in passing an even simpler formula:

$$h = \frac{\left\lVert\left(\vec{p}_1 - \vec{p}_0\right)\times\left(\vec{p}_0 - \vec{p}_2\right)\right\rVert}{\left\lVert\vec{p}_1 - \vec{p}_0\right\rVert}$$

(Note the different numbering here, $2\gets 0$, $0\gets 1$, $1\gets 2$, compared to the Mathworld page.)

In pseudocode:

function triangle_height(x0,y0,z0, x1,y1,z1, x2,y2,z2):
    # x0,y0,z0   First base vertex 
    # x1,y1,z1   Second base vertex
    # x2,y2,z2   Apex vertex

    tmp0 = x1 - x0
    tmp1 = y1 - y0
    tmp2 = z1 - z0
    tmp3 = x0 - x2
    tmp4 = y0 - y2
    tmp5 = z0 - z2
    tmp6 = tmp0*tmp0 + tmp1*tmp1 + tmp2*tmp2
    if (tmp6 <= 0) then
        hh = tmp3*tmp3 + tmp4*tmp4 + tmp5*tmp5
    else
        tmp7 = tmp5*tmp1 - tmp2*tmp4
        tmp8 = tmp2*tmp3 - tmp5*tmp0
        tmp9 = tmp4*tmp0 - tmp1*tmp3
        hh = (tmp7*tmp7 + tmp8*tmp8 + tmp9*tmp9) / tmp6
    end if

    return sqrt(hh)

Both functions are mathematically the same (but using floating point numbers, the rounding errors may differ; so do not expect the results to be exactly the same when using floating-point math). I don't see any significant difference between the two. In particular, both need twelve multiplications and one division. The latter does need fewer additions/subtractions in the non-degenerate-triangle cases, so it might be a tiny bit faster.