Matriks memiliki peran kedua yang menjelaskan pentingnya dalam grafika, yaitu memetakan setiap titik ke posisi baru melalui perkalian matriks dan vektor. Topik ini membahas tiga transformasi dasar, yaitu rotasi, penskalaan, dan geseran (shear), beserta visualisasi sebelum dan sesudahnya pada bidang dua dimensi. Setiap rumus dihitung ulang menggunakan NumPy dan divisualisasikan menggunakan matplotlib. Setelah topik ini selesai, efek matriks transformasi dapat diperkirakan dari isinya sebelum kode dijalankan.
Titik berpindah karena perkalian matriks#
Perkalian sebuah matriks dengan vektor kolom menghasilkan vektor kolom baru , dan perpindahan inilah yang disebut transformasi. Satu matriks yang sama diterapkan pada semua titik sebuah bentuk, sehingga seluruh bentuk berubah serempak sesuai aturan yang sama. Bentuk umumnya ditulis sebagai berikut.
Setiap kolom matriks menyatakan posisi baru salah satu vektor dasar, yaitu dan . Vektor dasar berpindah ke , sedangkan berpindah ke , dan seluruh titik lain mengikuti keduanya secara proporsional. Pandangan ini mempermudah pembacaan matriks transformasi secara langsung tanpa menghitung setiap titik satu per satu. Matriks identitas membiarkan semua titik di tempatnya, sedangkan matriks diagonal merentangkan bidang sepanjang setiap sumbunya.
Rotasi#
Rotasi memutar setiap titik mengelilingi titik asal sebesar sudut berlawanan arah jarum jam. Matriksnya tersusun dari nilai kosinus dan sinus sudut yang sama.
Sudut memindahkan titik ke karena dan . Untuk memperlihatkan efeknya pada sebuah bentuk, skrip berikut memutar huruf F sejauh 90 derajat dengan menerapkan satu matriks yang sama pada seluruh titiknya.
import numpy as np
import matplotlib
matplotlib.use("Agg") # backend tanpa jendela, cocok untuk menyimpan berkas
import matplotlib.pyplot as plt
# titik-titik pada tepi huruf F, disusun sebagai baris-baris matriks
F = np.array([
[0, 0], [0, 4], [2, 4], [2, 3], [1, 3],
[1, 2], [2, 2], [2, 1], [0, 1], [0, 0],
])
sudut = np.deg2rad(90)
R = np.array([
[np.cos(sudut), -np.sin(sudut)],
[np.sin(sudut), np.cos(sudut)],
]) # matriks rotasi 90 derajat berlawanan arah jarum jam
F_putar = F @ R.T # setiap baris titik diputar oleh matriks yang sama
fig, ax = plt.subplots(figsize=(6, 3))
ax.plot(F[:, 0], F[:, 1], "o-", color="#2c5f8a", label="Sebelum")
ax.plot(F_putar[:, 0], F_putar[:, 1], "s--", color="#c2571a",
label="Sesudah rotasi 90 derajat")
ax.set_aspect("equal")
ax.grid(True, linestyle=":")
ax.axhline(0, color="gray", linewidth=0.8)
ax.axvline(0, color="gray", linewidth=0.8)
ax.legend(loc="lower left", fontsize=8)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_title("Huruf F sebelum dan sesudah rotasi")
fig.tight_layout()
fig.savefig("rotasi-titik.svg")Skrip di atas menyimpan gambar ke berkas rotasi-titik.svg tanpa mencetak apa pun, dan hasilnya ditampilkan sebagai berikut.
Bentuk huruf F tidak berubah, tidak melar dan tidak mengecil karena rotasi hanya memutar setiap titik sebesar sudut yang sama mengelilingi titik asal. Jarak antartitik tetap terjaga, sehingga rotasi tergolong transformasi kaku (rigid transformation). Gambar disusun menggunakan set_aspect("equal") agar satuan pada kedua sumbu sama panjang, kebiasaan penting saat memvisualisasikan geometri. Tanpa pengaturan itu, sudut putar dapat terlihat berbeda dari nilai sebenarnya karena skala kedua sumbunya tidak sama.
Penskalaan dan geseran#
Penskalaan merentangkan atau menyusutkan bentuk pada setiap sumbu menggunakan matriks diagonal. Faktor memanjangkan bentuk sepanjang sumbu horizontal, faktor antara 0 dan 1 menyusutkannya, sedangkan nilai negatif menambahkan pencerminan.
Geseran menggeser setiap titik secara horizontal sebanding dengan koordinat tegaknya, sehingga persegi berubah menjadi jajar genjang tanpa mengubah tingginya. Faktor geser diletakkan pada posisi di atas diagonal matriks.
Demo berikut menggabungkan ketiga transformasi pada satu bentuk. Atur sudut rotasi, faktor skala kedua sumbu, dan faktor geseran, lalu amati bentuk hasil beserta matriks gabungannya yang ditampilkan di bawah kanvas.
Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>Uji coba transformasi matriks</title>
<style>
body { font-family: sans-serif; margin: 12px; color: #241f31; }
canvas { border: 1px solid #b9bdc7; background: #fafafa; display: block; }
.baris { margin: 8px 0; display: flex; align-items: center; gap: 16px;
flex-wrap: wrap; font-size: 14px; }
label { display: inline-flex; align-items: center; gap: 6px; }
#tampilMatriks { font-family: monospace; background: #f0eeeb;
padding: 4px 8px; border-radius: 4px; }
.kunci { font-size: 13px; }
.chipAsli { display: inline-block; width: 18px; height: 4px;
background: #2c5f8a; vertical-align: middle; }
.chipHasil { display: inline-block; width: 18px; height: 0;
border-top: 3px dashed #c2571a; vertical-align: middle; }
</style>
</head>
<body>
<canvas id="kanvas" width="460" height="230"></canvas>
<div class="baris">
<label>Sudut <input id="sudut" type="range" min="0" max="360" value="30">
<span id="teksSudut">30°</span></label>
<label>Skala x <input id="skalaX" type="range" min="50" max="200" value="100"></label>
<label>Skala y <input id="skalaY" type="range" min="50" max="200" value="100"></label>
<label>Geseran <input id="geser" type="range" min="-100" max="100" value="0"></label>
</div>
<div class="baris">Matriks gabungan: <span id="tampilMatriks"></span></div>
<div class="baris kunci"><span class="chipAsli"></span> bentuk asli
<span class="chipHasil"></span> hasil transformasi</div>
<script>
// titik-titik pada tepi huruf F
var F = [[0,0],[0,4],[2,4],[2,3],[1,3],[1,2],[2,2],[2,1],[0,1],[0,0]];
function kali(A, B) {
// perkalian dua matriks 2x2: baris A dikali kolom B
return [
[A[0][0]*B[0][0] + A[0][1]*B[1][0], A[0][0]*B[0][1] + A[0][1]*B[1][1]],
[A[1][0]*B[0][0] + A[1][1]*B[1][0], A[1][0]*B[0][1] + A[1][1]*B[1][1]]
];
}
function matriksGabungan(sudutDerajat, sx, sy, k) {
var a = sudutDerajat * Math.PI / 180;
var R = [[Math.cos(a), -Math.sin(a)], [Math.sin(a), Math.cos(a)]];
var S = [[sx, 0], [0, sy]];
var K = [[1, k], [0, 1]]; // geseran horizontal
return kali(kali(K, S), R); // urutan: rotasi, skala, lalu geseran
}
function terapkan(M, p) {
return [M[0][0]*p[0] + M[0][1]*p[1], M[1][0]*p[0] + M[1][1]*p[1]];
}
function gambar() {
var sudut = +document.getElementById("sudut").value;
var sx = +document.getElementById("skalaX").value / 100;
var sy = +document.getElementById("skalaY").value / 100;
var k = +document.getElementById("geser").value / 100;
document.getElementById("teksSudut").textContent = sudut + "°";
var M = matriksGabungan(sudut, sx, sy, k);
document.getElementById("tampilMatriks").textContent =
"[[" + M[0][0].toFixed(2) + ", " + M[0][1].toFixed(2) + "], [" +
M[1][0].toFixed(2) + ", " + M[1][1].toFixed(2) + "]]";
var ctx = document.getElementById("kanvas").getContext("2d");
ctx.clearRect(0, 0, 460, 230);
// sistem koordinat: titik asal di (200, 190), sumbu y ke atas, 16 piksel per satuan
ctx.save();
ctx.translate(200, 190);
ctx.scale(16, -16);
ctx.lineWidth = 0.06;
ctx.strokeStyle = "#d8dde5";
for (var gx = -12; gx <= 16; gx += 2) {
ctx.beginPath(); ctx.moveTo(gx, -11); ctx.lineTo(gx, 2); ctx.stroke();
}
for (var gy = -10; gy <= 2; gy += 2) {
ctx.beginPath(); ctx.moveTo(-12, gy); ctx.lineTo(16, gy); ctx.stroke();
}
ctx.strokeStyle = "#9aa2ad";
ctx.lineWidth = 0.1;
ctx.beginPath(); ctx.moveTo(0, -11); ctx.lineTo(0, 2); ctx.stroke(); // sumbu y
ctx.beginPath(); ctx.moveTo(-12, 0); ctx.lineTo(16, 0); ctx.stroke(); // sumbu x
// bentuk asli
ctx.beginPath();
F.forEach(function (p, i) {
if (i === 0) { ctx.moveTo(p[0], p[1]); } else { ctx.lineTo(p[0], p[1]); }
});
ctx.closePath();
ctx.fillStyle = "rgba(44, 95, 138, 0.15)";
ctx.fill();
ctx.lineWidth = 0.14;
ctx.strokeStyle = "#2c5f8a";
ctx.stroke();
// hasil transformasi
ctx.beginPath();
F.forEach(function (p, i) {
var q = terapkan(M, p);
if (i === 0) { ctx.moveTo(q[0], q[1]); } else { ctx.lineTo(q[0], q[1]); }
});
ctx.closePath();
ctx.lineWidth = 0.14;
ctx.setLineDash([0.35, 0.25]);
ctx.strokeStyle = "#c2571a";
ctx.stroke();
ctx.restore();
// label sumbu dan titik asal (digambar pada koordinat layar)
ctx.fillStyle = "#6b7280";
ctx.font = "12px sans-serif";
ctx.fillText("y", 206, 18);
ctx.fillText("x", 446, 185);
ctx.fillText("(0, 0)", 186, 203);
}
["sudut", "skalaX", "skalaY", "geser"].forEach(function (id) {
document.getElementById(id).addEventListener("input", gambar);
});
gambar();
</script>
</body>
</html>Matriks gabungan pada demo dihitung sebagai hasil kali ketiga matriks, yaitu rotasi diterapkan lebih dahulu, kemudian penskalaan, lalu geseran. Mengubah urutan perkaliannya mengubah bentuk akhir, konsisten dengan sifat perkalian matriks yang tidak komutatif. Matriks gabungan pun tetap berukuran meskipun tiga transformasi digabungkan, karena hasil kali matriks-matriks seukuran mempertahankan ukurannya.
Di mana hal ini ditemui dalam kode#
Transformasi matriks bekerja di belakang hampir semua gambar yang bergerak pada layar, dan bentuk eksplisitnya dapat ditemukan pada dokumentasi maupun kode sumber. Mengenali polanya membantu saat membaca pustaka grafik atau pemrosesan citra. Beberapa tempat yang mudah dikenali adalah sebagai berikut:
- Mesin grafik menerapkan matriks transformasi pada setiap objek sebelum menggambar, dan rangkaian transformasinya ditulis sebagai perkalian matriks berurutan.
- Rotasi citra pada pemrosesan gambar menghitung posisi baru setiap piksel menggunakan matriks rotasi, termasuk saat citra diputar untuk mengoreksi kemiringan pindai.
- Augmentasi data pada machine learning memperbanyak contoh latihan dengan memutar dan menskalakan citra secara acak melalui transformasi yang sama.
- Properti CSS
transformdengan fungsirotate(),scale(), danskew()diterjemahkan browser menjadi perkalian matriks yang sama dengan demo di atas. - Kamera pada aplikasi tiga dimensi dirotasi dua kali secara berurutan, dan urutan rotasinya menentukan arah pandang akhir karena perkalian matriks tidak komutatif.
Kelima contoh menggunakan matriks atau perluasannya pada ruang berdimensi lebih tinggi. Inti perhitungannya tetap sama, yaitu perkalian satu matriks dengan satu vektor pada setiap titik. Perbedaannya hanya terletak pada banyaknya baris dan kolom matriks yang terlibat.
Ringkasan#
- Mengalikan matriks dengan vektor titik memetakan titik ke posisi baru, dan kolom matriks menyatakan posisi baru vektor dasar.
- Rotasi menggunakan kosinus dan sinus pada matriks serta menjaga jarak antartitik, sedangkan penskalaan menggunakan matriks diagonal.
- Geseran menggeser setiap titik sebanding koordinat tegaknya dan mengubah persegi menjadi jajar genjang.
- Transformasi gabungan merupakan hasil kali matriks-matriksnya, dan urutan perkaliannya menentukan hasil akhir.
Referensi#
- Matplotlib documentation — Matplotlib project
— Dokumentasi
subplots,set_aspect, dansavefigyang digunakan pada skrip visualisasi topik ini. - NumPy 2.1 documentation — NumPy project
— Dokumentasi
np.cos,np.sin, dannp.deg2radyang menyusun matriks rotasi. - Mathematics for Machine Learning — Cambridge University Press — Bab aljabar linear menggunakan transformasi geometris sebagai pintu masuk untuk memahami matriks.