
ํ๋ํ์ ์๊ธฐ ์ ์ฌ์ฑ(Self-Similarity)์ ๊ฐ์ง๋ ๊ธฐํํ์ ํํ๋ก, ์๋ฌด๋ฆฌ ํ๋ํด๋ ์ ์ฒด์ ๋น์ทํ ๋ชจ์์ด ๋ฐ๋ณต๋๋ ํน์ง์ ๊ฐ์ต๋๋ค.
์์ ๋ค๋ฃจ์ด๋ณธ ์ฌ๊ท(Recursion)์ ํ๋ํ์ ์ฝ๋๋ก ๊ตฌํํ๋ ์๊ณ ๋ฆฌ์ฆ์ ๋๊ตฌ์
๋๋ค.
์ด๋ฒ์ ๋ค๋ฃจ์ด๋ณผ ํ๋ํ์ ์๋์ ๊ฐ์ด ๊ตฌ๋ถํ ์ ์์ต๋๋ค.
์ฌ๊ท ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์์ ๋ํ๊ณผ ๋ช ํํ ๋ณํ ๊ท์น์ ๋ฐ๋ณต์ ์ผ๋ก ์ ์ฉํ๋ฉฐ ํ์ฅํ๋ ํ๋ํ์ ๋๋ค. ์ด ๋ฐฉ์์ Geometry์ Transformations ๊ฐ๋ ์ ์ตํ๋ ๋ฐ ๊ฐ์ฅ ์ข์ต๋๋ค.
๋ฐ๋ณต(Iteration) ๊ณ์ฐ, ์ฃผ๋ก ๋ณต์์ ๊ณ์ฐ ๋๋ ๋ฐ๋ณต์ ์ธ ํฝ์ ๋งคํ์ ์ฌ์ฉํ์ฌ ๊ตฌ์กฐ๋ฅผ ์์ฑํ๋ ํ๋ํ์ ๋๋ค. ์ด ๋ฐฉ์์ ์ํ์ ๊น์ด์ ์์ ๋งคํ(Color Mapping) ๊ธฐ์ ์ ์ตํ๋ ๋ฐ ์ค์ํฉ๋๋ค.
๋ฆฐ๋ด๋ง์ด์ด ์์คํ (Lindenmayer System)์ด๋ผ๋ ํ์ ๋ฌธ๋ฒ์ ์ฌ์ฉํ์ฌ ๋ฌธ์์ด์ ํ์ฅํ๊ณ , ์ด ๋ฌธ์์ด์ ํฐํ ๊ทธ๋ํฝ์ค(Turtle Graphics)๋ก ํด์ํ์ฌ ์๊ฐํํ๋ ๋ฐฉ์์ ๋๋ค. ๊ท์น ๊ธฐ๋ฐ ์์ฑ๊ณผ ํ์ฑ ๋ก์ง์ ์ตํ๋ ๋ฐ ์ ์ฉํฉ๋๋ค.
์ด ์ค์์ ๋ช ๊ฐ์ง๋ง ์ฝ๋๋ก ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค.


let cols = 800;
let rows = 800;
let maxIter = 100; // ์ต๋ ๋ฐ๋ณต ํ์
function setup() {
createCanvas(cols, rows);
pixelDensity(1);
colorMode(RGB, 255);
noLoop();
}
function draw() {
background(25);
loadPixels();
for (let x = 0; x < cols; x++) {
for (let y = 0; y < rows; y++) {
// ํ๋ฉด ์ขํ (ํฝ์
) โ ๋ณต์ํ๋ฉด ์ขํ (c = ca + i*cb)
// ๋ง๋ธ๋ธ๋กํธ ์งํฉ์ ์ฃผ์ ์์ญ์ ํฌํจํ๋๋ก ๋งคํ ๋ฒ์ ์กฐ์
let ca = map(x, 0, cols, -2.5, 1);
let cb = map(y, 0, rows, -1.5, 1.5);
let a = 0; // z์ ์ค์๋ถ (z0 = 0)
let b = 0; // z์ ํ์๋ถ (z0 = 0)
let n = 0; // ๋ฐ์ฐ๊น์ง์ ๋ฐ๋ณต ํ์ ์นด์ดํฐ
while (n < maxIter) {
// z_next = z^2 + c ๊ณ์ฐ
let aa = a * a - b * b; // z^2์ ์ค์๋ถ
let bb = 2 * a * b; // z^2์ ํ์๋ถ
a = aa + ca; // z_next์ ์ค์๋ถ
b = bb + cb; // z_next์ ํ์๋ถ
// ๋ฐ์ฐ ์กฐ๊ฑด: ๋ณต์์์ ํฌ๊ธฐ๊ฐ ํน์ ์๊ณ๊ฐ์ ๋์ผ๋ฉด ๋ฐ์ฐ์ผ๋ก ๊ฐ์ฃผ
// (a^2 + b^2) > 16 ๋๋ abs(a) + abs(b) > 16 ๋ฑ์ผ๋ก ์ต์ ํ ๊ฐ๋ฅ
if (a * a + b * b > 16) {
break;
}
n++;
}
let r, g, bb; // 'b' ๋ณ์์์ ์ถฉ๋์ ํผํ๊ธฐ ์ํด 'bb'๋ก ์ ์ธ
if (n < maxIter) { // ๋ฐ์ฐํ ๊ฒฝ์ฐ (์งํฉ ์ธ๊ณฝ)
// ๋ฐ์ฐ ๋ฐ๋ณต ํ์ 'n'์ ๋ฐ๋ผ ๋ฏผํธ์์ ๋ฐ๊ธฐ๋ฅผ ์กฐ์
// n์ด ์์์๋ก ์ด๋ก๊ณ , ํด์๋ก ๋ฐ๊ฒ (๋๋ ๊ทธ ๋ฐ๋๋ก)
let brightnessFactor = map(n, 0, maxIter, 0.2, 1.0); // 20% ~ 100% ๋ฐ๊ธฐ
r = 30 * brightnessFactor;
g = 190 * brightnessFactor;
bb = 180 * brightnessFactor;
} else { // ๋ฐ์ฐํ์ง ์์ ๊ฒฝ์ฐ (๋ง๋ธ๋ธ๋กํธ ์งํฉ ๋ด๋ถ)
// ๋ง๋ธ๋ธ๋กํธ ์งํฉ ๋ด๋ถ๋ ์ด๋์ด ๋ฐฐ๊ฒฝ์๊ณผ ๋น์ทํ๊ฒ ์ค์ ํ์ฌ '๊ฒ์์'์ผ๋ก ๋ณด์
r = 25;
g = 25;
bb = 25;
}
let pix = (x + y * cols) * 4;
pixels[pix + 0] = r; // Red
pixels[pix + 1] = g; // Green
pixels[pix + 2] = bb; // Blue
pixels[pix + 3] = 255; // Alpha (๋ถํฌ๋ช
)
}
}
updatePixels();
}
๐ ์ค๋ช
cols, rows๋ก ์บ๋ฒ์ค ํฌ๊ธฐ๋ฅผ ์ ํ๊ณ , maxIter๋ก ๋ฐ์ฐ๊น์ง์ ์ต๋ ๋ฐ๋ณต ํ์๋ฅผ ๊ฒฐ์ ํฉ๋๋ค. maxIter๊ฐ ํด์๋ก ์งํฉ ๊ฒฝ๊ณ์ ๋ํ
์ผ์ด ์ด์๋์ง๋ง, ์งํฉ ๋ด๋ถ์ ์ํ๋ ๋ชจ๋ ํฝ์
์ ๋ํด ๋ถํ์ํ ๊ณ์ฐ ์๊ฐ์ด ์ฆ๊ฐํฉ๋๋ค.pixelDensity(1): ๊ณ ํด์๋ ํ๋ฉด์์ ํฝ์
์ค๋ณต ๊ณ์ฐ์ ๋ฐฉ์งํ๋ ์ฝ๋์
๋๋ค.loadPixels() / updatePixels(): ํฝ์
๋ฐฐ์ด์ ์ง์ ์ ๊ทผํ์ฌ CPU ๊ธฐ๋ฐ์์ ๋น ๋ฅธ ํฝ์
์กฐ์์ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค. map(x, 0, cols, -2.5, 1): ํฝ์
์์น ๋ณต์์ ์ ์ค์๋ถ() ๋ฐ ํ์๋ถ()๋ก ๋ณํํฉ๋๋ค. ์ด ๋ฒ์๊ฐ ๋ง๋ธ๋ธ๋กํธ ์งํฉ์ ์ฃผ๋ ์ฌ์ฅํ ์์ญ์ ํฌํจํฉ๋๋ค.while (n < maxIter): ๋ง๋ธ๋ธ๋กํธ ๊ณต์ ์ ๋ฐ๋ณตํ์ฌ ๋ณต์์ ๊ฐ ๋ฌดํ๋๋ก ๋ฐ์ฐํ๋์ง ํ์ธํฉ๋๋ค. ๋ ๋์ ์ฑ๋ฅ์ ์ํด์๋ ๊ณ์ฐ์ GPU์์ ํ๋ WebGL/GLSL ์
ฐ์ด๋ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌํํด์ผ ํฉ๋๋ค.(createShader ์ฌ์ฉ)๐ง ์ํ์ /์๊ณ ๋ฆฌ์ฆ ๋ถ์
A. ๋ง๋ธ๋ธ๋กํธ ๊ณต์ ()
B. ํ๋ํ ์๊ฐํ ์๋ฆฌ
๐ ์ปฌ๋ฌ ๊ตฌํ ๋ก์ง
colorMode(RGB, 255): RGB ๋ชจ๋๋ฅผ ์ฌ์ฉํ์ฌ ์ง๊ด์ ์ผ๋ก ์ํ๋ ์์ ๊ฐ์ ์ ์ฉํฉ๋๋ค.n์ ๋ฐ๊ธฐ ๊ณ์(brightnessFactor)๋ก ๋ณํํ์ฌ ์งํฉ ์ธ๊ณฝ์ ์์กฐ๋ฅผ ์ ์งํ๋ฉด์ ๋ฐ๋ณต ํ์์ ๋ฐ๋ฅธ ์ธต์๋ฅผ ์๊ฐ์ ์ผ๋ก ํํํ์ต๋๋ค.n์ด ์์์๋ก โ brightnessFactor๊ฐ ์์ โ ์ด๋์ด ๋ฏผํธ์n์ด ํด์๋ก โ brightnessFactor๊ฐ ํผ โ ๋ฐ์ ๋ฏผํธ์
let W = 800;
let H = 400;
// ์ฌ๊ท ๊น์ด (๋ฐ๋ณต ํ์)
// 0: ๋จ์ํ ์ ๋ถ, 1: ํฑ๋ ๋ชจ์, 5-6 ์ ๋๊ฐ ์ ์
let maxDepth = 5;
function setup() {
// ์บ๋ฒ์ค๋ฅผ ์์ฑํฉ๋๋ค.
createCanvas(W, H);
// ๋ฐฐ๊ฒฝ์ ์ด๋์ด ์์ผ๋ก ์ค์ ํฉ๋๋ค.
background(20);
// ์ ๋ถ์ ์์๊ณผ ๊ตต๊ธฐ๋ฅผ ์ค์ ํฉ๋๋ค.
stroke(30, 240, 210);
strokeWeight(1);
noFill();
// ์ฝํ ๊ณก์ ์ ์์์ ๊ณผ ๋์ ์ ์ ์ํฉ๋๋ค.
// ์บ๋ฒ์ค ์ค์ ํ๋จ์ ๋๊ฒ ํผ์ณ์ง๋๋ก ์ค์ ํฉ๋๋ค.
let p1 = createVector(50, H - 50);
let p2 = createVector(W - 50, H - 50);
// ์ฌ๊ท ํจ์๋ฅผ ํธ์ถํ์ฌ ์ฝํ ๊ณก์ ์ ๊ทธ๋ฆฝ๋๋ค.
// ์ด๊ธฐ ๊น์ด๋ 0์
๋๋ค.
koch(p1, p2, 0);
// 'noLoop()'๋ฅผ ์ฌ์ฉํ์ฌ draw() ํจ์๊ฐ ๋ฐ๋ณต ์คํ๋์ง ์๋๋ก ํฉ๋๋ค.
// ์ ์ ์ธ ๊ฒฐ๊ณผ๋ฌผ๋ง ํ์ํ๋ฏ๋ก ์ฑ๋ฅ ์ต์ ํ์ ๋์์ด ๋ฉ๋๋ค.
noLoop();
}
/**
* ์ฝํ ๊ณก์ ์ ์ฌ๊ท์ ์ผ๋ก ์์ฑํ๋ ํจ์์
๋๋ค.
* @param {p5.Vector} startP - ํ์ฌ ์ ๋ถ์ ์์์
* @param {p5.Vector} endP - ํ์ฌ ์ ๋ถ์ ๋์
* @param {number} depth - ํ์ฌ ์ฌ๊ท ๊น์ด
*/
function koch(startP, endP, depth) {
// 1. **์ฌ๊ท ํ์ถ ์กฐ๊ฑด (Base Case)**:
// ์ฌ๊ท ๊น์ด๊ฐ 'maxDepth'์ ๋๋ฌํ๋ฉด ๋ ์ด์ ๋ถํ ํ์ง ์๊ณ ํ์ฌ ์ ๋ถ๋ง ๊ทธ๋ฆฝ๋๋ค.
if (depth >= maxDepth) {
line(startP.x, startP.y, endP.x, endP.y);
return;
}
// 2. **์ ๋ถ ๋ถํ ์ ์ํ ์ค๊ฐ์ ๊ณ์ฐ**:
// ํ์ฌ ์ ๋ถ (startP - endP)์ 3๋ฑ๋ถํ๊ณ , ์ ์ผ๊ฐํ์ ๋ง๋ค๊ธฐ ์ํ ๊ผญ์ง์ ์ ๊ณ์ฐํฉ๋๋ค.
// ์ ๋ถ์ ๋ฒกํฐ๋ก ํํ: V = endP - startP
let v = p5.Vector.sub(endP, startP);
// a: ์์์ ์์ ์ ์ฒด ๊ธธ์ด์ 1/3 ์ง์
let a = p5.Vector.add(startP, v.copy().mult(1 / 3));
// c: ์์์ ์์ ์ ์ฒด ๊ธธ์ด์ 2/3 ์ง์
let c = p5.Vector.add(startP, v.copy().mult(2 / 3));
// b: ๋์ถ๋ ์ ์ผ๊ฐํ์ ๊ผญ์ง์ (a์ c ์ฌ์ด์ ์ค๊ฐ ์ง์ ์ 60๋ ํ์ )
// 1. ๋ฒกํฐ v๋ฅผ 1/2๋ก ์ค์ด๊ณ , startP์ ๋ํด ์ค๊ฐ์ (a์ c์ ์ค๊ฐ)์ ๊ตฌํฉ๋๋ค. (ํ์์์)
// 2. a์์ c๋ก ํฅํ๋ ๋ฒกํฐ๋ฅผ ๊ตฌํฉ๋๋ค: acV = c - a = v/3
// 3. acV๋ฅผ 60๋ (PI/3 ๋ผ๋์) ๋ฐ์๊ณ ๋ฐฉํฅ์ผ๋ก ํ์ ์ํต๋๋ค.
let b = p5.Vector.sub(c, a); // b๋ ์ด์ a-c ๊ฐ์ ๋ฒกํฐ (v/3)
b.rotate(-PI / 3); // 60๋ (PI/3) ํ์ -> ์ฝํ ๋๊ฝ์ ๋ง๋ค๋ ค๋ฉด PI/3์ ์ฌ์ฉํฉ๋๋ค.
b.add(a); // ์์์ a๋ฅผ ๋ํด์ ์ ๋ ์์น๋ฅผ ๊ตฌํฉ๋๋ค.
// 3. **์ฌ๊ท ํธ์ถ (Recursive Step)**:
// ํ์ฌ ์ ๋ถ์ 4๊ฐ์ ์์ ์ ๋ถ์ผ๋ก ๋์ฒดํ๋ฉฐ ๋ค์ ๊น์ด๋ก ์ฌ๊ท ํธ์ถํฉ๋๋ค.
// [startP] -- [a] -- [b] -- [c] -- [endP] ์์์
๋๋ค.
let nextDepth = depth + 1;
// 1. ์์์ (startP) -> ์ฒซ ๋ฒ์งธ ์ง์ (a)
koch(startP, a, nextDepth);
// 2. ์ฒซ ๋ฒ์งธ ์ง์ (a) -> ๋์ถ๋ ๊ผญ์ง์ (b)
koch(a, b, nextDepth);
// 3. ๋์ถ๋ ๊ผญ์ง์ (b) -> ์ธ ๋ฒ์งธ ์ง์ (c)
koch(b, c, nextDepth);
// 4. ์ธ ๋ฒ์งธ ์ง์ (c) -> ๋์ (endP)
koch(c, endP, nextDepth);
}
// ์ฐธ๊ณ : ์ฝํ ๋๊ฝ(Koch Snowflake)์ ๋ง๋ค๋ ค๋ฉด
// setup()์์ ์ธ ๊ฐ์ koch() ํธ์ถ์ ์ฌ์ฉํ์ฌ ์ ์ผ๊ฐํ์ ์ธ ๋ณ์ ํ๋ํํํด์ผ ํฉ๋๋ค.

let W = 800;
let H = 700;
// ์ฌ๊ท ๊น์ด (๋ฐ๋ณต ํ์)
// 0: ํ๋์ ์ผ๊ฐํ, 6~7 ์ ๋๊ฐ ์ ์
let maxDepth = 7;
function setup() {
// ์บ๋ฒ์ค๋ฅผ ์์ฑํฉ๋๋ค.
createCanvas(W, H);
// ๋ฐฐ๊ฒฝ์ ์ด๋์ด ์์ผ๋ก ์ค์ ํฉ๋๋ค.
background(20);
// ์ ์คํ์ผ ์ค์ : ์ธ๊ณฝ์ ์ ํฐ์, ์ฑ์ฐ๊ธฐ๋ ๊ฒ์์ (๋น์ด์๋ ํจ๊ณผ)
stroke(30, 240, 210);
strokeWeight(1);
fill(20); // ๋ฐฐ๊ฒฝ์๊ณผ ๊ฐ์ ์์ผ๋ก ์ฑ์ ์ญ์ผ๊ฐํ์ด '๋น์ด ๋ณด์ด๊ฒ' ๋ง๋ญ๋๋ค.
// ์์๋ฅดํ์คํค ๊ฐ์คํท์ ์ธ ๊ผญ์ง์ ์ ์ ์ํฉ๋๋ค.
// ์บ๋ฒ์ค ์ค์ ์๋จ, ์ผ์ชฝ ํ๋จ, ์ค๋ฅธ์ชฝ ํ๋จ์ ์ ์ผ๊ฐํ ๋ชจ์์ผ๋ก ๋ฐฐ์นํฉ๋๋ค.
let p1 = createVector(W / 2, 50); // ์๋จ ๊ผญ์ง์
let p2 = createVector(50, H - 50); // ์ผ์ชฝ ํ๋จ ๊ผญ์ง์
let p3 = createVector(W - 50, H - 50); // ์ค๋ฅธ์ชฝ ํ๋จ ๊ผญ์ง์
// ์ฌ๊ท ํจ์๋ฅผ ํธ์ถํ์ฌ ์์๋ฅดํ์คํค ๊ฐ์คํท์ ๊ทธ๋ฆฝ๋๋ค.
sierpinski(p1, p2, p3, 0);
// ์ ์ ์ธ ๊ฒฐ๊ณผ๋ฌผ์ด๋ฏ๋ก noLoop()๋ฅผ ์ฌ์ฉํฉ๋๋ค.
noLoop();
}
/**
* ์์๋ฅดํ์คํค ๊ฐ์คํท์ ์ฌ๊ท์ ์ผ๋ก ์์ฑํ๋ ํจ์์
๋๋ค.
* @param {p5.Vector} v1 - ์ผ๊ฐํ์ ์ฒซ ๋ฒ์งธ ๊ผญ์ง์
* @param {p5.Vector} v2 - ์ผ๊ฐํ์ ๋ ๋ฒ์งธ ๊ผญ์ง์
* @param {p5.Vector} v3 - ์ผ๊ฐํ์ ์ธ ๋ฒ์งธ ๊ผญ์ง์
* @param {number} depth - ํ์ฌ ์ฌ๊ท ๊น์ด
*/
function sierpinski(v1, v2, v3, depth) {
// 1. **์ฌ๊ท ํ์ถ ์กฐ๊ฑด (Base Case)**:
// ์ฌ๊ท ๊น์ด๊ฐ 'maxDepth'์ ๋๋ฌํ๋ฉด ๋ ์ด์ ๋ถํ ํ์ง ์๊ณ ํ์ฌ ์ผ๊ฐํ์ ๊ทธ๋ฆฝ๋๋ค.
if (depth >= maxDepth) {
// ์ธ ๊ผญ์ง์ ์ ์๋ ์ผ๊ฐํ์ ๊ทธ๋ฆฝ๋๋ค.
triangle(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y);
return;
}
// 2. **์๋ก์ด ๊ผญ์ง์ (์ค๊ฐ์ ) ๊ณ์ฐ**:
// ํ์ฌ ์ผ๊ฐํ์ ๊ฐ ๋ณ์ ์ค์ ์ ๊ณ์ฐํฉ๋๋ค.
// ๋ฒกํฐ์ ํฉ์ 2๋ก ๋๋๋ฉด (v1 + v2) / 2 ๊ฐ ๋์ด ์ค์ ์ ์ฝ๊ฒ ๊ตฌํ ์ ์์ต๋๋ค.
// v1๊ณผ v2์ ์ค์ (m12)
let m12 = p5.Vector.add(v1, v2).mult(0.5);
// v2์ v3์ ์ค์ (m23)
let m23 = p5.Vector.add(v2, v3).mult(0.5);
// v3๊ณผ v1์ ์ค์ (m31)
let m31 = p5.Vector.add(v3, v1).mult(0.5);
// 3. **์ฌ๊ท ํธ์ถ (Recursive Step)**:
// ์๋ ์ผ๊ฐํ์ ์ธ ๊ผญ์ง์ ๊ณผ ์๋ก ๊ณ์ฐํ ์ธ ์ค์ ์ ์ฌ์ฉํ์ฌ 3๊ฐ์ ์์ '๋ถ๋ถ ์ผ๊ฐํ'์ ๋ํด ์ฌ๊ท ํธ์ถํฉ๋๋ค.
let nextDepth = depth + 1;
// 1. ์๋จ ์์ ์ผ๊ฐํ: (v1, m12, m31)
sierpinski(v1, m12, m31, nextDepth);
// 2. ์ผ์ชฝ ํ๋จ ์์ ์ผ๊ฐํ: (m12, v2, m23)
sierpinski(m12, v2, m23, nextDepth);
// 3. ์ค๋ฅธ์ชฝ ํ๋จ ์์ ์ผ๊ฐํ: (m31, m23, v3)
sierpinski(m31, m23, v3, nextDepth);
// (์ฐธ๊ณ : ์ค์์ ์ญ์ผ๊ฐํ (m12, m23, m31)์ ๊ทธ๋ฆฌ์ง ์์์ผ๋ก์จ '๋น ๊ณต๊ฐ'์ ๋ง๋ญ๋๋ค.)
}
let axiom = "F";
let sentence = axiom;
let rules = [];
let len = 100;
function setup() {
createCanvas(800, 600);
angleMode(DEGREES);
rules.push({
a: "F",
b: "FF+[+F-F-F]-[-F+F+F]"
});
background(255);
stroke(0, 100);
turtle();
noLoop();
}
// L-System ๋ฌธ์์ด ํ์ฅ ํจ์
function generate() {
let nextSentence = "";
for (let i = 0; i < sentence.length; i++) {
let current = sentence.charAt(i);
let found = false;
for (let j = 0; j < rules.length; j++) {
if (current === rules[j].a) {
nextSentence += rules[j].b;
found = true;
break;
}
}
if (!found) {
nextSentence += current;
}
}
sentence = nextSentence;
len *= 0.5; // ์ธ๋๋ง๋ค ๊ฐ์ง ๊ธธ์ด ์ ๋ฐ
turtle();
}
// ๋ฌธ์์ด์ ํด์ํ์ฌ ๊ทธ๋ฆผ์ ๊ทธ๋ฆผ
function turtle() {
resetMatrix();
translate(width / 2, height);
stroke(0, 80);
for (let i = 0; i < sentence.length; i++) {
let current = sentence.charAt(i);
if (current === "F") {
line(0, 0, 0, -len);
translate(0, -len);
} else if (current === "+") {
rotate(25);
} else if (current === "-") {
rotate(-25);
} else if (current === "[") {
push();
} else if (current === "]") {
pop();
}
}
}
// ํค๋ฅผ ๋๋ฌ ์ธ๋ ํ์ฅ
function keyPressed() {
if (key === " ") {
background(255);
generate();
}
}
๐ฑ ์๋ ๋ฐฉ์
axiom (์์ ๋ฌธ์์ด) = "F"
๊ท์น:
generate() ํจ์๋ ์คํ์ด์ค๋ฐ๋ฅผ ๋๋ฅผ ๋๋ง๋ค
ํ ์ธ๋์ฉ ์ฑ์ฅํ๋๋ก ๋ง๋ฆ ๐ฟ
len์ ์ธ๋๋ง๋ค ์ ๋ฐ์ผ๋ก ์ค์ฌ์
๋๋ฌด๊ฐ ์ ์ ์์์ง๊ณ ๊ฐ์ง๊ฐ ์ธ๋ฐํด์ง
๋ค๋ฅธ L-system ๋ณํ(์: Koch curve, Dragon curve, Plant-like system)๋ ์์.
let axiom = "F";
let sentence = axiom;
let rules = [{ a: "F", b: "F+FโFโF+F" }];
let len = 200;
function setup() {
createCanvas(800, 300);
background(255);
stroke(0);
noFill();
angleMode(DEGREES);
turtle();
noLoop();
}
// ๋ฌธ์์ด ๋ณํ (ํ ์ธ๋์ฉ ํ์ฅ)
function generate() {
let nextSentence = "";
for (let c of sentence) {
let rule = rules.find(r => r.a === c);
nextSentence += rule ? rule.b : c;
}
sentence = nextSentence;
len *= 0.5;
turtle();
}
// ๋ฌธ์์ด์ ๊ทธ๋ฆผ์ผ๋ก ํด์
function turtle() {
resetMatrix();
translate(50, height / 2 + 50);
for (let c of sentence) {
if (c === "F") {
line(0, 0, len, 0);
translate(len, 0);
} else if (c === "+") {
rotate(90);
} else if (c === "โ" || c === "-") {
rotate(-90);
}
}
}
// ์คํ์ด์ค๋ฐ๋ก ๋ค์ ์ธ๋ ์์ฑ
function keyPressed() {
if (key === " ") {
background(255);
generate();
}
}
๐ ํฌ์ธํธ
let axiom = "FX";
let sentence = axiom;
let rules = [
{ a: "X", b: "X+YF+" },
{ a: "Y", b: "-FX-Y" }
];
let len = 10;
let generation = 0;
function setup() {
createCanvas(800, 600);
background(255);
stroke(0);
angleMode(DEGREES);
noFill();
turtle();
noLoop();
}
function generate() {
let next = "";
for (let c of sentence) {
let rule = rules.find(r => r.a === c);
next += rule ? rule.b : c;
}
sentence = next;
generation++;
len *= 0.7;
turtle();
}
function turtle() {
resetMatrix();
translate(width / 2, height / 2);
for (let c of sentence) {
if (c === "F") {
line(0, 0, len, 0);
translate(len, 0);
} else if (c === "+") {
rotate(90);
} else if (c === "-") {
rotate(-90);
}
}
}
// ์คํ์ด์ค๋ฐ๋ก ํ์ฅ
function keyPressed() {
if (key === " ") {
background(255);
generate();
}
}
๐ ํฌ์ธํธ
let axiom = "X";
let sentence = axiom;
let rules = [
{ a: "X", b: "F+[[X]-X]-F[-FX]+X" },
{ a: "F", b: "FF" }
];
let len = 100;
let angle = 25;
function setup() {
createCanvas(800, 600);
background(255);
stroke(50, 100, 30);
angleMode(DEGREES);
turtle();
noLoop();
}
function generate() {
let next = "";
for (let c of sentence) {
let rule = rules.find(r => r.a === c);
next += rule ? rule.b : c;
}
sentence = next;
len *= 0.5;
turtle();
}
function turtle() {
resetMatrix();
translate(width / 2, height);
for (let c of sentence) {
if (c === "F") {
line(0, 0, 0, -len);
translate(0, -len);
} else if (c === "+") {
rotate(angle);
} else if (c === "-") {
rotate(-angle);
} else if (c === "[") {
push();
} else if (c === "]") {
pop();
}
}
}
function keyPressed() {
if (key === " ") {
background(255);
generate();
}
}
๐ ํฌ์ธํธ
let axiom = "X";
let sentence = axiom;
let len = 100;
let angle = 25;
let rules = [
{ a: "X", b: "F+[[X]-X]-F[-FX]+X", chance: 0.4 },
{ a: "X", b: "F-[[X]+X]+F[+FX]-X", chance: 0.4 },
{ a: "X", b: "F+[FX]-X", chance: 0.2 },
{ a: "F", b: "FF", chance: 1.0 }
];
function setup() {
createCanvas(800, 600);
background(255);
stroke(50, 80, 30);
angleMode(DEGREES);
noFill();
turtle();
noLoop();
}
// ๋ค์ ์ธ๋๋ก ๋ฐ์ ์ํค๋ ํจ์
function generate() {
let next = "";
for (let c of sentence) {
let matched = rules.filter(r => r.a === c);
if (matched.length > 0) {
// ํ๋ฅ ๊ธฐ๋ฐ ์ ํ
let rnd = random();
let acc = 0;
for (let r of matched) {
acc += r.chance;
if (rnd < acc) {
next += r.b;
break;
}
}
} else {
next += c;
}
}
sentence = next;
len *= 0.55;
turtle();
}
// ๋ฌธ์์ด์ ํด์ํด ๊ทธ๋ฆผ์ผ๋ก ๋ณํ
function turtle() {
resetMatrix();
translate(width / 2, height);
for (let c of sentence) {
if (c === "F") {
line(0, 0, 0, -len);
translate(0, -len);
} else if (c === "+") {
rotate(angle);
} else if (c === "-") {
rotate(-angle);
} else if (c === "[") {
push();
} else if (c === "]") {
pop();
}
}
}
// ์คํ์ด์ค๋ฐ๋ก ๋ค์ ์ธ๋
function keyPressed() {
if (key === " ") {
background(255);
generate();
}
}
๐ฑ ์๋ ์๋ฆฌ
๐ ๊ท์น ์ค๋ช
| a | b | chance | ์ค๋ช |
|---|---|---|---|
| X | F+[[X]-X]-F[-FX]+X | 0.4 | ์์ชฝ์ผ๋ก ๋๋ฌดํ ํ์ฅ |
| X | F-[[X]+X]+F[+FX]-X | 0.4 | ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ๊ฐ์ง ํ์ฅ |
| X | F+[FX]-X | 0.2 | ๋จ์ ์งง์ ๊ฐ์ง |
| F | FF | 1.0 | ๋ชจ๋ F๋ ๋ ๋ฐฐ๋ก ์ฑ์ฅ |