๐ŸŽ“8์ฃผ์ฐจ: ํ”„๋ž™ํƒˆ & ์ž๊ธฐ์œ ์‚ฌ์„ฑ (Fractal & Self-Similarity)

BamgasiJMยท2025๋…„ 11์›” 6์ผ

p5.js Art

๋ชฉ๋ก ๋ณด๊ธฐ
19/67
post-thumbnail

ํ”„๋ž™ํƒˆ์€ ์ž๊ธฐ ์œ ์‚ฌ์„ฑ(Self-Similarity)์„ ๊ฐ€์ง€๋Š” ๊ธฐํ•˜ํ•™์  ํ˜•ํƒœ๋กœ, ์•„๋ฌด๋ฆฌ ํ™•๋Œ€ํ•ด๋„ ์ „์ฒด์™€ ๋น„์Šทํ•œ ๋ชจ์–‘์ด ๋ฐ˜๋ณต๋˜๋Š” ํŠน์ง•์„ ๊ฐ–์Šต๋‹ˆ๋‹ค.
์•ž์„œ ๋‹ค๋ฃจ์–ด๋ณธ ์žฌ๊ท€(Recursion)์€ ํ”„๋ž™ํƒˆ์„ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์  ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.
์ด๋ฒˆ์— ๋‹ค๋ฃจ์–ด๋ณผ ํ”„๋ž™ํƒˆ์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ๐ŸŒฒ ์žฌ๊ท€์  ํ”„๋ž™ํƒˆ (Recursive Fractals)

์žฌ๊ท€ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹œ์ž‘ ๋„ํ˜•๊ณผ ๋ช…ํ™•ํ•œ ๋ณ€ํ˜• ๊ทœ์น™์„ ๋ฐ˜๋ณต์ ์œผ๋กœ ์ ์šฉํ•˜๋ฉฐ ํ™•์žฅํ•˜๋Š” ํ”„๋ž™ํƒˆ์ž…๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์€ Geometry์™€ Transformations ๊ฐœ๋…์„ ์ตํžˆ๋Š” ๋ฐ ๊ฐ€์žฅ ์ข‹์Šต๋‹ˆ๋‹ค.

  • ์‹œ์–ดํ•€์Šคํ‚ค ์‚ผ๊ฐํ˜• (Sierpinski Triangle) ์‚ผ๊ฐํ˜•์„ ์„ธ ๊ฐœ์˜ ์ž‘์€ ์‚ผ๊ฐํ˜•์œผ๋กœ ๋‚˜๋ˆ„๊ณ  ๊ฐ€์šด๋ฐ ์‚ผ๊ฐํ˜•์„ ์ œ๊ฑฐํ•˜๋Š” ์žฌ๊ท€.
  • ์‹œ์–ดํ•€์Šคํ‚ค ์นดํŽซ (Sierpinski Carpet) ์ •์‚ฌ๊ฐํ˜•์„ 9๊ฐœ์˜ ์ž‘์€ ์ •์‚ฌ๊ฐํ˜•์œผ๋กœ ๋‚˜๋ˆ„๊ณ  ๊ฐ€์šด๋ฐ ์ •์‚ฌ๊ฐํ˜•์„ ์ œ๊ฑฐํ•˜๋Š” 2D ์žฌ๊ท€.
  • ์ฝ”ํ ๊ณก์„  (Koch Curve) ์„ ๋ถ„์„ 4๊ฐœ์˜ ์ž‘์€ ์„ ๋ถ„์œผ๋กœ ๋Œ€์ฒดํ•˜๋ฉฐ ๊ฐ๋„๋ฅผ ๊บพ๋Š” ์žฌ๊ท€. (์ฝ”ํ ๋ˆˆ์†ก์ด์˜ ๊ธฐ๋ณธ ์š”์†Œ)
  • ์žฌ๊ท€ ๋‚˜๋ฌด/๊ฐ€์ง€ (Recursive Tree/Branch) ์„ ๋ถ„์„ ๊ทธ๋ฆฐ ํ›„ ๋์ ์—์„œ ํšŒ์ „๊ณผ ์ถ•์†Œ๋ฅผ ์ ์šฉํ•˜์—ฌ 2๊ฐœ ์ด์ƒ์˜ ๊ฐ€์ง€๋ฅผ ์น˜๋Š” ์žฌ๊ท€.
  • ํ”ผํƒ€๊ณ ๋ผ์Šค ๋‚˜๋ฌด (Pythagoras Tree) ์ง๊ฐ์‚ผ๊ฐํ˜•๊ณผ ์ •์‚ฌ๊ฐํ˜•์„ ํ™œ์šฉํ•˜์—ฌ ๋‚˜๋ฌด ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“œ๋Š” ๊ธฐํ•˜ํ•™์  ์žฌ๊ท€.
  • ๋งน๊ฑฐ ์ŠคํŽ€์ง€ (Menger Sponge) 3์ฐจ์› ํ๋ธŒ๋ฅผ 27๊ฐœ๋กœ ๋‚˜๋ˆ„๊ณ  ๊ฐ€์šด๋ฐ 7๊ฐœ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” 3D ์žฌ๊ท€.

