Cara Membuat Pohon dengan Transformasi Affine dan Fractal
Transformasi yang akan saya bagikan sekarang sedikit berbeda dengan transformasi yang sudah dipelajari di bangku SMA maupun SMP.
Menurut Barnsley, 1988 Sistem fungsi iterasi (SFI) adalah suatu sistem yang terdiri dari suatu ruang metrik lengkap (X,d) bersama dengan himpunan berhingga dari pemetaan kontraksi wn : X ® X dengan faktor kontraksi masing-masing adalah sn, untuk n = 1,2,…,N. Dan biasanya diberi notasi {X; wn , n=1,2,…,N}.
Sedangkan
yang dimaksud dengan Atraktor dari suatu sistem fungsi iterasi adalah suatu
titik tetap (fixed point) A yang merupakan himpunan bagian kompak dari X yang
memenuhi
A = W(A) = w1(A) È w2(A) È … ÈwN(A),
yaitu A = Lim n®¥ Wn(B) , untuk sembarang
B himpunan bagian kompak dari X, dimana
W1(B)=W(B), W2(B)=W(W(B)),
W3(B)=W(W2(B)),…,Wn(B)=W(Wn-1(B)).
Untuk membuat suatu gambar fractal, sebagai pemetaan
kontraksinya dapat digunakan transformasi affine sebagai berikut :
Ada dua algoritma untuk menggambar Fraktal, yaitu deterministik dan random. Perbedaan dari kedua algoritma ini sangatlah terasa. Jika menggunakan deterministik kita dapat menentukan sendiri jumlah perulangan yang digunakan. Tetapi dengan iterasi random kita melakukan iterasi sebanyak mungkin hingga terbentuk bentuk yang kita inginkan.
Ada dua algoritma untuk menggambar Fraktal, yaitu deterministik dan random. Perbedaan dari kedua algoritma ini sangatlah terasa. Jika menggunakan deterministik kita dapat menentukan sendiri jumlah perulangan yang digunakan. Tetapi dengan iterasi random kita melakukan iterasi sebanyak mungkin hingga terbentuk bentuk yang kita inginkan.
Algoritma Deterministik
Menentukan window {xwmin, ywmin, xwmax, ywmax} dan viewport {xvmin, yvmin, xvmax, yvmax};
Baca data fungsi Affine {a, b, c, d, e, f};
Buat gambar sembarang dalam viewport;
iterasi = 0;
reperat
for i = xvmin to xvmax do begin
for j = yvmin to yvmax do begin
if(pixels[i, j] = warnaobjek) then begin
VW(i, j, x, y) // Transformasi viewport ke window dan hasilnya simpan di x, y
for k = 1 to n do begin // dengan n banyak fungsi transformasi Affine
xb = a[k] * x + b[k] * y + e[k]
yb = c[k] * x + d[k] * y + f[k]
WV(xb, yb, xi, yi) // Transfornasi window ke viewport dan hasilnya di simpan di xi, yi
bm.pixels[xl, yl] = warnaobjek
end;
end;
end;
copy(bm)
inc(iterasi)
until(iterasi = numit) // sesuaikan sampai terbentuk bentuk yang diinginkan
end
Penjelasan singkatnya:
Pertama tentukan window dan viewport yang akan dibuat. Setelah itu tentukan rumus affine yang akan dibuat. Selanjutnya berikan sembarang gambar dalam viewport, hal ini digunakan untuk ditransformasikan ke bentuk selanjutnya. Scan viewport jika menemukan pixels yang membentuk gambar sembarang tadi lakukan transformasi affine. lakukan hal ini sampai membantuk objek yang diinginkan.
Algoritma Iterasi random
Menentukan window {xwmin, ywmin, xwmax, ywmax} dan viewport {xvmin, yvmin, xvmax, yvmax};
Baca data fungsi Affine {a, b, c, d, e, f}; banyaktitik = 300000
x = 0; y = 0; // sebagai titik awal transformasi
for i = 0 to banyaktitik do begin
r = random
if r<1/3 then k = 1; else if r<2/3 then k = 2; else k = 3; // sesuaikan jumlah fungsi transformasi affine
xb = a[k] * x + b[k] * y + e[k]
yb = c[k] * x + d[k] * y + f[k]
if i>10 then begin
WV(xb, yb, xi, yi) // Transformasi window ke viewport dan hasilnya di xi, yi
bm.pixels[xi, yi] = warnaobjek
end
x = xb
y = yb
end
Penjelasan singkat:
Pertama tentukan window dan viewport yang dibutuhkan. Baca / tentukan transformasi affine yang digunakan. Tentukan jumlah literasi dengan variabel banyak titik. Lakukann literasi dengan menggunakan pemilihan fungsi secara random. Gambar hasilnya. lakukan sampai i = banyak literasi / banyak titik.
Untuk memahaminya anda haruslah paham mengenai cara menentukan window to viewport atau sebaliknya.
Window to Viewport
Window adalah koordinat dunia nyata atau yang biasa kita sebut dengan koordinat kartesius. Sementara itu viewport adalah koordinat elektronik atau koordinat layar pada komputer.
Window mendefinisikan apa yang ingin ditampilkan. Dalam artikel ini kita ingin menampilkan kurva y = x^2. Viewport mendefinisikan di mana ditampilkannya. Pemetaan bagian dari koordinat dunia nyata ke koordinat device sering disebut dengan window to viewport transformation atau windowing transformation.
Langkah-langkah transformation
- Buat world-coordinate scene menggunakan pemodelan dan transformasi koordinat
- Konversi world-coordinate ke viewing coordinate
- Petakan viewing coordinate ke normalized viewing coordinate menggunakan spesifikasi window-viewport
- Petakan normalized viewing coordinat ke koordinat device
Viewport biasanya didefinisikan dalam bentuk persegi panjang atau square. Namun bisa juga dengan membuatnya menjadi lingkaran atau bentuk lainnya.
Lihat gambar diatas, window dengan viewport memiliki perbandingan yang sama antara jarak titik dan panjang atau lebar titik. Dalam matematika waktu smp kita telah belajar mengenai perbandingan. Dengan cara ini kita dapat menentukan letak titik pada device atau komputer.
Titik di posisi (xw, yw) pada window dipetakkan di posisi (xv, yv) pada viewport. Agar posisi relativ sama gunakan rumus berikut ini.
Dengan sifat matematika didapatkan rumus sebagai berikut :
Sekarang saatnya kita menggambarkannya dalam source code.
Untuk Viewport to window dapat anda cari dengan rumus diatas dengan cara yang sama melalui perbandingan matematika.
Untuk Viewport to window dapat anda cari dengan rumus diatas dengan cara yang sama melalui perbandingan matematika.
Menggambar Pohon dengan Affine Fractal
Masuk ke dalam menggambar grafik affine. Pertama tentukan window dan viewportnya. Disini saya menggunakan window dan viewport
xwmin:=-3;xwmax:=3;ywmin:=0;ywmax:=7;
xvmin:=0;xvmax:=600;yvmin:=0;yvmax:=700;
Untuk rumus affine saya menggunakna :
alpa:=5; beta:=-50; gama:=40;
a[1]:=0;b[1]:=0;c[1]:=0;d[1]:=0.37;e[1]:=0;f[1]:=0;
a[2]:=0.65*cos(alpa*pi/180);
b[2]:=-0.65*sin(alpa*pi/180);
c[2]:=0.65*sin(alpa*pi/180);
d[2]:=0.65*cos(alpa*pi/180);
e[2]:=0; f[2]:=2.5;
a[3]:=0.5*cos(beta*pi/180);
b[3]:=-0.5*sin(beta*pi/180);
c[3]:=0.5*sin(beta*pi/180);
d[3]:=0.5*cos(beta*pi/180);
e[3]:=0; f[3]:=1.5;
a[4]:=0.5*cos(gama*pi/180);
b[4]:=-0.5*sin(gama*pi/180);
c[4]:=0.5*sin(gama*pi/180);
d[4]:=0.5*cos(gama*pi/180);
e[4]:=0; f[4]:=1.7;
Saya akan menggunakan algoritma deterministik dengan sedikit perubahan karena saya tidak dapat menemukan fungsi copyrect pada javascript.
Source code Affine Pohon dapat dilihat dibawah ini:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Pohon</title> | |
</head> | |
<body> | |
<canvas id="canvas" width="1400" height="700"></canvas> | |
<p>copyright: <a href="zonapemrograman.blogspot.com">zonapemrograman.blogspot.com</a></p> | |
<script> | |
const ctx = document.getElementById("canvas") | |
const canvas = ctx.getContext("2d") | |
// window | |
const xwmin = -3 | |
const xwmax = 3 | |
const ywmin = 0 | |
const ywmax = 7 | |
//viewport | |
const xvmin = 0 | |
const xvmax = 600 | |
const yvmin = 0 | |
const yvmax = 700 | |
// viewport 2 | |
const xvmin2 = 700 | |
const xvmax2 = 1300 | |
const yvmin2 = 0 | |
const yvmax2 = 700 | |
// function window to viewport 2 | |
function WV2(xw, yw) { | |
const xv = xvmin2 + Math.round((xw - xwmin) * (xvmax2 - xvmin2) / (xwmax - xwmin)) | |
const yv = yvmax2 - Math.round((yw - ywmin) * (yvmax2 - yvmin2) / (ywmax - yvmin)) | |
return { x: xv, y: yv } | |
} | |
function VW2(xv, yv) { | |
const xw = (xwmin + (xv - xvmin2) * (xwmax - xwmin) / (xvmax2 - xvmin2)) | |
const yw = (ywmin + (yvmax2 - yv) * (ywmax - ywmin) / (yvmax2 - yvmin2)) | |
return { x: xw, y: yw } | |
} | |
// function window to viewport | |
function WV(xw, yw) { | |
const xv = xvmin + Math.round((xw - xwmin) * (xvmax - xvmin) / (xwmax - xwmin)) | |
const yv = yvmax - Math.round((yw - ywmin) * (yvmax - yvmin) / (ywmax - yvmin)) | |
return { x: xv, y: yv } | |
} | |
function VW(xv, yv) { | |
const xw = (xwmin + (xv - xvmin) * (xwmax - xwmin) / (xvmax - xvmin)) | |
const yw = (ywmin + (yvmax - yv) * (ywmax - ywmin) / (yvmax - yvmin)) | |
return { x: xw, y: yw } | |
} | |
const drawFilledCircle = (centerX, centerY, radius, color) => { | |
canvas.beginPath(); | |
canvas.strokeStyle = color; | |
canvas.arc(centerX, centerY, radius, 0, 2 * Math.PI, true); | |
canvas.stroke(); | |
canvas.closePath() | |
} | |
function getPixel(i, j) { | |
let bm = canvas.getImageData(i, j, 1, 1); | |
if (bm.data[0] != 255) | |
return true; | |
else if (bm.data[1] != 255) | |
return true; | |
else if (bm.data[2] != 255) | |
return true; | |
return false; | |
} | |
function drawRect(width, height, color) { | |
canvas.beginPath() | |
canvas.fillStyle = color | |
canvas.fillRect(xvmin, yvmin, width, height) | |
canvas.closePath() | |
} | |
function drawRect2(width, height, color) { | |
canvas.beginPath() | |
canvas.fillStyle = color | |
canvas.fillRect(xvmin2, yvmin2, width, height) | |
canvas.closePath() | |
} | |
// Transformasi Affine | |
const alpa = 5; const beta = -50; const gama = 40 | |
let a = [0], b = [0], c = [0], d = [0.37], e = [0], f = [0]; | |
a.push(0.65 * Math.cos(alpa * Math.PI / 180)) | |
b.push(-0.65 * Math.sin(alpa * Math.PI / 180)) | |
c.push(0.65 * Math.sin(alpa * Math.PI / 180)) | |
d.push(0.65 * Math.cos(alpa * Math.PI / 180)) | |
e.push(0); f.push(2.5) | |
a.push(0.5 * Math.cos(beta * Math.PI / 180)) | |
b.push(-0.5 * Math.sin(beta * Math.PI / 180)) | |
c.push(0.5 * Math.sin(beta * Math.PI / 180)) | |
d.push(0.5 * Math.cos(beta * Math.PI / 180)) | |
e.push(0); f.push(1.5) | |
a.push(0.5 * Math.cos(gama * Math.PI / 180)) | |
b.push(-0.5 * Math.sin(gama * Math.PI / 180)) | |
c.push(0.5 * Math.sin(gama * Math.PI / 180)) | |
d.push(0.5 * Math.cos(gama * Math.PI / 180)) | |
e.push(0); f.push(1.7) | |
drawRect((xvmax - xvmin), (yvmax - yvmin), "rgba(255, 255, 255, 255)") | |
drawRect2((xvmax2 - xvmin2), (yvmax2 - yvmin2), "rgba(255, 255, 255, 255)") | |
drawFilledCircle((xvmax - xvmin) / 2, (yvmax - yvmin) / 2, (300 - 10), "rgba(0, 0, 0, 255)") | |
canvas.stroke() | |
async function pohon() { | |
let numit = 8; let k = 0 | |
while (k <= numit) { | |
await new Promise((resolve, reject) => setTimeout(resolve, 0)) | |
if (k % 2 == 0) { | |
console.log("iterasi ke- " + k + " di k % 2 == 0") | |
for (let i = xvmin + 2; i < xvmax - 2; i++) { | |
for (let j = yvmin + 2; j < yvmax - 2; j++) { | |
if (getPixel(i, j)) { | |
let p = VW(i, j) | |
xb = a[0] * p.x + b[0] * p.y + e[0] | |
yb = c[0] * p.x + d[0] * p.y + f[0] | |
let q = WV2(xb, yb) | |
drawFilledCircle(q.x, q.y, 1, "rgb(128, 128, 0)") | |
xb = a[1] * p.x + b[1] * p.y + e[1] | |
yb = c[1] * p.x + d[1] * p.y + f[1] | |
q = WV2(xb, yb) | |
drawFilledCircle(q.x, q.y, 1, "green") | |
xb = a[2] * p.x + b[2] * p.y + e[2] | |
yb = c[2] * p.x + d[2] * p.y + f[2] | |
q = WV2(xb, yb) | |
drawFilledCircle(q.x, q.y, 1, "green") | |
xb = a[3] * p.x + b[3] * p.y + e[3] | |
yb = c[3] * p.x + d[3] * p.y + f[3] | |
q = WV2(xb, yb) | |
drawFilledCircle(q.x, q.y, 1, "green") | |
} | |
} | |
} | |
drawRect((xvmax - xvmin), (yvmax - yvmin), "rgba(255, 255, 255, 255)") | |
} | |
else { | |
console.log("iterasi ke- " + k + " di else") | |
for (let i = xvmin2 + 2; i < xvmax2 - 2; i++) { | |
for (let j = yvmin2 + 2; j < yvmax2 - 2; j++) { | |
if (getPixel(i, j)) { | |
let p = VW2(i, j) | |
xb = a[0] * p.x + b[0] * p.y + e[0] | |
yb = c[0] * p.x + d[0] * p.y + f[0] | |
let q = WV(xb, yb) | |
drawFilledCircle(q.x, q.y, 1, "rgb(128, 128, 0)") | |
xb = a[1] * p.x + b[1] * p.y + e[1] | |
yb = c[1] * p.x + d[1] * p.y + f[1] | |
q = WV(xb, yb) | |
drawFilledCircle(q.x, q.y, 1, "green") | |
xb = a[2] * p.x + b[2] * p.y + e[2] | |
yb = c[2] * p.x + d[2] * p.y + f[2] | |
q = WV(xb, yb) | |
drawFilledCircle(q.x, q.y, 1, "green") | |
xb = a[3] * p.x + b[3] * p.y + e[3] | |
yb = c[3] * p.x + d[3] * p.y + f[3] | |
q = WV(xb, yb) | |
drawFilledCircle(q.x, q.y, 1, "green") | |
} | |
} | |
} | |
drawRect2((xvmax2 - xvmin2), (yvmax2 - yvmin2), "rgba(255, 255, 255, 255)") | |
} | |
k++ | |
} | |
console.log("finish") | |
} | |
pohon() | |
</script> | |
</body> | |
</html> |
Terimakasih telah membaca artikel ini jika anda terbantu dengan artikel ini silakan share ke web yang anda mau terimakasih.
Kemarin gue nonton anime Violet Evergarden :v. Rekomended untuk ditonton wkwk. :)
Berkomentarlah secara bijak.
EmoticonEmoticon