Medium3D Geometry

Angle Between 3D Vectors

3D Geometry

Medium

Problem

Compute the angle in radians between two 3D vectors. First obtain their cosine:

c=\frac{\mathbf{v}\cdot\mathbf{w}}{\lVert\mathbf{v}\rVert_2\lVert\mathbf{w}\rVert_2}

Then recover the angle:

\theta=\arccos(c)

Clamp c to [-1,1] before applying arccos to protect against floating-point error. If either vector has zero norm, the angle is undefined, so return np.nan. Otherwise return a Python float in [0,\pi].

Theory

The angle between two vectors is a fundamental concept in geometry and linear algebra. It measures how much two vectors "point in different directions" and ranges from 0 (parallel) to \pi radians or 180 degrees (anti-parallel).

This concept is essential in 3D graphics, physics simulations, robotics, and machine learning.


The Dot Product Connection

The angle between vectors is intimately connected to the dot product. For vectors \mathbf{a} and \mathbf{b}:

\mathbf{a} \cdot \mathbf{b} = ||\mathbf{a}|| \cdot ||\mathbf{b}|| \cdot \cos(\theta)

where:


The Formula for the Angle

Solving for \theta:

\cos(\theta) = \frac{\mathbf{a} \cdot \mathbf{b}}{||\mathbf{a}|| \cdot ||\mathbf{b}||}

\theta = \arccos\left(\frac{\mathbf{a} \cdot \mathbf{b}}{||\mathbf{a}|| \cdot ||\mathbf{b}||}\right)

The result is in radians. To convert to degrees: \theta_{deg} = \theta_{rad} \times \frac{180}{\pi}


Computing the Dot Product in 3D

For 3D vectors \mathbf{a} = (a_x, a_y, a_z) and \mathbf{b} = (b_x, b_y, b_z):

\mathbf{a} \cdot \mathbf{b} = a_x b_x + a_y b_y + a_z b_z

This is the sum of component-wise products.


Computing the Magnitude in 3D

The magnitude (Euclidean norm) of a 3D vector:

||\mathbf{a}|| = \sqrt{a_x^2 + a_y^2 + a_z^2}

||\mathbf{b}|| = \sqrt{b_x^2 + b_y^2 + b_z^2}


Step-by-Step Procedure

Step 1: Compute the dot product \mathbf{a} \cdot \mathbf{b}

Step 2: Compute the magnitudes ||\mathbf{a}|| and ||\mathbf{b}||

Step 3: Compute \cos(\theta) = \frac{\mathbf{a} \cdot \mathbf{b}}{||\mathbf{a}|| \cdot ||\mathbf{b}||}

Step 4: Apply inverse cosine: \theta = \arccos(\cos(\theta))


Worked Example

Vectors:

Step 1: Dot product

\mathbf{a} \cdot \mathbf{b} = 1(4) + 2(5) + 3(6) = 4 + 10 + 18 = 32

Step 2: Magnitudes

||\mathbf{a}|| = \sqrt{1^2 + 2^2 + 3^2} = \sqrt{1 + 4 + 9} = \sqrt{14} \approx 3.742

||\mathbf{b}|| = \sqrt{4^2 + 5^2 + 6^2} = \sqrt{16 + 25 + 36} = \sqrt{77} \approx 8.775

Step 3: Cosine of angle

\cos(\theta) = \frac{32}{3.742 \times 8.775} = \frac{32}{32.83} \approx 0.9746

Step 4: Angle

\theta = \arccos(0.9746) \approx 0.226 \text{ radians} \approx 12.9°

The vectors are nearly parallel (small angle).


Special Cases

Parallel vectors (\theta = 0):

\cos(\theta) = 1 \implies \mathbf{a} \cdot \mathbf{b} = ||\mathbf{a}|| \cdot ||\mathbf{b}||

Vectors point in the same direction.

Perpendicular vectors (\theta = 90°):

\cos(\theta) = 0 \implies \mathbf{a} \cdot \mathbf{b} = 0

Vectors are orthogonal.

Anti-parallel vectors (\theta = 180°):

\cos(\theta) = -1 \implies \mathbf{a} \cdot \mathbf{b} = -||\mathbf{a}|| \cdot ||\mathbf{b}||

Vectors point in opposite directions.


Example: Perpendicular Vectors

Vectors:

Dot product:

\mathbf{a} \cdot \mathbf{b} = 1(0) + 0(1) + 0(0) = 0

Cosine:

\cos(\theta) = \frac{0}{1 \times 1} = 0

Angle:

\theta = \arccos(0) = \frac{\pi}{2} = 90°

The x and y axes are perpendicular, as expected.


Example: Opposite Vectors

Vectors:

Dot product:

\mathbf{a} \cdot \mathbf{b} = 1(-1) + 2(-2) + 3(-3) = -1 - 4 - 9 = -14

Magnitudes:

||\mathbf{a}|| = ||\mathbf{b}|| = \sqrt{14}

Cosine:

\cos(\theta) = \frac{-14}{\sqrt{14} \times \sqrt{14}} = \frac{-14}{14} = -1

Angle:

\theta = \arccos(-1) = \pi = 180°


Using Unit Vectors

If vectors are already normalized (unit length), the formula simplifies:

\cos(\theta) = \hat{\mathbf{a}} \cdot \hat{\mathbf{b}}

No need to divide by magnitudes since ||\hat{\mathbf{a}}|| = ||\hat{\mathbf{b}}|| = 1.

This is why normalizing vectors is common in graphics and physics.


Numerical Stability

Due to floating-point errors, \cos(\theta) might slightly exceed the range [-1, 1].

Problem: \arccos(1.0000001) is undefined.

Solution: Clamp the value:

\cos(\theta) = \max(-1, \min(1, \cos(\theta)))

Then apply \arccos safely.


Alternative: Using Cross Product

The angle can also be found using the cross product magnitude:

||\mathbf{a} \times \mathbf{b}|| = ||\mathbf{a}|| \cdot ||\mathbf{b}|| \cdot \sin(\theta)

Combined with the dot product:

\tan(\theta) = \frac{||\mathbf{a} \times \mathbf{b}||}{\mathbf{a} \cdot \mathbf{b}}

\theta = \arctan2(||\mathbf{a} \times \mathbf{b}||, \mathbf{a} \cdot \mathbf{b})

This method is more numerically stable for very small or very large angles.


The Cross Product in 3D

For completeness, the cross product is:

\mathbf{a} \times \mathbf{b} = \begin{pmatrix} a_y b_z - a_z b_y \\ a_z b_x - a_x b_z \\ a_x b_y - a_y b_x \end{pmatrix}

And its magnitude:

||\mathbf{a} \times \mathbf{b}|| = \sqrt{(a_y b_z - a_z b_y)^2 + (a_z b_x - a_x b_z)^2 + (a_x b_y - a_y b_x)^2}


Applications in 3D Graphics

Lighting calculations:

The angle between surface normal and light direction determines brightness:

\text{intensity} = \max(0, \cos(\theta)) = \max(0, \mathbf{n} \cdot \mathbf{l})

Collision detection:

Angle between velocity and surface normal affects reflection.

Camera orientation:

Angle between view direction and object determines visibility.


Applications in Machine Learning

Cosine similarity:

In high-dimensional spaces, cosine similarity measures vector similarity:

\text{similarity} = \cos(\theta) = \frac{\mathbf{a} \cdot \mathbf{b}}{||\mathbf{a}|| \cdot ||\mathbf{b}||}

Used in text embeddings, recommendation systems, and clustering.

Angular distance:

\text{distance} = \frac{\theta}{\pi} = \frac{\arccos(\text{similarity})}{\pi}


Signed vs Unsigned Angle

The formula gives an unsigned angle in [0, \pi].

For a signed angle (determining rotation direction), you need additional context:

In 2D, signed angle is straightforward using \arctan2.


Generalizing to Higher Dimensions

The same formula works in any dimension:

\cos(\theta) = \frac{\mathbf{a} \cdot \mathbf{b}}{||\mathbf{a}|| \cdot ||\mathbf{b}||} = \frac{\sum_i a_i b_i}{\sqrt{\sum_i a_i^2} \cdot \sqrt{\sum_i b_i^2}}

The geometric interpretation of "angle" extends to n-dimensional space.


Edge Cases

Zero vector:

If either vector is zero, the angle is undefined. Check for ||\mathbf{a}|| = 0 or ||\mathbf{b}|| = 0 before computing.

Nearly parallel vectors:

When \cos(\theta) \approx 1, \arccos can be numerically unstable. The \arctan2 method is more robust.

Very small vectors:

Normalize carefully to avoid division by very small numbers.

Examples

Example 1

Input
v = [1, 0, 0], w = [0, 1, 0]
Output
1.570796
Explanation
Orthogonal vectors have cosine zero and an angle of pi divided by two.

Example 2

Input
v = [1, 2, 3], w = [2, 4, 6]
Output
0

Hints

  1. Use np.dot(v, w) for the numerator and squared sums for both norms.
  2. Pass np.clip(cosine, -1.0, 1.0) to np.arccos.

Requirements

Constraints

Starter Code

import numpy as np

def angle_between_3d(v: list, w: list) -> float:
    """
    Returns the angle as a float.
    """
    # Write code here
    pass

Test Cases

CaseMatches
Orthogonal vectors (pi/2)public
Parallel vectors (0)public