Linear transformation mempertahankan addition & scaling — representasi sebagai matriks memungkinkan rotasi sprite, scaling, translation dengan homogeneous coordinates, dan camera transform dalam game graphics serta image preprocessing.

Setelah di episode 18 kita mempelajari determinant, inverse, dan Gaussian elimination — alat untuk menyelesaikan sistem linear — pada episode ini kita mempelajari linear transformations: fungsi yang mentransformasi vektor sambil mempertahankan struktur linear. Dalam 2D/3D, linear transformation selalu bisa direpresentasikan sebagai perkalian matriks.
Mengapa linear transformations penting? Karena dalam game graphics, setiap rotasi sprite, scaling ukuran, dan pergeseran posisi menggunakan matriks transformasi. Dalam image processing, rotasi dan flip gambar menggunakan matriks yang sama. Memahami matriks transformasi memberikan kemampuan untuk memanipulasi ruang secara presisi dan efisien.
Fungsi T adalah linear transformation jika:
T(u + v) = T(u) + T(v)T(c × v) = c × T(v)import numpy as np
def T(v):
"""Transformasi: T(x,y) = (2x + y, x - y)."""
return np.array([2*v[0] + v[1], v[0] - v[1]])
u = np.array([1, 2])
v = np.array([3, 1])
c = 5
# Additivity
lhs = T(u + v)
rhs = T(u) + T(v)
print(f"T(u+v) = T({u+v}) = {lhs}")
print(f"T(u)+T(v) = {T(u)}+{T(v)} = {rhs}")
print(f"Additivity: {np.allclose(lhs, rhs)}")
# Homogeneity
lhs = T(c * u)
rhs = c * T(u)
print(f"\nT(c×u) = T({c*u}) = {lhs}")
print(f"c×T(u) = {c}×{T(u)} = {rhs}")
print(f"Homogeneity: {np.allclose(lhs, rhs)}")Matriks rotasi sudut θ (radian):
import numpy as np
import math
def rotation_matrix(theta):
"""Matriks rotasi 2D."""
c, s = math.cos(theta), math.sin(theta)
return np.array([[c, -s], [s, c]])
# Rotasi titik (1, 0) sebesar 30°
theta = math.radians(30)
R = rotation_matrix(theta)
point = np.array([1.0, 0.0])
rotated = R @ point
print(f"Matriks rotasi 30°:")
print(f" [[{R[0][0]:.4f}, {R[0][1]:.4f}],")
print(f" [{R[1][0]:.4f}, {R[1][1]:.4f}]]")
print(f"\nTitik (1,0) → ({rotated[0]:.4f}, {rotated[1]:.4f})")
print(f"Expected: ({math.cos(theta):.4f}, {math.sin(theta):.4f})")
# Rotasi 90°
R90 = rotation_matrix(math.radians(90))
p = np.array([1.0, 0.0])
print(f"\n(1,0) rotated 90° = {R90 @ p}") # (0, 1)def scale_matrix(sx, sy):
"""Matriks scaling 2D."""
return np.array([[sx, 0], [0, sy]])
S = scale_matrix(2, 3) # x×2, y×3
point = np.array([1.0, 1.0])
scaled = S @ point
print(f"Scaling (2,3): {point} → {scaled}")
# Determinant = area scaling factor
print(f"Area scaling: {abs(np.linalg.det(S))}x")def shear_matrix(shx, shy=0):
"""Matriks shear 2D."""
return np.array([[1, shx], [shy, 1]])
# Horizontal shear
H = shear_matrix(1) # shx = 1
point = np.array([1.0, 1.0])
sheared = H @ point
print(f"Shear horizontal (1): {point} → {sheared}")
print(f"Area unchanged: det = {np.linalg.det(H)}")Beberapa transformasi bisa digabungkan dengan perkalian matriks:
import math
theta = math.radians(45)
R = rotation_matrix(theta)
S = scale_matrix(2, 1)
# Komposisi: M = S × R (rotasi dulu, lalu scaling)
M = S @ R
point = np.array([1.0, 0.0])
result_separate = S @ (R @ point)
result_composed = M @ point
print(f"Rotasi 45° lalu scaling (2,1):")
print(f" Separate: {result_separate}")
print(f" Composed: {result_composed}")
print(f" Same? {np.allclose(result_separate, result_composed)}")
# Urutan penting!
M_reversed = R @ S
result_reversed = M_reversed @ point
print(f"\nScaling (2,1) lalu rotasi 45°:")
print(f" Result: {result_reversed}")
print(f" Different? {not np.allclose(result_separate, result_reversed)}")Matriks linear 2×2 tidak bisa merepresentasikan translation. Solusi: gunakan homogeneous coordinates (3×3):
import numpy as np
def translation_matrix(tx, ty):
"""Matriks translation menggunakan homogeneous coordinates."""
return np.array([
[1, 0, tx],
[0, 1, ty],
[0, 0, 1],
])
def rotation_matrix_3x3(theta):
"""Rotasi 3×3 dengan homogeneous coordinates."""
c, s = np.cos(theta), np.sin(theta)
return np.array([
[c, -s, 0],
[s, c, 0],
[0, 0, 1],
])
# Titik (1, 0) dalam homogeneous coordinates
point = np.array([1.0, 0.0, 1.0]) # [x, y, 1]
# Translate (3, 2)
T = translation_matrix(3, 2)
translated = T @ point
print(f"Translate (3,2): {point[:2]} → {translated[:2]}")
# Rotate 45° lalu translate
theta = np.pi / 4
R = rotation_matrix_3x3(theta)
T = translation_matrix(3, 2)
M = T @ R # rotate first, then translate
result = M @ point
print(f"Rotate 45° + Translate (3,2): {result[:2]}")import numpy as np
import math
class Transform2D:
def __init__(self):
self.matrix = np.eye(3)
def translate(self, tx, ty):
T = np.array([[1, 0, tx], [0, 1, ty], [0, 0, 1]])
self.matrix = T @ self.matrix
return self
def rotate(self, degrees):
theta = math.radians(degrees)
c, s = math.cos(theta), math.sin(theta)
R = np.array([[c, -s, 0], [s, c, 0], [0, 0, 1]])
self.matrix = R @ self.matrix
return self
def scale(self, sx, sy):
S = np.array([[sx, 0, 0], [0, sy, 0], [0, 0, 1]])
self.matrix = S @ self.matrix
return self
def apply(self, point):
p = np.array([point[0], point[1], 1.0])
result = self.matrix @ p
return result[:2]
# Pipeline: scale 2x → rotate 45° → translate (10, 5)
sprite = Transform2D()
sprite.scale(2, 2).rotate(45).translate(10, 5)
corners = [(0, 0), (1, 0), (1, 1), (0, 1)]
print("Sprite corners after transform:")
for corner in corners:
transformed = sprite.apply(corner)
print(f" {corner} → ({transformed[0]:.2f}, {transformed[1]:.2f})")Tip
Urutan transformasi penting: T × R × S berarti "scale dulu, rotasi, lalu translate" (dibaca kanan ke kiri). Kesalahan urutan adalah bug umum dalam graphics programming. Selalu verifikasi dengan kasus sederhana (misal rotasi 90° terhadap titik (1,0)).
Inti yang harus dibawa pulang:
cos dan sin; scaling: matriks diagonal; shear: matriks off-diagonal.M = T₂ × T₁ — transformasi diterapkan dari kanan ke kiri.Di episode 20 selanjutnya kita akan mempelajari eigenvalues dan eigenvectors — vektor yang hanya diskalakan (bukan diputar) oleh transformasi, fundamental untuk PCA, PageRank, dan matrix stability analysis!