🔄 Transformation Foundations

🎯 What are Transformations?

Transformations are mathematical operations that map points from one coordinate system to another. They are fundamental to computer graphics, robotics, computer vision, and geometric modeling.

Core Concepts

Linear transformations: Preserve lines and origin. Affine transformations: Preserve lines but not necessarily origin. Projective: Preserve lines but not parallelism.

📌 Linear📌 Affine📌 Projective📌 Isometric
import numpy as np # Basic transformation operations def translate(point, tx, ty): return point + np.array([tx, ty]) def rotate_2d(point, angle): theta = np.radians(angle) rot = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) return rot @ point def scale(point, sx, sy): return point * np.array([sx, sy])

📐 Translation & Vectors

🎯 Vector Translation

Translation moves every point by a constant vector. It's the simplest transformation and fundamental to all graphics operations.

Translation Properties

Vector addition: p' = p + t. Commutative: Translations commute (t1 + t2 = t2 + t1). Inverse: Inverse translation is -t.

📐 Vector Addition🔄 Commutative🔁 Inverse
import numpy as np class Translation: def __init__(self, tx, ty, tz=0): self.t = np.array([tx, ty, tz]) def apply(self, points): return points + self.t def inverse(self): return Translation(-self.t[0], -self.t[1], -self.t[2]) def compose(self, other): return Translation(*(self.t + other.t)) # Example t1 = Translation(2, 3) point = np.array([1, 1, 0]) result = t1.apply(point) print(f"Translated: {result}")

🔄 Rotation Matrices

🎯 Rotations in 2D and 3D

Rotation matrices are orthogonal matrices with determinant 1. They represent rotations around axes in 2D and 3D space.

Rotation Properties

Orthogonal: R^T R = I. Determinant: det(R) = 1. Group: Rotations form SO(2) and SO(3) groups.

📐 2D Rotation🌐 3D Rotation🔄 Orthogonal
import numpy as np def rotation_2d(theta): """2D rotation matrix""" c, s = np.cos(theta), np.sin(theta) return np.array([[c, -s], [s, c]]) def rotation_3d_x(angle): """Rotation around X axis""" c, s = np.cos(angle), np.sin(angle) return np.array([ [1, 0, 0], [0, c, -s], [0, s, c] ]) def rotation_3d_y(angle): """Rotation around Y axis""" c, s = np.cos(angle), np.sin(angle) return np.array([ [c, 0, s], [0, 1, 0], [-s, 0, c] ]) def rotation_3d_z(angle): """Rotation around Z axis""" c, s = np.cos(angle), np.sin(angle) return np.array([ [c, -s, 0], [s, c, 0], [0, 0, 1] ])

📏 Scaling & Reflection

🎯 Scale and Mirror Operations

Scaling changes the size of objects. Reflection flips objects across axes. Both are linear transformations.

Scaling Properties

Uniform scaling: sx = sy (preserves aspect ratio). Non-uniform: sx ≠ sy (stretches). Reflection: Scaling with negative factors.

📐 Uniform📏 Non-Uniform🔄 Reflection
def scale_matrix(sx, sy, sz=1): return np.diag([sx, sy, sz]) def reflect_x(): return np.diag([-1, 1, 1]) def reflect_y(): return np.diag([1, -1, 1]) def reflect_z(): return np.diag([1, 1, -1]) # Example: Uniform scaling by 2 S = scale_matrix(2, 2, 2) point = np.array([1, 1, 1]) result = S @ point print(f"Scaled: {result}")

🧊 Homogeneous Coordinates

🎯 Unified Representation

Homogeneous coordinates add a fourth coordinate (w) to represent points. This allows translations and linear transformations in the same matrix.

Why Homogeneous?

Advantages: Unified transformation matrices, representation of points at infinity, easy composition of transformations.

🧊 4D Vector📐 w=1 for points∞ w=0 for vectors
def translation_matrix(tx, ty, tz=0): return np.array([ [1, 0, 0, tx], [0, 1, 0, ty], [0, 0, 1, tz], [0, 0, 0, 1] ]) def transform_point(mat, point): # Convert to homogeneous, transform, convert back h_point = np.append(point, 1) result = mat @ h_point return result[:3] / result[3] # Compose transformations T = translation_matrix(2, 3, 0) point = np.array([1, 1, 0]) result = transform_point(T, point) print(f"Transformed: {result}")

📊 Affine Transformations

🎯 Linear + Translation

Affine transformations combine linear transformations (rotation, scaling, shear) with translation. They preserve lines and ratios.

Affine Properties

Preserves: Lines, parallelism, collinearity, ratios of distances on lines. Does not preserve: Angles, lengths (unless isometric).

📊 Linear + Translation📐 6 DOF (2D)🌐 12 DOF (3D)
class AffineTransform: def __init__(self, matrix, translation): self.A = matrix # 2x2 for 2D, 3x3 for 3D self.t = translation def apply(self, points): return points @ self.A.T + self.t def compose(self, other): return AffineTransform( self.A @ other.A, self.A @ other.t + self.t ) def inverse(self): inv_A = np.linalg.inv(self.A) return AffineTransform(inv_A, -inv_A @ self.t) # 2D affine: rotation + translation theta = np.radians(30) R = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) t = np.array([2, 3]) affine = AffineTransform(R, t)

🎯 Projection Transforms

🎯 Orthographic & Perspective

Projection transforms map 3D points to 2D image planes. Orthographic preserves parallelism. Perspective mimics human vision.

Projection Types

Orthographic: No perspective distortion, used in engineering drawings. Perspective: Objects farther away appear smaller, realistic rendering.

📐 Orthographic👁️ Perspective🎯 Camera Model
def orthographic_projection(): return np.array([ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1] ]) def perspective_projection(fov, aspect, near, far): """Perspective projection matrix""" f = 1 / np.tan(np.radians(fov) / 2) return np.array([ [f/aspect, 0, 0, 0], [0, f, 0, 0], [0, 0, (far+near)/(near-far), 2*far*near/(near-far)], [0, 0, -1, 0] ])

🌀 Quaternions & Rotations

🎯 3D Rotation Representation

Quaternions provide a compact, singularity-free representation of 3D rotations. They are essential for smooth interpolation (SLERP).

Quaternion Properties

Unit quaternion: ||q|| = 1 represents rotation. SLERP: Spherical linear interpolation for smooth animation. No gimbal lock: Unlike Euler angles.

🌀 Unit Quaternion🔄 SLERP🚫 No Gimbal Lock
import numpy as np class Quaternion: def __init__(self, w, x, y, z): self.w = w self.v = np.array([x, y, z]) def normalize(self): norm = np.sqrt(self.w**2 + np.sum(self.v**2)) return Quaternion(self.w/norm, *(self.v/norm)) def to_rotation_matrix(self): w, x, y, z = self.w, *self.v return np.array([ [1-2*y*y-2*z*z, 2*x*y-2*z*w, 2*x*z+2*y*w], [2*x*y+2*z*w, 1-2*x*x-2*z*z, 2*y*z-2*x*w], [2*x*z-2*y*w, 2*y*z+2*x*w, 1-2*x*x-2*y*y] ]) def slerp(self, other, t): # Spherical linear interpolation dot = self.w*other.w + np.dot(self.v, other.v) if dot < 0: other = Quaternion(-other.w, *(-other.v)) dot = -dot if dot > 0.9995: # Linear interpolation for small angles result = Quaternion( self.w + t*(other.w - self.w), *(self.v + t*(other.v - self.v)) ) return result.normalize() theta = np.arccos(np.clip(dot, -1, 1)) return Quaternion( np.sin((1-t)*theta)/np.sin(theta) * self.w + np.sin(t*theta)/np.sin(theta) * other.w, *(np.sin((1-t)*theta)/np.sin(theta) * self.v + np.sin(t*theta)/np.sin(theta) * other.v) ).normalize()

📐 Euler Angles

🎯 Yaw, Pitch, Roll

Euler angles decompose 3D rotation into three sequential rotations around coordinate axes. Common in aerospace, robotics, and gaming.

Euler Angle Issues

Gimbal lock: Loss of one degree of freedom at certain angles. Ambiguity: Multiple Euler angle representations for same rotation.

📐 Yaw-Pitch-Roll🚫 Gimbal Lock🔄 Order Matters
def euler_to_rotation(yaw, pitch, roll): """Convert Euler angles to rotation matrix (XYZ order)""" Rz = rotation_3d_z(yaw) Ry = rotation_3d_y(pitch) Rx = rotation_3d_x(roll) return Rz @ Ry @ Rx def rotation_to_euler(R): """Extract Euler angles from rotation matrix""" # Simplified for ZYX order sy = np.sqrt(R[0,0]**2 + R[1,0]**2) singular = sy < 1e-6 if not singular: yaw = np.arctan2(R[1,0], R[0,0]) pitch = np.arctan2(-R[2,0], sy) roll = np.arctan2(R[2,1], R[2,2]) else: yaw = np.arctan2(-R[1,2], R[1,1]) pitch = np.arctan2(-R[2,0], sy) roll = 0 return yaw, pitch, roll

🔧 Rigid Body Transformations

🎯 Rotation + Translation Only

Rigid body transformations preserve distances and angles. They are the foundation of robotics, animation, and physics simulation.

Rigid Motion Properties

SE(3): Special Euclidean group — rigid transformations in 3D. DOF: 6 degrees of freedom (3 rotation + 3 translation).

🔧 SE(3)📐 6 DOF🔄 Isometric
class RigidTransform: def __init__(self, R, t): self.R = R # Rotation matrix (3x3) self.t = t # Translation vector (3) def apply(self, points): return points @ self.R.T + self.t def inverse(self): return RigidTransform(self.R.T, -self.R.T @ self.t) def compose(self, other): return RigidTransform(self.R @ other.R, self.R @ other.t + self.t) def to_homogeneous(self): H = np.eye(4) H[:3, :3] = self.R H[:3, 3] = self.t return H

📷 Camera Transformations

🎯 World → Camera → Image

Camera transformations convert 3D world coordinates to 2D image coordinates through extrinsic (pose) and intrinsic (camera) parameters.

Camera Model

Extrinsic: World-to-camera transformation (R, t). Intrinsic: Focal length, principal point, distortion. Projection: 3D → 2D using perspective projection.

📷 Extrinsic🎯 Intrinsic📐 Projection
class Camera: def __init__(self, fx, fy, cx, cy): self.K = np.array([ [fx, 0, cx], [0, fy, cy], [0, 0, 1] ]) def project(self, points_3d, R, t): # Transform to camera coordinates points_cam = points_3d @ R.T + t # Project to image plane points_img = points_cam[:, :2] / points_cam[:, 2:] # Apply intrinsic matrix points_img = points_img @ self.K[:2, :2].T + self.K[:2, 2] return points_img def unproject(self, points_2d, depth, R, t): # Inverse projection points_hom = np.column_stack([points_2d, np.ones(len(points_2d))]) points_cam = points_hom @ np.linalg.inv(self.K).T points_cam *= depth[:, np.newaxis] points_3d = points_cam @ R - t return points_3d

🌍 Coordinate Systems

🎯 World, Local, Object, View

Different coordinate systems in graphics and robotics: world space, object space, camera space, screen space, and model space.

Coordinate Spaces

Object space: Local to object. World space: Global reference frame. View space: Camera-centric. Screen space: Final 2D pixel coordinates.

🌐 World📦 Object📷 View🖥️ Screen
class CoordinateSystem: def __init__(self, origin, axes): self.origin = origin self.axes = axes # Matrix of basis vectors def world_to_local(self, point): return self.axes.T @ (point - self.origin) def local_to_world(self, point): return self.origin + self.axes @ point # Example: Create a local coordinate system origin = np.array([1, 2, 3]) axes = np.eye(3) # Aligned with world axes local = CoordinateSystem(origin, axes) world_point = np.array([2, 3, 4]) local_point = local.world_to_local(world_point) print(f"Local point: {local_point}")

🎨 Graphics Pipeline

🎯 Transformation Pipeline

Modern graphics pipelines use a series of transformations: model → world → view → projection → viewport transformations.

Pipeline Stages

Model-View-Projection (MVP): Combines model, view, and projection matrices. Viewport: Maps NDC to screen coordinates. Vertex shader: Executes per-vertex transformations.

🎨 MVP Matrix📐 Viewport🖥️ Rasterization
def create_mvp(model, view, projection): """Create Model-View-Projection matrix""" return projection @ view @ model class GraphicsPipeline: def __init__(self, viewport_width, viewport_height): self.viewport_width = viewport_width self.viewport_height = viewport_height def transform_vertex(self, vertex, model, view, projection): """Apply full transformation pipeline to a vertex""" # Model -> World world_pos = model @ vertex # World -> View view_pos = view @ world_pos # View -> Clip clip_pos = projection @ view_pos # Perspective divide ndc_pos = clip_pos[:3] / clip_pos[3] # Viewport mapping screen_x = (ndc_pos[0] + 1) * self.viewport_width / 2 screen_y = (1 - ndc_pos[1]) * self.viewport_height / 2 return np.array([screen_x, screen_y, ndc_pos[2]])

🚀 Applications & Tools

🎯 Real-World Applications

Transformations are everywhere: 3D gaming, robotics, computer vision, CAD, animation, AR/VR, and more.

Application Domains

Robotics: Forward/inverse kinematics, motion planning. AR/VR: Head tracking, object placement. Animation: Character rigging, skinning. Computer Vision: Image registration, structure from motion.

🤖 Robotics🎮 Gaming🥽 AR/VR📐 CAD
# Example: Robot manipulator kinematics class RobotArm: def __init__(self, lengths): self.lengths = lengths self.joint_angles = np.zeros(len(lengths)) def forward_kinematics(self): """Compute end-effector position""" position = np.zeros(3) current = np.eye(4) for i, (length, angle) in enumerate(zip(self.lengths, self.joint_angles)): # Build transformation matrix for joint i T = np.eye(4) T[:3, 3] = [length, 0, 0] R = rotation_3d_z(angle) T[:3, :3] = R current = current @ T return current[:3, 3] def set_joints(self, angles): self.joint_angles = angles def inverse_kinematics(self, target): # Simplified inverse kinematics (2D case) import numpy as np x, y = target[:2] L1, L2 = self.lengths[:2] cos_theta2 = (x**2 + y**2 - L1**2 - L2**2) / (2 * L1 * L2) theta2 = np.arccos(np.clip(cos_theta2, -1, 1)) theta1 = np.arctan2(y, x) - np.arctan2(L2 * np.sin(theta2), L1 + L2 * np.cos(theta2)) return np.array([theta1, theta2])