2. ๐ŸŒŒ ๋ฐ˜๋ณต ํ”„๋ž™ํƒˆ (Iterative Fractals)

๋ฐ˜๋ณต(Iteration) ๊ณ„์‚ฐ, ์ฃผ๋กœ ๋ณต์†Œ์ˆ˜ ๊ณ„์‚ฐ ๋˜๋Š” ๋ฐ˜๋ณต์ ์ธ ํ”ฝ์…€ ๋งคํ•‘์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์กฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ”„๋ž™ํƒˆ์ž…๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์€ ์ˆ˜ํ•™์  ๊นŠ์ด์™€ ์ƒ‰์ƒ ๋งคํ•‘(Color Mapping) ๊ธฐ์ˆ ์„ ์ตํžˆ๋Š” ๋ฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

  • ๋งŒ๋ธ๋ธŒ๋กœํŠธ ์ง‘ํ•ฉ (Mandelbrot Set)zn+1=zn2+cz_{n+1} = z_n^2 + c ๊ณต์‹์„ ๋ฐ˜๋ณตํ•˜์—ฌ, ์ˆ˜๋ ด ์—ฌ๋ถ€๋ฅผ ํ”ฝ์…€์˜ ์ƒ‰์ƒ์œผ๋กœ ํ‘œํ˜„. (๋ณต์†Œ์ˆ˜ ๋ฐ˜๋ณต)
  • ์œจ๋ฆฌ์•„ ์ง‘ํ•ฉ (Julia Set)๋งŒ๋ธ๋ธŒ๋กœํŠธ์™€ ์œ ์‚ฌํ•˜๋‚˜, ์ดˆ๊ธฐ ์ƒ์ˆ˜ cc๋ฅผ ๊ณ ์ •ํ•˜๊ณ  ์ดˆ๊ธฐ ๊ฐ’ z0z_0์„ ํ”ฝ์…€ ์œ„์น˜๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ˜๋ณต.
  • ํ”„๋ž™ํƒˆ ๋ถˆ๊ฝƒ (Fractal Flames)๋ฐ˜๋ณต ํ•จ์ˆ˜ ์‹œ์Šคํ…œ(IFS)์˜ ํ™•๋ฅ ์  ๊ตฌํ˜„์„ ํ†ตํ•ด ๋ณต์žกํ•˜๊ณ  ์œ ๊ธฐ์ ์ธ ๋ชจ์–‘์„ ์ƒ์„ฑ.
  • IFS ํ”„๋ž™ํƒˆ (Iterated Function Systems)์—ฌ๋Ÿฌ ๊ฐœ์˜ ์•„ํ•€ ๋ณ€ํ™˜(์ถ•์†Œ, ํšŒ์ „, ์ด๋™) ํ•จ์ˆ˜๋ฅผ ๋ฐ˜๋ณต์ ์œผ๋กœ ์ ์šฉํ•˜์—ฌ ๊ตฌ์กฐ๋ฅผ ์ƒ์„ฑ. (์˜ˆ: ์–‘์น˜ ์‹๋ฌผ ํ”„๋ž™ํƒˆ)

3. ๐ŸŒฟ L-์‹œ์Šคํ…œ ํ”„๋ž™ํƒˆ (L-System Fractals)

