Skip to content

Instantly share code, notes, and snippets.

@twolfson
Last active November 1, 2017 04:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save twolfson/207556d7ac0cd5d04fa283f6062841ab to your computer and use it in GitHub Desktop.
Save twolfson/207556d7ac0cd5d04fa283f6062841ab to your computer and use it in GitHub Desktop.
KaTeX files for "Practical applications of the dot product" blog post

gist-dot-product-katex

KaTeX files for "Practical applications of the dot product" blog post

Documentation

File structure

We are using BEM notation for our filenames where the "block" is our proof and "element" is the equation in the proof. For example:

  • "projection__cosTheta-equals-axisLength.tex" has proof "projection" and step "cosTheta-equals-axisLength"
    • We try to use camelCasing to associate relevant terms (e.g. length, operations, functions)

Compile images

To get our repo working locally, run the following:

# Clone our repo
git clone git@gist.github.com:207556d7ac0cd5d04fa283f6062841ab.git gist-dot-product-katex
cd gist-dot-product-katex

# Install our dependencies
npm install

# Compile our latest images
./build.sh

Proof

Here are ASCII based notes for the proof so we can easily modify/update/check ourselves

Projecting a vector onto a vector

Assume -pi <= theta <= pi. We have 3 scenarios

-pi/2 < theta < pi/2
   +
  /
 / theta
****-----+

theta = -pi/2, theta = pi/2

+
|
| theta
*--------+

-pi <= theta < -pi/2, pi/2 < theta <= pi

+
 \
  \ theta
****------+

Skipping over > pi and < -pi as they fall into the same buckets for theta

For -pi/2 < theta < pi/2, focus on 0 <= theta < pi/2, by the triangle sum theorem, we know for a given triangle with theta as an angle, we have:

alpha + beta + theta = pi
theta = pi - alpha - beta // Rearranged

0 <= pi - alpha - beta // Inequality substitution (1)
alpha + beta <= pi // Addition

pi - alpha - beta < pi/2 // Inequality substitution (2)
pi/2 - alpha - beta < 0 // Subtraction
pi/2 < alpha + beta // Addition

Let's build our triangle where:

axisProjectionVector = z * axisVector

such that the angle between (-axisProjectionVector) and (offshootVector - axisProjectionVector) is 90 degrees

   +
  /|
 / |
****-----+

// Handwaving over proof of triangle existence, I feel like we require a low-level Euclidean axiom but don't know which

Then by the trigonometric identity, we can state

cos(theta) = |axisProjectionVector| / |offshootVector|

For -pi/2 < theta < 0, we build a similar triangle and yield the same cos(θ) equation.

Else if theta = pi/2, then our offshoot vector is orthogonal to our axis and there's projection vector is the zero vector

Else if -pi <= theta < -pi/2 and pi/2 < theta <= pi, focus on pi/2 < theta < pi. Then we build a right triangle with phi similar to above and by cosine identity:

cos(phi) = |axisProjectionVector| / |offshootVector|

  +
   \
phi \ theta
 ****------+

phi = pi - theta

cos(pi - theta) = |axisProjectionVector| / |offshootVector|
cos(pi)cos(theta) + sin(pi)sin(theta) = |axisProjectionVector| / |offshootVector|
-1*cos(theta) + 0*sin(theta)
-cos(theta) = |axisProjectionVector| / |offshootVector|
cos(theta) = - |axisProjectionVector| / |offshootVector|

For -pi <= theta < -pi/2, we build a similar triangle and yield the same cos(θ) equation.

If cos(theta) >= 0, then let d = 1. Otherwise, let d = -1

This allows us to restate:

cos(theta) = d |axisProjectionVector| / |offshootVector|

If cos(theta) >= 0, then we have scenario where projection is positive with axis. Thus
  axisProjectionUnitVector = 1 * axisUnitVector
Else (cos(theta) < 0), the nwe have scenario where projection is negative with axis. Thus
  axisProjectionUnitVector = -1 * axisUnitVector

We can rewrite this via `d` to:
  axisProjectionUnitVector = d * axisUnitVector

Knowns:
  axisUnitVector = axisVector / |axisVector|
  axisProjectionVector = axisProjectionUnitVector * |axisProjectionVector|
  dot(axisVector, offshootVector) = |axisVector| * |offshootVector| * cos(theta)
Helpful derivation:
  dot(axisVector, offshootVector) = |axisVector| * |offshootVector| * cos(theta)
    = |axisVector| * |offshootVector| * (d * |axisProjectionVector|/|offshootVector|) // Substituion
    = d * |axisVector| * |axisProjectionVector| // Cancellation
  |axisProjectionVector| = dot(axisVector, offshootVector) / (d * |axisVector|) // Division
