Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Prerequisites: ch103 (Unit Circle), ch108 (Transformations)

Outcomes: Derive the 2D rotation matrix from first principles; Implement rotation around arbitrary centers; Compose multiple rotations; Connect to eigenvectors (preview)

Derivation of the Rotation Matrix

Rotating point P = (x, y) by angle θ around the origin:

Using complex number multiplication (ch103): (x + iy) · (cos θ + i sin θ) = (x cos θ - y sin θ) + i(x sin θ + y cos θ)

Therefore the rotated point is: x’ = x cos θ - y sin θ y’ = x sin θ + y cos θ

In matrix form: [x’; y’] = R(θ) · [x; y]

where the rotation matrix is: R(θ) = [[cos θ, -sin θ], [sin θ, cos θ]]

Properties:

  • R(0) = I (identity)

  • R(θ)·R(φ) = R(θ+φ) — composition is angle addition

  • R(θ)⁻¹ = R(-θ) = R(θ)ᵀ — inverse is transpose (orthogonal matrix)

  • det(R) = cos²θ + sin²θ = 1 — rotation preserves area

(det and orthogonal matrices formalized in ch158–159; eigenvalues of R in ch170.)

# --- Rotation matrix implementation ---
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn-v0_8-whitegrid')

def rotation_matrix(theta):
    """2D rotation matrix for angle theta (radians)."""
    c, s = np.cos(theta), np.sin(theta)
    return np.array([[c, -s], [s, c]])

def rotate_points(points, theta, center=(0,0)):
    """Rotate an (N,2) array by theta around center."""
    pts = np.asarray(points, dtype=float) - np.array(center)
    R = rotation_matrix(theta)
    return (R @ pts.T).T + np.array(center)

# L-shaped polygon
L_shape = np.array([[0,0],[2,0],[2,1],[1,1],[1,3],[0,3],[0,0]], dtype=float)

fig, axes = plt.subplots(1, 3, figsize=(15, 5))

# Rotation around origin
ax = axes[0]
colors = ['gray','steelblue','crimson','darkgreen','darkorange']
for angle_deg, color in zip([0, 45, 90, 135, 180], colors):
    rotated = rotate_points(L_shape, np.radians(angle_deg))
    ax.plot(rotated[:,0], rotated[:,1], color=color, lw=2, alpha=0.8, label=f'{angle_deg}°')
ax.set_aspect('equal'); ax.set_title('Rotation Around Origin'); ax.legend(fontsize=8)
ax.axhline(0, color='k', linewidth=0.4); ax.axvline(0, color='k', linewidth=0.4)

# Rotation around arbitrary center
ax = axes[1]
center = np.array([1.0, 1.5])
ax.plot(L_shape[:,0], L_shape[:,1], 'steelblue', lw=2, label='Original')
for angle_deg, color in [(45,'crimson'),(90,'darkgreen'),(135,'darkorange')]:
    rotated = rotate_points(L_shape, np.radians(angle_deg), center=center)
    ax.plot(rotated[:,0], rotated[:,1], color=color, lw=2, alpha=0.8, label=f'{angle_deg}°')
ax.plot(*center, 'k*', markersize=15, label='Center')
ax.set_aspect('equal'); ax.set_title('Rotation Around Arbitrary Center'); ax.legend(fontsize=8)

# Verify R(-θ) = R(θ)^T = R(θ)^(-1)
ax = axes[2]
theta = np.radians(37)
R = rotation_matrix(theta)
R_inv = rotation_matrix(-theta)
print("R·R⁻¹ = I?", np.allclose(R @ R_inv, np.eye(2)))
print("R⁻¹ = Rᵀ?", np.allclose(R_inv, R.T))
print("det(R) =", np.linalg.det(R).round(10))

# Show sequential rotation = sum of angles
pts = np.array([[1,0],[0.5,0.5]])
for angles, color, lbl in [([30],[0.8,0.2,0.2],'30°'),
                             ([45],[0.2,0.5,0.8],'45°'),
                             ([30,45],[0.2,0.7,0.2],'30°+45°'),
                             ([75],[0.7,0.2,0.7],'75°')]:
    result = pts.copy()
    for a in angles:
        result = rotate_points(result, np.radians(a))
    ax.plot(result[:,0],result[:,1],'o',color=color,markersize=8,label=lbl)
ax.plot(pts[:,0],pts[:,1],'k^',markersize=10,label='Start')
ax.set_aspect('equal'); ax.set_title('R(30°)·R(45°) = R(75°)')
ax.legend(fontsize=8); ax.axhline(0, color='k', linewidth=0.4); ax.axvline(0, color='k', linewidth=0.4)

plt.suptitle('2D Rotation Matrix', fontsize=12, fontweight='bold')
plt.tight_layout(); plt.show()
R·R⁻¹ = I? True
R⁻¹ = Rᵀ? True
det(R) = 1.0
<Figure size 1500x500 with 3 Axes>

Summary

  • R(θ) = [[cos θ, -sin θ],[sin θ, cos θ]] — derived from complex multiplication

  • R(θ)·R(φ) = R(θ+φ); R(θ)⁻¹ = R(θ)ᵀ; det(R) = 1

  • Rotation around arbitrary center: translate to origin, rotate, translate back

  • R is an orthogonal matrix: its columns are orthonormal (Part VI, ch158)

Forward: ch113 shows how to compose rotation with other transforms in one matrix product; ch169 (Eigenvalues) reveals that R(θ) has complex eigenvalues e^(±iθ).