๋ฆฐ๋ด๋งˆ์ด์–ด ์‹œ์Šคํ…œ(Lindenmayer System)์ด๋ผ๋Š” ํ˜•์‹ ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์ž์—ด์„ ํ™•์žฅํ•˜๊ณ , ์ด ๋ฌธ์ž์—ด์„ ํ„ฐํ‹€ ๊ทธ๋ž˜ํ”ฝ์Šค(Turtle Graphics)๋กœ ํ•ด์„ํ•˜์—ฌ ์‹œ๊ฐํ™”ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ๊ทœ์น™ ๊ธฐ๋ฐ˜ ์ƒ์„ฑ๊ณผ ํŒŒ์‹ฑ ๋กœ์ง์„ ์ตํžˆ๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ๊ฒฐ์ •์  L ์‹œ์Šคํ…œ (Deterministic L-System)๊ณ ์ •๋œ ๊ทœ์น™(์˜ˆ: Fโ†’F+Fโˆ’FF \to F+F-F)์„ ๋ฐ˜๋ณตํ•˜์—ฌ ๋ฌธ์ž์—ด์„ ํ™•์žฅ. (์˜ˆ: ์ฝ”ํ ๊ณก์„ ์˜ L-System ๋ฒ„์ „, ๋‚˜๋ฌด/์‹๋ฌผ ๊ตฌ์กฐ์˜ ๊ธฐ๋ณธ)
  • ํ™•๋ฅ ์  L ์‹œ์Šคํ…œ (Stochastic L-System)๊ทœ์น™์— ํ™•๋ฅ ์„ ๋ถ€์—ฌํ•˜์—ฌ ๋ณด๋‹ค ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ๋ณ€์น™์ ์ธ ์‹๋ฌผ ํŒจํ„ด์„ ์ƒ์„ฑ.
  • ๋“œ๋ž˜๊ณค ๊ณก์„  (Dragon Curve)X,YX, Y ๋‘ ๊ฐ€์ง€ ๋ณ€์ˆ˜ ๊ทœ์น™๊ณผ 90โˆ˜90^\circ ํšŒ์ „์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณต์žกํ•œ ๋‚˜์„ ํ˜• ๊ตฌ์กฐ ์ƒ์„ฑ.
  • ํž๋ฒ ๋ฅดํŠธ ๊ณก์„  (Hilbert Curve)ํ‰๋ฉด์„ ์ฑ„์šฐ๋Š” ๊ณต๊ฐ„ ์ฑ„์›€ ๊ณก์„ (Space-Filling Curve) ์ค‘ ํ•˜๋‚˜๋กœ, L-์‹œ์Šคํ…œ์œผ๋กœ ๊น”๋”ํ•˜๊ฒŒ ํ‘œํ˜„ ๊ฐ€๋Šฅ.

์ด ์ค‘์—์„œ ๋ช‡ ๊ฐ€์ง€๋งŒ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


์˜ˆ์‹œ ์ฝ”๋“œ

์˜ˆ์ œ A: Mandelbrot ์ง‘ํ•ฉ ์‹œ๊ฐํ™”

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): ํ”ฝ์…€ ์œ„์น˜ โ†’\to ๋ณต์†Œ์ˆ˜ cc์˜ ์‹ค์ˆ˜๋ถ€(caca) ๋ฐ ํ—ˆ์ˆ˜๋ถ€(cbcb)๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฒ”์œ„๊ฐ€ ๋งŒ๋ธ๋ธŒ๋กœํŠธ ์ง‘ํ•ฉ์˜ ์ฃผ๋œ ์‹ฌ์žฅํ˜• ์˜์—ญ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
  • while (n < maxIter): ๋งŒ๋ธ๋ธŒ๋กœํŠธ ๊ณต์‹ zn+1=zn2+cz_{n+1} = z_n^2 + c ์„ ๋ฐ˜๋ณตํ•˜์—ฌ ๋ณต์†Œ์ˆ˜ zz๊ฐ€ ๋ฌดํ•œ๋Œ€๋กœ ๋ฐœ์‚ฐํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋” ๋†’์€ ์„ฑ๋Šฅ์„ ์œ„ํ•ด์„œ๋Š” ๊ณ„์‚ฐ์„ GPU์—์„œ ํ•˜๋Š” WebGL/GLSL ์…ฐ์ด๋” ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.(createShader ์‚ฌ์šฉ)

