Chapter 10: Three-Dimensional Linear Transformationsยถ
Introductionยถ
Everything weโve learned about 2D linear transformations extends beautifully to 3D!
Key Differences in 3Dยถ
Basis vectors: Three instead of two (รฎ, ฤต, kฬ)
Matrices: 3ร3 instead of 2ร2
Determinant: Measures volume scaling (not area!)
Rotations: Need to specify an axis to rotate around
The Big Pictureยถ
Just like 2D:
Columns of matrix = where basis vectors land
Transformations morph 3D space
Grid lines stay parallel and evenly spaced
But in 3D, weโre morphing all of space, not just a plane!
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import seaborn as sns
sns.set_style('whitegrid')
plt.rcParams['figure.figsize'] = (12, 10)
np.set_printoptions(precision=3, suppress=True)
1. 3D Basis Vectorsยถ
In 3D, we have three standard basis vectors:
Geometric picture:
รฎ points along the x-axis
ฤต points along the y-axis
kฬ points along the z-axis
Any 3D vector can be expressed as:
def draw_3d_vector(ax, origin, vector, color='blue', label=None):
"""Draw a 3D vector as an arrow."""
ax.quiver(origin[0], origin[1], origin[2],
vector[0], vector[1], vector[2],
color=color, arrow_length_ratio=0.15,
linewidth=2.5, label=label)
def setup_3d_ax(ax, lim=(-2, 2)):
"""Setup 3D axis."""
ax.set_xlim(lim)
ax.set_ylim(lim)
ax.set_zlim(lim)
ax.set_xlabel('X', fontsize=12)
ax.set_ylabel('Y', fontsize=12)
ax.set_zlabel('Z', fontsize=12)
ax.grid(True, alpha=0.3)
# Visualize 3D basis
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
i_hat = np.array([1, 0, 0])
j_hat = np.array([0, 1, 0])
k_hat = np.array([0, 0, 1])
draw_3d_vector(ax, np.zeros(3), i_hat, 'red', 'รฎ')
draw_3d_vector(ax, np.zeros(3), j_hat, 'green', 'ฤต')
draw_3d_vector(ax, np.zeros(3), k_hat, 'blue', 'kฬ')
setup_3d_ax(ax)
ax.legend(fontsize=11)
ax.set_title('3D Standard Basis Vectors', fontsize=14, fontweight='bold')
plt.show()
print('Standard 3D basis vectors:')
print(f'รฎ = {i_hat}')
print(f'ฤต = {j_hat}')
print(f'kฬ = {k_hat}')
2. Reading 3ร3 Matricesยถ
A 3ร3 matrix represents a linear transformation in 3D:
How to read it:
First column = where รฎ lands
Second column = where ฤต lands
Third column = where kฬ lands
Example:
รฎ โ [2, 0, 0] (stretched by 2 in x)
ฤต โ [0, 1, 0] (unchanged)
kฬ โ [1, 0, 2] (moved and stretched)
def draw_unit_cube(ax, color='cyan', alpha=0.1):
"""Draw a unit cube."""
# Define vertices
vertices = np.array([
[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0], # bottom
[0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1] # top
])
# Define faces
faces = [
[vertices[0], vertices[1], vertices[5], vertices[4]], # front
[vertices[2], vertices[3], vertices[7], vertices[6]], # back
[vertices[0], vertices[3], vertices[7], vertices[4]], # left
[vertices[1], vertices[2], vertices[6], vertices[5]], # right
[vertices[0], vertices[1], vertices[2], vertices[3]], # bottom
[vertices[4], vertices[5], vertices[6], vertices[7]] # top
]
cube = Poly3DCollection(faces, alpha=alpha, facecolor=color, edgecolor='black', linewidths=1)
ax.add_collection3d(cube)
def visualize_3d_transformation(A, title):
"""Visualize how a 3x3 matrix transforms the unit cube."""
fig = plt.figure(figsize=(14, 6))
# Before transformation
ax1 = fig.add_subplot(121, projection='3d')
draw_unit_cube(ax1)
draw_3d_vector(ax1, np.zeros(3), np.array([1, 0, 0]), 'red', 'รฎ')
draw_3d_vector(ax1, np.zeros(3), np.array([0, 1, 0]), 'green', 'ฤต')
draw_3d_vector(ax1, np.zeros(3), np.array([0, 0, 1]), 'blue', 'kฬ')
setup_3d_ax(ax1, lim=(-1, 3))
ax1.set_title('Before Transformation', fontsize=13, fontweight='bold')
ax1.legend()
# After transformation
ax2 = fig.add_subplot(122, projection='3d')
# Transform cube vertices
vertices = np.array([
[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0],
[0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1]
]).T
transformed = A @ vertices
# Draw transformed cube
t_verts = transformed.T
faces = [
[t_verts[0], t_verts[1], t_verts[5], t_verts[4]],
[t_verts[2], t_verts[3], t_verts[7], t_verts[6]],
[t_verts[0], t_verts[3], t_verts[7], t_verts[4]],
[t_verts[1], t_verts[2], t_verts[6], t_verts[5]],
[t_verts[0], t_verts[1], t_verts[2], t_verts[3]],
[t_verts[4], t_verts[5], t_verts[6], t_verts[7]]
]
cube = Poly3DCollection(faces, alpha=0.1, facecolor='orange', edgecolor='black', linewidths=1)
ax2.add_collection3d(cube)
# Draw transformed basis
i_new = A @ np.array([1, 0, 0])
j_new = A @ np.array([0, 1, 0])
k_new = A @ np.array([0, 0, 1])
draw_3d_vector(ax2, np.zeros(3), i_new, 'red', 'รฎ transformed')
draw_3d_vector(ax2, np.zeros(3), j_new, 'green', 'ฤต transformed')
draw_3d_vector(ax2, np.zeros(3), k_new, 'blue', 'kฬ transformed')
setup_3d_ax(ax2, lim=(-1, 3))
ax2.set_title(f'After: {title}', fontsize=13, fontweight='bold')
ax2.legend()
plt.tight_layout()
plt.show()
# Example transformation
A = np.array([[2, 0, 1],
[0, 1, 0],
[0, 0, 2]])
print(f"Matrix A:\n{A}\n")
print(f"รฎ โ {A[:, 0]}")
print(f"ฤต โ {A[:, 1]}")
print(f"kฬ โ {A[:, 2]}")
visualize_3d_transformation(A, "Scaling and Shearing")
3. Determinants in 3D: Volume Scalingยถ
In 2D, the determinant measured area scaling.
In 3D, the determinant measures volume scaling!
Key concept:
The unit cube has volume = 1
After transformation, it becomes a parallelepiped
det(A) = volume of that parallelepiped
Sign of determinant:
Positive: Orientation preserved (right-hand rule maintained)
Negative: Orientation flipped (like turning space inside-out)
Zero: Space squished to lower dimension (3D โ plane or line)
def visualize_3d_determinant():
"""Show how determinant relates to volume scaling."""
matrices = [
("Identity (det=1)", np.eye(3)),
("Scaling by 2 (det=8)", 2 * np.eye(3)),
("Rotation (det=1)", np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]])),
("Squished (detโ0)", np.array([[1, 0, 0.5], [0, 1, 0.5], [0, 0, 0.1]]))
]
for name, A in matrices:
det = np.linalg.det(A)
print(f"\n{'='*60}")
print(f"{name}")
print(f"{'='*60}")
print(f"Matrix:\n{A}")
print(f"\nDeterminant = {det:.6f}")
print(f"Volume scaling = {abs(det):.2f}x")
if det > 0:
print("Orientation: Preserved โ")
elif det < 0:
print("Orientation: Flipped โป")
else:
print("Dimension: Squished to lower!")
visualize_3d_determinant()
4. 3D Rotationsยถ
In 2D, rotation is simple: one angle around the origin.
In 3D, we need to specify which axis to rotate around!
Rotation around the z-axisยถ
Notice: This looks like 2D rotation with an extra โ1โ at the bottom-right!
Rotation around the x-axisยถ
Rotation around the y-axisยถ
Right-hand rule: Curl fingers in rotation direction, thumb points along axis!
def rotation_matrix_x(theta):
"""Rotation around x-axis."""
c, s = np.cos(theta), np.sin(theta)
return np.array([[1, 0, 0],
[0, c, -s],
[0, s, c]])
def rotation_matrix_y(theta):
"""Rotation around y-axis."""
c, s = np.cos(theta), np.sin(theta)
return np.array([[c, 0, s],
[0, 1, 0],
[-s, 0, c]])
def rotation_matrix_z(theta):
"""Rotation around z-axis."""
c, s = np.cos(theta), np.sin(theta)
return np.array([[c, -s, 0],
[s, c, 0],
[0, 0, 1]])
# Demonstrate rotations
angle = np.pi / 4 # 45 degrees
print("Rotation Matrices (45ยฐ rotations):\n")
print("Around x-axis:")
print(rotation_matrix_x(angle))
print(f"\nDeterminant = {np.linalg.det(rotation_matrix_x(angle)):.6f} (preserves volume!)\n")
print("Around y-axis:")
print(rotation_matrix_y(angle))
print(f"\nDeterminant = {np.linalg.det(rotation_matrix_y(angle)):.6f}\n")
print("Around z-axis:")
print(rotation_matrix_z(angle))
print(f"\nDeterminant = {np.linalg.det(rotation_matrix_z(angle)):.6f}")
# Visualize rotation around z-axis
visualize_3d_transformation(rotation_matrix_z(np.pi/3), "60ยฐ Rotation around Z-axis")
5. Composite Transformations in 3Dยถ
Just like in 2D, we can combine transformations by multiplying matrices!
Example: Rotate around z-axis, then scale
Remember: Read right to left! (First \(R_z\), then \(S\))
Cool application: Any 3D rotation can be decomposed into rotations around x, y, and z axes (Euler angles)!
# Composite transformation: rotate then scale
theta = np.pi / 4
R = rotation_matrix_z(theta)
S = np.diag([2, 1, 1.5]) # Scale x by 2, z by 1.5
# Composite
M = S @ R
print("Rotation matrix:")
print(R)
print("\nScaling matrix:")
print(S)
print("\nComposite M = S ร R:")
print(M)
print(f"\nDeterminant of composite: {np.linalg.det(M):.6f}")
visualize_3d_transformation(M, "Rotate 45ยฐ then Scale")
# Test on a vector
v = np.array([1, 0, 0])
print(f"\nOriginal vector: {v}")
print(f"After rotation: {R @ v}")
print(f"After composite: {M @ v}")
6. Column Space and Rank in 3Dยถ
In 3D, the column space can have different dimensions:
Rank 3 (full rank):
Column space = all of 3D space
Transformation is reversible (invertible)
det(A) โ 0
Rank 2:
Column space = a 2D plane through origin
3D space gets squished to a plane
det(A) = 0
Rank 1:
Column space = a line through origin
Everything squishes to a line
det(A) = 0
Rank 0:
Everything maps to origin
det(A) = 0
Key insight: rank = number of dimensions that โsurviveโ the transformation!
def analyze_3d_rank():
"""Analyze rank for different 3D matrices."""
matrices = [
("Full Rank (3)", np.array([[1, 0, 1], [0, 1, 1], [0, 0, 1]])),
("Rank 2 (Plane)", np.array([[1, 0, 2], [0, 1, 3], [0, 0, 0]])),
("Rank 1 (Line)", np.array([[1, 2, 3], [2, 4, 6], [3, 6, 9]])),
]
for name, A in matrices:
rank = np.linalg.matrix_rank(A)
det = np.linalg.det(A)
print(f"\n{'='*60}")
print(f"{name}")
print(f"{'='*60}")
print(f"Matrix:\n{A}")
print(f"\nRank: {rank}")
print(f"Determinant: {det:.6f}")
print(f"Column space dimension: {rank}D")
if rank == 3:
print("โ Full rank - transformation is invertible!")
elif rank == 2:
print("โ Squished to a plane")
elif rank == 1:
print("โ Squished to a line")
else:
print("โ Everything maps to origin")
analyze_3d_rank()
7. Applications: Computer Graphicsยถ
3D transformations are the foundation of computer graphics!
Graphics Pipeline:
Model transformation: Position objects in the world
View transformation: Position camera
Projection transformation: 3D โ 2D screen
Each step uses matrix multiplication!
Common operations:
Rotating objects (Rx, Ry, Rz)
Scaling objects (S)
Translating objects (using homogeneous coordinates)
Camera positioning
Perspective projection
Modern GPUs are optimized for matrix multiplication!
def demonstrate_3d_graphics():
"""Show how 3D graphics uses transformations."""
print("3D Graphics Transformation Pipeline\n")
# Define a simple 3D object (cube vertices)
cube = np.array([
[0, 1, 1, 0, 0, 1, 1, 0],
[0, 0, 1, 1, 0, 0, 1, 1],
[0, 0, 0, 0, 1, 1, 1, 1]
])
print("Step 1: Model Transformation (scale and rotate)")
scale = np.diag([1.5, 1, 1])
rotate = rotation_matrix_y(np.pi / 6)
model = rotate @ scale
cube_model = model @ cube
print(f"Model matrix:\n{model}\n")
print("Step 2: View Transformation (position camera)")
# Simple view: translate away from camera
view = np.eye(3) # Simplified (normally 4x4 with translation)
cube_view = view @ cube_model
print(f"View matrix:\n{view}\n")
print("Step 3: Projection (3D โ 2D)")
# Orthographic projection (drop z-coordinate)
projection = np.array([[1, 0, 0],
[0, 1, 0]])
cube_screen = projection @ cube_view
print(f"Projection matrix:\n{projection}\n")
print(f"Final 2D screen coordinates:\n{cube_screen}\n")
# Visualize the result
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
# 3D view
ax1 = plt.subplot(121, projection='3d')
ax1.scatter(cube_model[0], cube_model[1], cube_model[2], c='blue', s=100)
ax1.set_title('3D Object (after model transform)', fontweight='bold')
ax1.set_xlabel('X')
ax1.set_ylabel('Y')
ax1.set_zlabel('Z')
# 2D projection
ax2.scatter(cube_screen[0], cube_screen[1], c='red', s=100)
ax2.set_xlim(-2, 2)
ax2.set_ylim(-2, 2)
ax2.set_aspect('equal')
ax2.set_title('2D Screen (after projection)', fontweight='bold')
ax2.set_xlabel('Screen X')
ax2.set_ylabel('Screen Y')
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
demonstrate_3d_graphics()
8. Cross Product Connectionยถ
The cross product in 3D is intimately connected to determinants!
Geometric meaning:
Result is perpendicular to both v and w
Magnitude = area of parallelogram spanned by v and w
Direction follows right-hand rule
Volume interpretation: The scalar triple product gives the volume of a parallelepiped:
def visualize_cross_product_3d():
"""Demonstrate cross product as perpendicular vector."""
v = np.array([1, 0, 0.5])
w = np.array([0, 1, 0.3])
# Compute cross product
cross = np.cross(v, w)
# Verify perpendicularity
dot_v = np.dot(cross, v)
dot_w = np.dot(cross, w)
print("Cross Product in 3D\n")
print(f"v = {v}")
print(f"w = {w}")
print(f"\nv ร w = {cross}")
print(f"\nMagnitude |v ร w| = {np.linalg.norm(cross):.4f}")
print(f"\nVerifying perpendicularity:")
print(f"(v ร w) ยท v = {dot_v:.10f} โ 0 โ")
print(f"(v ร w) ยท w = {dot_w:.10f} โ 0 โ")
# Visualize
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
draw_3d_vector(ax, np.zeros(3), v, 'blue', 'v')
draw_3d_vector(ax, np.zeros(3), w, 'green', 'w')
draw_3d_vector(ax, np.zeros(3), cross, 'red', 'v ร w (perpendicular!)')
setup_3d_ax(ax, lim=(-1, 2))
ax.legend(fontsize=11)
ax.set_title('Cross Product: Perpendicular to Both Vectors',
fontsize=14, fontweight='bold')
plt.show()
visualize_cross_product_3d()
9. Summary and Key Takeawaysยถ
3D vs 2Dยถ
Concept |
2D |
3D |
|---|---|---|
Basis vectors |
รฎ, ฤต |
รฎ, ฤต, kฬ |
Matrix size |
2ร2 |
3ร3 |
Determinant |
Area scaling |
Volume scaling |
Rotation |
One angle |
Axis + angle |
Full rank |
2 |
3 |
Key Insightsยถ
Reading matrices: Columns = where basis vectors land
Determinant: Volume scaling factor (can be negative!)
Rotations: Need to specify an axis (Rx, Ry, Rz)
Rank: Dimensions that survive transformation
Applications: Computer graphics, robotics, physics simulations
Mental Modelยถ
Think of 3D transformations as morphing all of 3D space:
Grid lines stay parallel and evenly spaced
Origin stays fixed
Everything is determined by where รฎ, ฤต, kฬ land
Next Stepsยถ
Practice with rotation matrices
Explore quaternions (better for animations!)
Study homogeneous coordinates (4ร4 matrices for translation)
Learn about perspective projection
Remember: The geometric intuition from 2D extends beautifully to 3D and beyond!
10. Practice Exercisesยถ
Exercise 1: 3D Transformationsยถ
For the matrix:
a) Where do รฎ, ฤต, and kฬ land?
b) What is the determinant?
c) Is this transformation invertible?
d) Apply it to the vector [1, 0, 1]
# Your code here
A = np.array([[2, 0, 1],
[1, 3, 0],
[0, 1, 2]])
print("a) Basis vectors after transformation:")
print(f" รฎ โ {A[:, 0]}")
print(f" ฤต โ {A[:, 1]}")
print(f" kฬ โ {A[:, 2]}")
det_A = np.linalg.det(A)
print(f"\nb) Determinant = {det_A:.6f}")
print(f" Volume scaling factor: {det_A:.2f}x")
print(f"\nc) Invertible? {det_A != 0}")
if det_A != 0:
print(f" Inverse exists! โ")
v = np.array([1, 0, 1])
Av = A @ v
print(f"\nd) A ร [1, 0, 1] = {Av}")
Exercise 2: Rotationsยถ
Create a transformation that:
Rotates 90ยฐ around the z-axis
Then rotates 45ยฐ around the x-axis
Apply it to the vector [1, 1, 0]
# Your code here
# Step 1: Rotate 90ยฐ around z-axis
R_z = rotation_matrix_z(np.pi / 2)
# Step 2: Rotate 45ยฐ around x-axis
R_x = rotation_matrix_x(np.pi / 4)
# Composite (remember: right to left!)
M = R_x @ R_z
v = np.array([1, 1, 0])
result = M @ v
print("Rotation around z (90ยฐ):")
print(R_z)
print("\nRotation around x (45ยฐ):")
print(R_x)
print("\nComposite M = Rx ร Rz:")
print(M)
print(f"\nOriginal vector: {v}")
print(f"After transformations: {result}")
print(f"\nMagnitude preserved? {np.isclose(np.linalg.norm(v), np.linalg.norm(result))}")
Exercise 3: Rank and Column Spaceยถ
Determine the rank and describe the column space for:
# Your code here
B = np.array([[1, 2, 3],
[0, 0, 0],
[2, 4, 6]])
rank = np.linalg.matrix_rank(B)
det_B = np.linalg.det(B)
print(f"Matrix B:\n{B}")
print(f"\nRank: {rank}")
print(f"Determinant: {det_B:.10f}")
if rank == 1:
print("\nColumn space: A line through the origin")
print("All three columns are parallel!")
print(f"Column 1: {B[:, 0]}")
print(f"Column 2: {B[:, 1]} = 2 ร column 1")
print(f"Column 3: {B[:, 2]} = 3 ร column 1")
elif rank == 2:
print("\nColumn space: A plane through the origin")
else:
print("\nColumn space: All of 3D space")
Further Readingยถ
Quaternions: Better than Euler angles for smooth rotations
Homogeneous coordinates: 4ร4 matrices for translation
Gimbal lock: Problem with Euler angles
Perspective projection: How 3D becomes 2D on screen
Robotics: Forward and inverse kinematics using 3D transforms
Next: Eigenvalues and eigenvectors - the natural axes of transformations!