Goal: axisProjectionVector
  axisProjectionVector = axisProjectionUnitVector * |axisProjectionVector|
    = d * axisUnitVector * (dot(axisVector, offshootVector) / (d * |axisVector|)) // Subtitution
    = axisUnitVector * dot(axisVector, offshootVector) / |axisVector| // Cancellation
    = axisVector / |axisVector| * dot(axisVector, offshootVector) / |axisVector| // Substitution
    = axisVector * dot(axisVector, offshootVector) / |axisVector|^2 // Consolidation

Finding the orthogonal component of a vector to another vector

offshootVector = axisProjectionVector + axisComplementVector // Resolved by right triangle
axisComplementVector = offshootVector - axisProjectionVector // Subtraction and rearranged sides

// Sanity explanation
offshootVector = c_axis*axisUnitVector + c_2*v_2 + ... + c_n*v_n
axisProjectionVector = c_axis * axisUnitVector + 0*v2 + ... + 0*v_n // By definition of component
Thus:
offshootVector - axisProjectionVector = 0*axisUnitVector + c_2*v2 + ... + c_n*v_n

Finding the shortest distance from a point to a segment

We have 3 scenarios
"Behind" v1
pt
 *
  v1--------v2

Between v1 and v2
     pt
     *
v1---*----v2

"Ahead of" v2
             pt
            *
v1--------v2

In the first/last, the shortest line is from our point to the closest vertex
In the middle, it's the shortest line to our segment (aka axisComplementVector)

To determine the scenario, we will resolve the axial component of offshootVector via the same calculation as `axisProjectionVector`. The key difference being we aren't multiplying by the axisVector

ratio = dot(axisVector, offshootVector) / |axisVector|^2

This gives us the ratio for how much of offshootVector is projecting onto axisVector

If it's negative, then we know it's "behind" v1

length = |offshootVector|

If it's greater than 1, then we know it's longer than our axis vector and ths "ahead" of v2

length = |offshootVector|

If it's in-between, then use vector addition to resolve axisComplementVector again and return its length