๐Ÿง  ์ˆ˜ํ•™์ /์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ถ„์„
A. ๋งŒ๋ธ๋ธŒ๋กœํŠธ ๊ณต์‹ (zn+1=zn2+cz_{n+1} = z_n^2 + c)

  • zz: ๋ฐ˜๋ณต๋˜๋Š” ๋ณต์†Œ์ˆ˜ (์ดˆ๊ธฐ z0=0+0iz_0 = 0 + 0i).
  • cc: ์บ”๋ฒ„์Šค ์ขŒํ‘œ์— ์˜ํ•ด ๊ฒฐ์ •๋œ ๊ณ ์ •๋œ ๋ณต์†Œ์ˆ˜ (ํ”ฝ์…€๋งˆ๋‹ค ๋‹ค๋ฆ„).
  • ๋ฐœ์‚ฐ ์กฐ๊ฑด: ๋งŒ์•ฝ โˆฃzโˆฃ|z| (๋ณต์†Œ์ˆ˜์˜ ํฌ๊ธฐ)๊ฐ€ 2๋ณด๋‹ค ์ปค์ง€๋ฉด, ๊ทธ ์ดํ›„๋กœ๋Š” โˆฃzโˆฃ|z|๊ฐ€ ๋ฌดํ•œ๋Œ€๋กœ ๋ฐœ์‚ฐํ•จ์ด ์ˆ˜ํ•™์ ์œผ๋กœ ์ฆ๋ช…๋ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ์—์„œ๋Š” โˆฃzโˆฃ2>4|z|^2 > 4๋ณด๋‹ค ํฐ a a + b b > 16 (์•ˆ์ „ ๋งˆ์ง„)์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐœ์‚ฐ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค.

B. ํ”„๋ž™ํƒˆ ์‹œ๊ฐํ™” ์›๋ฆฌ

  • ๋งŒ๋ธ๋ธŒ๋กœํŠธ ์ง‘ํ•ฉ์˜ ์ •์˜: ์ด ๋ฐ˜๋ณต ๊ณ„์‚ฐ์—์„œ zz๊ฐ€ ๋ฌดํ•œ๋Œ€๋กœ ๋ฐœ์‚ฐํ•˜์ง€ ์•Š๊ณ  ์œ ํ•œํ•œ ๊ฐ’์œผ๋กœ ์ˆ˜๋ ดํ•˜๊ฑฐ๋‚˜ ๋จธ๋ฌด๋ฅด๋Š” cc ๊ฐ’๋“ค์˜ ์ง‘ํ•ฉ์ž…๋‹ˆ๋‹ค.
  • ์ƒ‰์ƒ ๋ถ€์—ฌ: ๋ฐœ์‚ฐ์— ์˜ค๋ž˜ ๊ฑธ๋ฆด์ˆ˜๋ก (n์ด ํด์ˆ˜๋ก) ์ง‘ํ•ฉ ๊ฒฝ๊ณ„์— ๊ฐ€๊น๋‹ค๊ณ  ๋ณด๊ณ  ๋ฐ์€ ์ƒ‰์„ ๋ถ€์—ฌํ•˜๋ฉฐ, ๋ฐœ์‚ฐํ•˜์ง€ ์•Š์€ ์˜์—ญ (์ง‘ํ•ฉ ๋‚ด๋ถ€)์€ ๋‹จ์ƒ‰(์–ด๋‘์šด ๋ฐฐ๊ฒฝ์ƒ‰)์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ๊ฒฝ๊ณ„๋ฅผ ๊ฐ•์กฐํ•ฉ๋‹ˆ๋‹ค.

๐ŸŒŸ ์ปฌ๋Ÿฌ ๊ตฌํ˜„ ๋กœ์ง

  • colorMode(RGB, 255): RGB ๋ชจ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง๊ด€์ ์œผ๋กœ ์›ํ•˜๋Š” ์ƒ‰์ƒ ๊ฐ’์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ๊ธฐ ๋งคํ•‘: ๋ฐœ์‚ฐ ํšŸ์ˆ˜ n์„ ๋ฐ๊ธฐ ๊ณ„์ˆ˜(brightnessFactor)๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ง‘ํ•ฉ ์™ธ๊ณฝ์˜ ์ƒ‰์กฐ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ฐ˜๋ณต ํšŸ์ˆ˜์— ๋”ฐ๋ฅธ ์ธต์œ„๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ํ‘œํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • n์ด ์ž‘์„์ˆ˜๋ก โ†’ brightnessFactor๊ฐ€ ์ž‘์Œ โ†’ ์–ด๋‘์šด ๋ฏผํŠธ์ƒ‰
    • n์ด ํด์ˆ˜๋ก โ†’ brightnessFactor๊ฐ€ ํผ โ†’ ๋ฐ์€ ๋ฏผํŠธ์ƒ‰

์˜ˆ์ œ B: ์ฝ”ํ ๊ณก์„  (Koch Curve)

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() ํ˜ธ์ถœ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ •์‚ผ๊ฐํ˜•์˜ ์„ธ ๋ณ€์„ ํ”„๋ž™ํƒˆํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ œ C: ์‹œ์—๋ฅดํ•€์Šคํ‚ค ๊ฐœ์Šคํ‚ท (Sierpinski Gasket)

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)์€ ๊ทธ๋ฆฌ์ง€ ์•Š์Œ์œผ๋กœ์จ '๋นˆ ๊ณต๊ฐ„'์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.)
}

5๏ธโƒฃ Deterministic(๊ฒฐ์ •์ ) L-System

5๏ธโƒฃ-1๏ธโƒฃ "๋‚˜๋ฌดํ˜•(Tree-like)โ€ L-System

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();
  }
}

๐ŸŒฑ ์ž‘๋™ ๋ฐฉ์‹

  1. axiom (์‹œ์ž‘ ๋ฌธ์ž์—ด) = "F"

  2. ๊ทœ์น™:

  • "F" โ†’ "FF+[+F-F-F]-[-F+F+F]"
  • ์ฆ‰, ๊ฐ€์ง€๊ฐ€ ๊ฐˆ๋ผ์ง€๋ฉฐ + / - ๋กœ ํšŒ์ „
  1. generate() ํ•จ์ˆ˜๋Š” ์ŠคํŽ˜์ด์Šค๋ฐ”๋ฅผ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค
    ํ•œ ์„ธ๋Œ€์”ฉ ์„ฑ์žฅํ•˜๋„๋ก ๋งŒ๋“ฆ ๐ŸŒฟ

  2. len์„ ์„ธ๋Œ€๋งˆ๋‹ค ์ ˆ๋ฐ˜์œผ๋กœ ์ค„์—ฌ์„œ
    ๋‚˜๋ฌด๊ฐ€ ์ ์  ์ž‘์•„์ง€๊ณ  ๊ฐ€์ง€๊ฐ€ ์„ธ๋ฐ€ํ•ด์ง

๋‹ค๋ฅธ L-system ๋ณ€ํ˜•(์˜ˆ: Koch curve, Dragon curve, Plant-like system)๋„ ์žˆ์Œ.

5๏ธโƒฃ-2๏ธโƒฃ Koch Curve (์ฝ”ํ ๊ณก์„  / ๋ˆˆ์†ก์ด ๊ธฐ๋ณธ ๊ตฌ์กฐ)

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();
  }
}

๐Ÿ“˜ ํฌ์ธํŠธ

  • "F โ†’ F+Fโˆ’Fโˆ’F+F"
  • 90๋„ ๊ฐ๋„๋กœ ๋ฐ˜๋ณต โ†’ ์ง์‚ฌ๊ฐํ˜• ๋ชจ๋“ˆ ๊ตฌ์กฐ
  • ์ŠคํŽ˜์ด์Šค๋ฐ”๋กœ ์„ธ๋Œ€๋ฅผ ํ™•์žฅํ•˜๋ฉฐ ์ ์  ๋ณต์žกํ•ด์ง€๋Š” ์„  ๊ตฌ์กฐ ํ™•์ธ ๊ฐ€๋Šฅ

5๏ธโƒฃ-3๏ธโƒฃ Dragon Curve (๋“œ๋ž˜๊ณค ๊ณก์„ )

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();
  }
}

๐Ÿ“˜ ํฌ์ธํŠธ

  • "X โ†’ X+YF+", "Y โ†’ -FX-Y"
  • ์ŠคํŽ˜์ด์Šค๋ฐ”๋กœ ๋‹จ๊ณ„ ํ™•์žฅ ์‹œ ์ž๊ธฐ์œ ์‚ฌ ๊ณก์„  ํŒจํ„ด์ด ๋“œ๋Ÿฌ๋‚จ
  • ํ•œ์ชฝ์œผ๋กœ ๊ฐ๊ธฐ๋ฉฐ โ€œ๋“œ๋ž˜๊ณค ์ŠคํŒŒ์ด๋Ÿดโ€ ๊ตฌ์กฐ ์ƒ์„ฑ

5๏ธโƒฃ-4๏ธโƒฃ Plant-like L-System (์ž์—ฐ์Šค๋Ÿฌ์šด ์‹๋ฌผ ํŒจํ„ด)

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();
  }
}

๐Ÿ“˜ ํฌ์ธํŠธ

  • "X โ†’ F+[[X]-X]-F[-FX]+X", "F โ†’ FF"
  • ๊ฐ€์ง€(branch)๋ฅผ push/pop์œผ๋กœ ์ €์žฅ โ†’ ํŠธ๋ฆฌ ๊ตฌ์กฐ ์„ฑ์žฅ
  • ์„ธ๋Œ€๊ฐ€ ๋Š˜์ˆ˜๋ก ๋ณต์žกํ•˜๊ณ  ์œ ๊ธฐ์ ์ธ ์‹๋ฌผ ํ˜•ํƒœ
  • angle ๊ฐ’์„ ๋ฐ”๊พธ๋ฉด ํ˜•ํƒœ๊ฐ€ ๋‹ค์–‘ํ•˜๊ฒŒ ๋ณ€ํ•จ ๐ŸŒฟ

6๏ธโƒฃ Stochastic (ํ™•๋ฅ ์ ) L-System

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();
  }
}

๐ŸŒฑ ์ž‘๋™ ์›๋ฆฌ

  • rules ๋ฐฐ์—ด์— ๋™์ผํ•œ a: "X"์— ๋Œ€ํ•ด ์—ฌ๋Ÿฌ ๊ฐœ์˜ b ํŒจํ„ด์ด ์กด์žฌํ•จ
  • ๊ฐ ํŒจํ„ด์—๋Š” chance๊ฐ€ ์ง€์ •๋˜์–ด ์žˆ์–ด
    ๋žœ๋ค(random) ๊ฐ’์— ๋”ฐ๋ผ ๊ทœ์น™์ด ๋‹ค๋ฅด๊ฒŒ ์ ์šฉ๋จ
  • ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋งค๋ฒˆ ๋‹ค๋ฅธ ํ˜•ํƒœ์˜ ๊ฐ€์ง€ ๊ตฌ์กฐ๊ฐ€ ๋งŒ๋“ค์–ด์ง
  • ์ŠคํŽ˜์ด์Šค๋ฐ”๋ฅผ ๋ˆ„๋ฅด๋ฉด ๋‹ค์Œ ์„ธ๋Œ€๋กœ ์„ฑ์žฅ

๐Ÿ“Š ๊ทœ์น™ ์„ค๋ช…

abchance์„ค๋ช…
XF+[[X]-X]-F[-FX]+X0.4์œ„์ชฝ์œผ๋กœ ๋‚˜๋ฌดํ˜• ํ™•์žฅ
XF-[[X]+X]+F[+FX]-X0.4๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ€์ง€ ํ™•์žฅ
XF+[FX]-X0.2๋‹จ์ˆœ ์งง์€ ๊ฐ€์ง€
FFF1.0๋ชจ๋“  F๋Š” ๋‘ ๋ฐฐ๋กœ ์„ฑ์žฅ

profile
Coding Art with Blender / oF / Processing / p5.js / nannou

0๊ฐœ์˜ ๋Œ“๊ธ€