length = |offshootComplementVector| // Ideal definition
length = |offshootVector - axisProjectionVector| // Vector addition expansion
length = |offshootVector - axisVector * dot(axisVector, offshootVector) / |axisVector|^2|| // Substituion for axisProjectionVector calculation
#!/usr/bin/env bash
# Exit on first error, unset variable, or pipe failure
set -euo pipefail
# Generate our images from .tex files in parallel (e.g. foo.tex -> foo.tex.png)
for filepath in *.tex; do
# If our image doesn't exist yet or its source code is newer, then generate it
if (! test -e "$filepath.png") || (test "$filepath" -nt "$filepath.png"); then
echo "Generating image for $filepath" 1>&2
katex-screenshot "$filepath" "$filepath.png" &
fi
done
# Wait for all our generation to complete
wait
\vec{ \textit{vector} };\quad \|\vec{ \textit{vector} }\| = \textit{length}(\vec{ \textit{vector} });\quad \hat{ \textit{vector} } = \frac{ 1 }{ \|\vec{ \textit{vector} }\| } \vec{ \textit{vector} }\enspace (\textnormal{i.e. } \|\hat{vector}\| = 1)
\vec{ \textit{axisComplement} } = \vec{ \textit{offshoot} } - \vec{ \textit{axisProjection} }
\vec{ \textit{axisProjection} } = c_{ \textit{axis} }\vec{ \textit{axis} } + 0\vec{v}_2 + ... + 0\vec{v}_n
\vec{ \textit{offshoot} } = \vec{ \textit{axisProjection} } + \vec{ \textit{axisComplement} }
\vec{ \textit{offshoot} } = c_{ \textit{axis} }\vec{ \textit{axis} } + c_2\vec{v}_2 + ... + c_n\vec{v}_n
\vec{ \textit{offshoot} } - \vec{ \textit{axisProjection} } = 0\vec{ \textit{axis} } + c_2\vec{v}_2 + ... + c_n\vec{v}_n
{
"name": "gist-dot-product-katex",
"version": "1.0.0",
"description": "KaTeX files for \"Practical applications of the dot product\" blog post",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Todd Wolfson <todd@twolfson.com> (http://twolfson.com/)",
"license": "Unlicense",
"devDependencies": {
"katex-screenshot": "~1.0.0"
}
}
\vec{ \textit{axis} }\, \cdot\, \vec{ \textit{offshoot} } = \|\vec{ \textit{axis} }\| \|\vec{ \textit{offshoot} }\|\, \textit{cos}(\theta)
\vec{ \textit{axisProjection} } = \vec{ \textit{axis} } \cdot \frac{ \vec{ \textit{axis} }\, \cdot\, \vec{ \textit{offshoot} } }{ \|\vec{ \textit{axis} }\|^2 }
\vec{ \textit{axisProjection} } = \hat{ \textit{axisProjection} } \cdot \|\vec{ \textit{axisProjection} }\|
\hat{ \textit{axisProjection} } = \begin{cases}
\phantom{-}1 * \hat{ \textit{axis} } & \textnormal{if } \textit{cos}(\theta) \geq 0
\\
-1 * \hat{ \textit{axis} } & \textnormal{if } \textit{cos}(\theta) < 0
\end{cases}
\|\vec{ \textit{axisProjection} }\| = \frac{ \vec{ \textit{axis} }\, \cdot\, \vec{ \textit{offshoot} } }{ d\, \|\vec{ \textit{axis} }\| }
\hat{ \textit{axisProjection} } = d \cdot \hat{ \textit{axis} }
\hat{ \textit{axis} } = \frac{ \vec{ \textit{axis} } }{ \|\vec{ \textit{axis} }\| }
\hat{ \textit{axis} } = \hat{ \textit{axisProjection} }
\textit{cos}(\varphi) = \frac{ \|\vec{ \textit{axisProjection} }\| }{ \|\vec{ \textit{offshoot} }\| }
\textit{cos}(\pi - \theta) = \frac{ \|\vec{ \textit{axisProjection} }\| }{ \|\vec{ \textit{offshoot} }\| }
\textit{cos}(\theta) = \frac{ \|\vec{ \textit{axisProjection} }\| }{ \|\vec{ \textit{offshoot} }\| }
\textit{cos}(\theta) = d\, \frac{ \|\vec{ \textit{axisProjection} }\| }{ \|\vec{ \textit{offshoot} }\| }
\textit{cos}(\theta) = -\frac{ \|\vec{ \textit{axisProjection} }\| }{ \|\vec{ \textit{offshoot} }\| }
= \frac{ \vec{ \textit{axis} } }{ \|\vec{ \textit{axis} }\| } \cdot \frac{ \vec{ \textit{axis} }\, \cdot\, \vec{ \textit{offshoot} } }{ \|\vec{ \textit{axis} }\| }
= \vec{ \textit{axis} } \cdot \frac{ \vec{ \textit{axis} }\, \cdot\, \vec{ \textit{offshoot} } }{ \|\vec{ \textit{axis} }\|^2 }
= \|\vec{ \textit{axis} }\| \|\vec{ \textit{offshoot} }\| \left( d\, \frac{ \|\vec{ \textit{axisProjection} }\| }{ \|\vec{ \textit{offshoot} }\| } \right)
= \hat{ \textit{axis} } \cdot \frac{ \vec{ \textit{axis} }\, \cdot\, \vec{ \textit{offshoot} } }{ \|\vec{ \textit{axis} }\| }
= d\, \|\vec{ \textit{axis} }\| \|\vec{ \textit{axisProjection} }\|
= d \cdot \hat{ \textit{axis} } \cdot \frac{ \vec{ \textit{axis} }\, \cdot\, \vec{ \textit{offshoot} } }{ d\, \|\vec{ \textit{axis} }\| }
-\textit{cos}(\theta) = \frac{ \|\vec{ \textit{axisProjection} }\| }{ \|\vec{ \textit{offshoot} }\| }
\textit{length} = \|\vec{ \textit{blueGreen} }\|
\textit{length} = \|\vec{ \textit{offshoot} } - \vec{ \textit{axisProjection} }\|
\textit{length} = \|\vec{ \textit{offshoot} } - \vec{ \textit{axis} } \cdot \frac{ \vec{ \textit{axis} }\, \cdot\, \vec{ \textit{offshoot} } }{ \|\vec{ \textit{axis} }\|^2 }\|
\textit{length} = \|\vec{ \textit{offshoot} } - \vec{ \textit{axis} } \cdot \textit{ratio}\|
\textit{length} = \|\vec{ \textit{offshootComplement} }\|
\textit{length} = \|\vec{ \textit{orangeGreen} }\|
\textit{length} = \begin{cases}
\|\vec{ \textit{blueGreen} }\| & \textit{ratio} < 0
\\
\|\vec{ \textit{offshoot} } - \vec{ \textit{axis} } \cdot \textit{ratio} \| & 0 \leq \textit{ratio} \leq 1
\\
\|\vec{ \textit{orangeGreen} }\| & \textit{ratio} > 1
\end{cases}
\textit{ratio} = \frac{ \vec{ \textit{axis} }\, \cdot\, \vec{ \textit{offshoot} } }{ \|\vec{ \textit{axis} }\|^2 }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment