๐ŸŽ“ 7์ฃผ์ฐจ: ์žฌ๊ท€์™€ ํŒจํ„ด (Recursion & Pattern)

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

p5.js Art

๋ชฉ๋ก ๋ณด๊ธฐ
16/65
post-thumbnail

Generative Art ํ˜น์€ Coding Art์˜ ๊ด€์ ์—์„œ ์žฌ๊ท€(Recursion)๋Š” ํ”„๋ž™ํƒˆ์ด๋‚˜ ๋ณต์žกํ•œ ํŒจํ„ด, ์œ ๊ธฐ์ ์ด๊ณ  ์ž์—ฐ์ ์ธ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๋Š” ๋ฐ ์žˆ์–ด ํ•ต์‹ฌ์ ์ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ž…๋‹ˆ๋‹ค.

์žฌ๊ท€ ํ•จ์ˆ˜๋Š” ์ž๊ธฐ ์ž์‹ ์„ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜์ด๋ฉฐ, ๋ฐ˜๋“œ์‹œ ์ข…๋ฃŒ ์กฐ๊ฑด(Base Case)๋ฅผ ๊ฐ€์ ธ์•ผ ๋ฌดํ•œ ๋ฃจํ”„์— ๋น ์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

1. Recursion BASIC : ์„ ๋ถ„ ๋‚˜๋ˆ„๊ธฐ

๊ฐ€์žฅ ๊ธฐ๋ณธ์ด ๋˜๋Š” ์žฌ๊ท€ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ ์„ ๋ถ„์„ ๊ณ„์†ํ•ด์„œ ๋ฐ˜์œผ๋กœ ๋‚˜๋ˆ„๋ฉด์„œ ๊ทธ๋ฆฌ๋Š” ์ž‘์—…์„ ๋ฐ˜๋ณตํ•ฉ๋‹ˆ๋‹ค.

const MAX_LEVEL = 7;

function setup() {
  createCanvas(800, 450);
  noLoop();
  stroke(255, 200, 70);
  strokeWeight(1);
}

function draw() {
  background(20);

  // ์žฌ๊ท€ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ์ž‘
  // x1, y1, x2, y2, level
  divideLine(50, height / 2, width - 50, height / 2, 0);
}

/**
 * ์„ ๋ถ„์„ ์žฌ๊ท€์ ์œผ๋กœ ๋‚˜๋ˆ„๋Š” ํ•จ์ˆ˜
 * @param {number} x1 ์‹œ์ž‘์  x ์ขŒํ‘œ
 * @param {number} y1 ์‹œ์ž‘์  y ์ขŒํ‘œ
 * @param {number} x2 ๋์  x ์ขŒํ‘œ
 * @param {number} y2 ๋์  y ์ขŒํ‘œ
 * @param {number} level ํ˜„์žฌ ์žฌ๊ท€ ๊นŠ์ด
 */

function divideLine(x1, y1, x2, y2, level) {
  // 1. **์ข…๋ฃŒ ์กฐ๊ฑด (Base Case)**:
  // ์žฌ๊ท€ ๊นŠ์ด๊ฐ€ ์ตœ๋Œ€ ๋ ˆ๋ฒจ์— ๋„๋‹ฌํ•˜๋ฉด ํ•จ์ˆ˜๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.
  if (level > MAX_LEVEL) {
    return;
  }

  // ํ˜„์žฌ ๋ ˆ๋ฒจ์˜ ์„ ๋ถ„์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
  line(x1, y1, x2, y2);

  // ์„ ๋ถ„์˜ ์ค‘์  (Midpoint) ๊ณ„์‚ฐ
  let midX = (x1 + x2) / 2;
  let midY = (y1 + y2) / 2;

  // ์ค‘์ ์—์„œ ์ž‘์€ ์ˆ˜์ง์„ ์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. (์‹œ๊ฐ์  ๊ตฌ๋ถ„)
  let length = dist(x1, y1, x2, y2);
  line(midX, midY - length * 0.3, midX, midY + length * 0.3);

  // 2. **์žฌ๊ท€ ํ˜ธ์ถœ (Recursive Call)**:
  // ์ƒˆ๋กœ์šด ๋‘ ๊ฐœ์˜ ์„ ๋ถ„์— ๋Œ€ํ•ด ํ•จ์ˆ˜ ์ž์‹ ์„ ๋‹ค์‹œ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
  // ์™ผ์ชฝ ์„ ๋ถ„: (x1, y1) ~ (midX, midY)
  divideLine(x1, y1, midX, midY, level + 1);

  // ์˜ค๋ฅธ์ชฝ ์„ ๋ถ„: (midX, midY) ~ (x2, y2)
  divideLine(midX, midY, x2, y2, level + 1);
}
  • ํ•˜๋‚˜์˜ ๊ธด ์„ ๋ถ„ (x1,y1)โ†’(x2,y2)(x_1, y_1) \to (x_2, y_2)๋ฅผ ๊ทธ๋ฆฐ ํ›„, ์ค‘์ ์„ ์ฐพ์•„ ๋‘ ๊ฐœ์˜ ์ ˆ๋ฐ˜ ๊ธธ์ด ์„ ๋ถ„์œผ๋กœ ๋‚˜๋ˆ•๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์„ ๋ฏธ๋ฆฌ ์ •ํ•ด๋‘” MAX_LEVEL๊นŒ์ง€ ๋ฐ˜๋ณตํ•ฉ๋‹ˆ๋‹ค.

  • MAX_LEVEL: ์žฌ๊ท€๊ฐ€ ์–ผ๋งˆ๋‚˜ ๊นŠ๊ฒŒ ์ง„ํ–‰๋ ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ์ œํ•œ ์žฅ์น˜์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์—†์œผ๋ฉด ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  • divideLine(...): ์žฌ๊ท€ ์‹คํ–‰์„ ์œ„ํ•ด ์ž‘์„ฑํ•œ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. level + 1์„ ์ธ์ž๋กœ ์ „๋‹ฌํ•˜์—ฌ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ๊นŠ์ด๊ฐ€ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.


2. Recursive Tree

์žฌ๊ท€๋ฅผ ์ด์šฉํ•˜์—ฌ ์ž์—ฐ์˜ ํ”„๋ž™ํƒˆ ๊ตฌ์กฐ๋ฅผ ํ‰๋‚ด ๋‚ด๋Š” ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค. ์žฌ๊ท€ ํ˜ธ์ถœ๋งˆ๋‹ค ํšŒ์ „(Rotation)๊ณผ ์ถ•์†Œ(Scaling)๊ฐ€ ์ ์šฉ๋˜์–ด ๋ณต์žกํ•œ ๋ชจ์–‘์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

ํ•˜์–€ ๋ผ์ธ์œผ๋กœ๋งŒ ๋งŒ๋“  ๊ฐ„๋‹จํ•œ recursive tree์˜ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

function setup() {
  createCanvas(500, 300);
  noLoop();
  background(10);
  stroke(220);
  translate(width / 2, height);
  branch(100);
}

function branch(len) {
  line(0, 0, 0, -len);
  translate(0, -len);
  if (len > 8) {
    push();
    rotate(PI / 6);
    branch(len * 0.67);
    pop();

    push();
    rotate(-PI / 4);
    branch(len * 0.67);
    pop();
  }
}

์ด๊ฑด ์ค‘์•™/์™ผ/์˜ค๋ฅธ์ชฝ ๊ฐ€์ง€๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“œ๋Š” ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

// 2_recursive_tree_1
function setup() {
  createCanvas(600, 600);
  angleMode(DEGREES);
  noLoop();
}

function draw() {
  background(15);
  translate(width / 2, height);
  stroke(80, 150, 100);
  strokeWeight(2);
  drawBranch(140, 10);
}

function drawBranch(len, depth) {
  // base
  line(0, 0, 0, -len);
  translate(0, -len);

  if (depth <= 0) {
    // ์žŽ
    noStroke();
    fill(200, 200, 200);
    ellipse(0, 0, 5, 5);
    stroke(20);
    return;
  }

  // ์™ผ์ชฝ ๊ฐ€์ง€
  push();
  rotate(-20 - depth * 1.5);
  drawBranch(len * 0.7, depth - 1);
  pop();

  // ์˜ค๋ฅธ์ชฝ ๊ฐ€์ง€
  push();
  rotate(20 + depth * 1.5);
  drawBranch(len * 0.7, depth - 1);
  pop();

  // ์ค‘์•™ ๊ฐ€์ง€ : 0 ๋Œ€์‹  ๋‹ค๋ฅธ ๊ฐ’์„ ๋„ฃ์œผ๋ฉด ์‚ด์ง ๋ฐฉํ–ฅ ์ „ํ™˜
  push();
  rotate(map(sin(depth * 2), 0, 0, -5, 5));
  drawBranch(len * 0.66, depth - 1);
  pop();

  // ๋Œ์•„์˜ค๊ธฐ
  translate(0, len);
}

์กฐ๊ธˆ ๋” ๊พธ๋ฉฐ๋ณด๋ฉด ์ด๋ ‡๊ฒŒ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

// 2_recursive_tree_1
let branchLength = 100;

function setup() {
  createCanvas(800, 400);
  angleMode(DEGREES); // ๊ฐ๋„ ๋‹จ์œ„๋ฅผ degree๋กœ ์„ค์ •
  stroke(10, 180, 170, 50);
  strokeWeight(3);
}

function draw() {
  background(25);
  translate(width / 2, height); // ๊ทธ๋ฆผ์˜ ์›์ ์„ ์บ”๋ฒ„์Šค ํ•˜๋‹จ ์ค‘์•™์œผ๋กœ ์ด๋™

  // ์žฌ๊ท€ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ์ž‘: ์ดˆ๊ธฐ ๊ธธ์ด์™€ ๊นŠ์ด
  drawBranch(branchLength, 0);
}

/**
 * ๊ฐ€์ง€๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ๊ทธ๋ฆฌ๋Š” ํ•จ์ˆ˜
 * @param {number} len ํ˜„์žฌ ๊ฐ€์ง€์˜ ๊ธธ์ด
 * @param {number} level ํ˜„์žฌ ์žฌ๊ท€ ๊นŠ์ด (์„ ํƒ ์‚ฌํ•ญ์ด์ง€๋งŒ ๋ช…์‹œ์ ์œผ๋กœ ์ถ”๊ฐ€)
 */
function drawBranch(len, level) {
  // 1. ํ˜„์žฌ ๊ฐ€์ง€(์„ )๋ฅผ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. (์›์  (0,0)์—์„œ ์œ„๋กœ)
  line(0, 0, 0, -len);

  // ์ด ์‹œ์ ์—์„œ ์ขŒํ‘œ๊ณ„๋Š” ๊ฐ€์ง€์˜ ๋์  (-len)์œผ๋กœ ์ด๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  translate(0, -len);

  // 2. **์ข…๋ฃŒ ์กฐ๊ฑด (Base Case)**:
  // ๊ฐ€์ง€์˜ ๊ธธ์ด๊ฐ€ ๋„ˆ๋ฌด ์งง์•„์ง€๋ฉด (์˜ˆ: 3 ํ”ฝ์…€ ๋ฏธ๋งŒ) ์žฌ๊ท€๋ฅผ ๋ฉˆ์ถฅ๋‹ˆ๋‹ค.
  if (len < 3) {
    // ์žŽ์‚ฌ๊ท€์ฒ˜๋Ÿผ ์ž‘์€ ์ ์„ ์ฐ์–ด ๋งˆ๋ฌด๋ฆฌ
    stroke(50, 200, 50); // ๋…น์ƒ‰
    point(0, 0);
    return;
  }

  // 3. **์žฌ๊ท€ ํ˜ธ์ถœ (Recursive Call)**:

  // A. ์˜ค๋ฅธ์ชฝ ๊ฐ€์ง€
  push(); // ํ˜„์žฌ ์ขŒํ‘œ๊ณ„ ์ƒํƒœ(์œ„์น˜/ํšŒ์ „) ์ €์žฅ
  rotate(30); // 30๋„ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ํšŒ์ „
  drawBranch(len * 0.8, level + 1); // ๊ธธ์ด 80%๋กœ ์ค„์ด๊ณ  ์žฌ๊ท€ ํ˜ธ์ถœ
  pop(); // ์ €์žฅ๋œ ์ƒํƒœ๋กœ ๋ณต์›

  // B. ์™ผ์ชฝ ๊ฐ€์ง€
  push(); // ํ˜„์žฌ ์ขŒํ‘œ๊ณ„ ์ƒํƒœ ์ €์žฅ
  rotate(-55); // -65๋„ ์™ผ์ชฝ์œผ๋กœ ํšŒ์ „
  drawBranch(len * 0.6, level + 1); // ๊ธธ์ด 60%๋กœ ์ค„์ด๊ณ  ์žฌ๊ท€ ํ˜ธ์ถœ
  pop(); // ์ €์žฅ๋œ ์ƒํƒœ๋กœ ๋ณต์›
}
  • ํ•ต์‹ฌ ์ž‘๋™ ์›๋ฆฌ: ๊ฐ€์ง€๋ฅผ ๊ทธ๋ฆฐ ํ›„, p5.js์˜ ๋ณ€ํ™˜ ํ•จ์ˆ˜(translate, rotate, push/pop)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€์ง€์˜ ๋์ ์—์„œ ์ขŒํ‘œ๊ณ„๋ฅผ ํšŒ์ „์‹œํ‚ค๊ณ , ๋” ์งง์€ ๊ธธ์ด๋กœ ๋‘ ๋ฒˆ ์žฌ๊ท€ ํ˜ธ์ถœ์„ ํ•ฉ๋‹ˆ๋‹ค. push()์™€ pop()์€ ๋ณต์žกํ•œ ๋ณ€ํ™˜์ด ์ค‘์ฒฉ๋  ๋•Œ ์ขŒํ‘œ๊ณ„๋ฅผ ์ด์ „ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฌ๋Š” ๋ฐ ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

  • ์ฃผ์š” ๋ณ€์ˆ˜/ํ•จ์ˆ˜:

    • angleMode(DEGREES): ๊ฐ๋„๋ฅผ ๋‹ค๋ฃจ๊ธฐ ์‰ฝ๊ฒŒ ๋„(Degree) ๋‹จ์œ„๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    • translate(width/2, height): ๊ทธ๋ฆผ ์‹œ์ž‘์ ์„ ํ™”๋ฉด ํ•˜๋‹จ ์ค‘์•™์œผ๋กœ ์ด๋™์‹œ์ผœ ๋•…์—์„œ ์˜ฌ๋ผ์˜ค๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
    • drawBranch(...): ์žฌ๊ท€ ํ•จ์ˆ˜๋กœ, ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ๊ธธ์ด๊ฐ€ ์ค„์–ด๋“ค๊ณ  ํšŒ์ „ํ•ฉ๋‹ˆ๋‹ค.
    • push()/pop(): ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์žฌ๊ท€ ํ˜ธ์ถœ ์ „์— ํ˜„์žฌ ์œ„์น˜์™€ ํšŒ์ „ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๊ณ , ์žฌ๊ท€ ํ˜ธ์ถœ์ด ๋๋‚œ ํ›„ ์›๋ž˜ ์œ„์น˜๋กœ ๋Œ์•„์˜ค๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

3. Recursive Circles

// ArtCode Mentor | p5.js Recursion Example 3 - Modified (Dynamic Radius Scaling)
// ๋ชฉ์ : ์žฌ๊ท€ ๊นŠ์ด์— ๋”ฐ๋ผ ๋ฐ˜์ง€๋ฆ„ ์ถ•์†Œ ๋น„์œจ(Scaling Factor)๊ณผ ํˆฌ๋ช…๋„(Alpha)๋ฅผ ๋ชจ๋‘ ์กฐ์ ˆ
// p5.js ์ตœ์‹  ์•ˆ์ •ํ™” ๋ฒ„์ „ ๊ธฐ๋ฐ˜

const START_RADIUS = 150; // ์‹œ์ž‘ ์›์˜ ๋ฐ˜์ง€๋ฆ„
const MAX_LEVEL = 5; // ์ตœ๋Œ€ ์žฌ๊ท€ ๊นŠ์ด ์ œํ•œ

function setup() {
  createCanvas(800, 400);
  noLoop();
  angleMode(DEGREES);
  noStroke();
}

function draw() {
  background(15);
  translate(width / 2, height / 2);

  // ์žฌ๊ท€ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ์ž‘ (์ค‘์‹ฌ์ , ๋ฐ˜์ง€๋ฆ„, ํ˜„์žฌ ๊นŠ์ด)
  drawRecursiveCircles(0, 0, START_RADIUS, 1);
}

/**
 * ์žฌ๊ท€์ ์œผ๋กœ ์›์„ ๊ทธ๋ฆฌ๊ณ  ๊นŠ์ด์— ๋”ฐ๋ผ ์ƒ‰์ƒ ํˆฌ๋ช…๋„์™€ ๋‹ค์Œ ๋ฐ˜์ง€๋ฆ„ ์ถ•์†Œ ๋น„์œจ์„ ์ ์šฉํ•˜๋Š” ํ•จ์ˆ˜
 * @param {number} x ์ค‘์‹ฌ x ์ขŒํ‘œ
 * @param {number} y ์ค‘์‹ฌ y ์ขŒํ‘œ
 * @param {number} radius ํ˜„์žฌ ์›์˜ ๋ฐ˜์ง€๋ฆ„
 * @param {number} level ํ˜„์žฌ ์žฌ๊ท€ ๊นŠ์ด (1๋ถ€ํ„ฐ ์‹œ์ž‘)
 */
function drawRecursiveCircles(x, y, radius, level) {
  // 1. **์ข…๋ฃŒ ์กฐ๊ฑด (Base Case)**:
  if (radius < 2 || level > MAX_LEVEL) {
    // ์ตœ์†Œ ๋ฐ˜์ง€๋ฆ„์„ 4๋กœ ์กฐ์ •
    return;
  }

  // --- ์ƒ‰์ƒ (ํˆฌ๋ช…๋„๋Š” ์ด์ „๊ณผ ๋™์ผํ•˜๊ฒŒ ๊นŠ์ด์— ๋”ฐ๋ผ ์ ์šฉ) ---
  // ๊นŠ์ด๊ฐ€ ๊นŠ์„์ˆ˜๋ก ํˆฌ๋ช…ํ•ด์ง€๋„๋ก alpha ์„ค์ • (255 -> 50)
  let alpha = map(level, 1, MAX_LEVEL, 170, 20);
  fill(10, 190, 180, alpha); // ์ฃผํ™ฉ์ƒ‰ ๊ณ„์—ด

  // ํ˜„์žฌ ์›์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
  ellipse(x, y, radius * 2);

  // --- ๋ฐ˜์ง€๋ฆ„ ์ถ•์†Œ ๋น„์œจ ๋™์  ๊ณ„์‚ฐ ---
  // ๊นŠ์ด(level)์— ๋”ฐ๋ผ ๋‹ค์Œ ๋ฐ˜์ง€๋ฆ„์˜ ์ถ•์†Œ ๋น„์œจ(Scaling Factor)์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
  // level 1: ์ถ•์†Œ ๋น„์œจ 0.35 (๋งŽ์ด ์ค„์–ด๋“ฆ)
  // level MAX_LEVEL: ์ถ•์†Œ ๋น„์œจ 0.85 (์ƒ๋Œ€์ ์œผ๋กœ ์ ๊ฒŒ ์ค„์–ด๋“ฆ)
  let scalingFactor = map(level, 1, MAX_LEVEL, 0.5, 0.35);

  // ๋‹ค์Œ ์žฌ๊ท€ ํ˜ธ์ถœ์„ ์œ„ํ•œ ์ƒˆ ๋ฐ˜์ง€๋ฆ„
  let nextRadius = radius * scalingFactor;

  // --- ์žฌ๊ท€ ํ˜ธ์ถœ ---
  let angleIncrement = 60;
  let offset = radius;

  for (let angle = 0; angle < 360; angle += angleIncrement) {
    let newX = x + cos(angle) * offset;
    let newY = y + sin(angle) * offset;

    // ์ƒˆ๋กœ์šด ์œ„์น˜, ๋™์  ์ถ•์†Œ๋œ ๋ฐ˜์ง€๋ฆ„, ๊นŠ์ด ์ฆ๊ฐ€๋กœ ์žฌ๊ท€ ํ˜ธ์ถœ
    drawRecursiveCircles(newX, newY, nextRadius, level + 1);
  }
}

4. Recursive Spiral (Polygonal)

์„ ๋ถ„์„ ๊ทธ๋ฆฌ๊ณ  ์™ธ๊ณฝ์—์„œ๋ถ€ํ„ฐ ์•ˆ์ชฝ์œผ๋กœ ์ ์  ๋ณ€์˜ ๊ธธ์ด๊ฐ€ ์ค„์–ด๋“ค๋ฉด์„œ ๊บพ์ด๋„๋ก ํ•˜๋ฉด ๊ฐ์ง„ ๋‚˜์„ ์ด ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค. ๊บพ์ด๋Š” ๊ฐ๋„๋Š” 360๋„๋ฅผ ๊ผญ์ง“์  ๊ฐœ์ˆ˜๋กœ ๋‚˜๋ˆ„์–ด์„œ ์‰ฝ๊ฒŒ ๊ตฌํ•˜๊ณ  ์ตœ์ดˆ ์‹œ์ž‘ ์„ ๋ถ„์˜ ๊ธธ์ด์— ๋งž์ถ”์–ด์„œ ์‹œ์ž‘์ ์„ ์ด๋™์‹œํ‚ค๋ฉด ์ค‘์•™ ์ •๋ ฌ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ์—์„œ๋Š” ์„ ๋ถ„์˜ ๊ธธ์ด๊ฐ€ ํŠน์ • ๊ฐ’์ด ๋˜๋ฉด ์žฌ๊ท€๋ฅผ ๋ฉˆ์ถ”๋Š” ๋กœ์ง์„ ๋„ฃ์—ˆ๊ณ , ์™ธ๊ณฝ๋ถ€ํ„ฐ ์•ˆ์ชฝ๊นŒ์ง€ ์„ ๋ถ„์€ ์ ์ฐจ ์–‡์•„์ง‘๋‹ˆ๋‹ค.

const NUM_SIDES = 6;         // ๋‹ค๊ฐํ˜•์˜ ๊ผญ์ง“์  ๊ฐœ์ˆ˜
const START_LENGTH = 400;    // ๋‚˜์„ ์˜ ๊ฐ€์žฅ ๋ฐ”๊นฅ์ชฝ ๋ณ€์˜ ์‹œ์ž‘ ๊ธธ์ด
const LENGTH_DECREMENT = 8;  // ๋งค๋ฒˆ ๊ธธ์ด๊ฐ€ ์ค„์–ด๋“œ๋Š” ๊ณ ์ • ๊ฐ’
const MIN_LENGTH = 2;        // ๋‚˜์„ ์ด ๋ฉˆ์ถ”๋Š” ์ตœ์†Œ ๊ธธ์ด
const MIN_WEIGHT = 0.3;      // ์„ ๋ถ„์˜ ์ตœ์†Œ ๋‘๊ป˜
const MAX_WEIGHT = 4         // ์„ ๋ฌธ์˜ ์ตœ๋Œ€ ๋‘๊ป˜

// ์ •๋‹ค๊ฐํ˜•์˜ ์™ธ๊ฐ (External Angle) = 360๋„ / ๊ผญ์ง“์  ๊ฐœ์ˆ˜
const TURN_ANGLE = 360 / NUM_SIDES;

function setup() {
  createCanvas(800, 400);
  noLoop();
  angleMode(DEGREES); // ํšŒ์ „์„ ์œ„ํ•ด DEGREE ๋ชจ๋“œ ์‚ฌ์šฉ

  background(5);

  // ์„ ๋ถ„์˜ ๋‘๊ป˜ ๋•Œ๋ฌธ์— ์บ”๋ฒ„์Šค ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ€์ง€ ์•Š๋„๋ก ์‹œ์ž‘์ ์„ ๋‘๊ป˜๋งŒํผ ์ด๋™
  // ์„ ๋ถ„์˜ ์‹œ์ž‘์ ์„ ์ตœ์ดˆ์„ ๋ถ„ ๊ธธ์ด๋งŒํผ ์ด๋™์‹œ์ผœ์„œ ๋‚˜์„  ์ค‘์‹ฌ์ด ์บ”๋ฒ„์Šค ์ค‘์‹ฌ์— ์˜ค๋„๋ก
  // y์ถ•์€ ํ•„์š”์— ๋”ฐ๋ผ ์‹ ์ถ”๊ฐ€
  translate(-2 * MAX_WEIGHT + START_LENGTH / 2, MAX_WEIGHT); 
  // ์žฌ๊ท€ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ์ž‘ (ํ˜„์žฌ ๊ธธ์ด, ํ˜„์žฌ ๋‹จ๊ณ„)
  drawPolygonalSpiral(START_LENGTH, 0);
}

/**
 * ๋‹ค๊ฐํ˜• ๋‚˜์„  ํŒจํ„ด์„ ์žฌ๊ท€์ ์œผ๋กœ ๊ทธ๋ฆฌ๋Š” ํ•จ์ˆ˜
 * @param {number} len ํ˜„์žฌ ์„ ๋ถ„์˜ ๊ธธ์ด
 * @param {number} step ํ˜„์žฌ ์žฌ๊ท€ ๋‹จ๊ณ„ (0๋ถ€ํ„ฐ ์‹œ์ž‘)
 */
function drawPolygonalSpiral(len, step) {
  // 1. **์ข…๋ฃŒ ์กฐ๊ฑด (Base Case)**:
  if (len <= MIN_LENGTH) {
    return;
  }

  // 2. **์„  ์Šคํƒ€์ผ ์„ค์ •**:
  // ๊นŠ์ด์— ๋”ฐ๋ผ ์„  ๋‘๊ป˜์™€ ์ƒ‰์ƒ ์„ค์ •
  let weight = map(len, MIN_LENGTH, START_LENGTH, MIN_WEIGHT, MAX_WEIGHT);
  strokeWeight(weight);
  stroke(60, 190, 180, 255);

  // 3. **๋‹ค๊ฐํ˜• ๊ทธ๋ฆฌ๊ธฐ ๋ฐ ๋ณ€ํ™˜**:

  // N๊ฐํ˜• ๋‚˜์„ ์€ N๊ฐœ์˜ ๋ณ€์„ ์ˆœ์„œ๋Œ€๋กœ ๊ทธ๋ฆฌ๋ฉฐ ๋‚˜์•„๊ฐ€์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  // ์‚ฌ๊ฐํ˜• ๋‚˜์„ (N=4)์—์„œ๋Š” 4๊ฐœ์˜ ๋ฐฉํ–ฅ์„ if/else if๋กœ ์ฒ˜๋ฆฌํ–ˆ์ง€๋งŒ,
  // N๊ฐํ˜•์—์„œ๋Š” for ๋ฃจํ”„์™€ p5.js์˜ 'rotate'๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ผ๋ฐ˜ํ™”ํ•ฉ๋‹ˆ๋‹ค.

  // ํ˜„์žฌ ๊ธธ์ด์™€ ๋ฐฉํ–ฅ์œผ๋กœ ํ•˜๋‚˜์˜ ๋ณ€์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
  line(0, 0, len, 0);

  // ๋‹ค์Œ ๋ณ€์„ ๊ทธ๋ฆด ์œ„์น˜๋กœ ์›์  ์ด๋™
  // (0, 0)์—์„œ ๊ทธ๋ฆฐ ์„ ๋ถ„์˜ ๋์  (len, 0)์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
  translate(len, 0);

  // 4. **ํšŒ์ „ (Rotation)**:
  // ๋‹ค์Œ ๋ณ€์„ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•ด ์ขŒํ‘œ๊ณ„๋ฅผ 'TURN_ANGLE'๋งŒํผ ํšŒ์ „์‹œํ‚ต๋‹ˆ๋‹ค.
  // ์ด๋Š” N๊ฐํ˜•์˜ ์™ธ๊ฐ๋งŒํผ ๊บพ๋Š” ํšจ๊ณผ๋ฅผ ์ค๋‹ˆ๋‹ค.
  rotate(TURN_ANGLE);

  // 5. **๊ธธ์ด ์—…๋ฐ์ดํŠธ**:

  let nextLength = len;

  // ์‚ฌ๊ฐํ˜• ๋‚˜์„ (N=4)์—์„œ๋Š” 2๋‹จ๊ณ„๋งˆ๋‹ค ๊ธธ์ด๋ฅผ ์ค„์˜€์ง€๋งŒ,
  // ์ผ๋ฐ˜์ ์ธ ๋‹ค๊ฐํ˜• ๋‚˜์„ ์—์„œ๋Š” ๊ฐ ๋ณ€์„ ๊ทธ๋ฆฐ ํ›„ ๊ธธ์ด๋ฅผ ์ค„์ด๋Š” ๊ฒƒ์ด ์ž์—ฐ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค.
  // ๊ทธ๋Ÿฌ๋‚˜ ์›๋ณธ ์‚ฌ๊ฐํ˜• ๋‚˜์„ ์˜ '๋‘ ๋ณ€๋งˆ๋‹ค ๊ธธ์ด ๊ฐ์†Œ' ๋กœ์ง์„ ์œ ์ง€ํ•˜์—ฌ ๋‚˜์„  ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  if (step % (NUM_SIDES / 2) === 0) {
    nextLength = len - LENGTH_DECREMENT;
  }

  // 6. **์žฌ๊ท€ ํ˜ธ์ถœ**:
  // ๊ธธ์ด๋Š” ์—…๋ฐ์ดํŠธํ•˜๋˜, ํšŒ์ „ ๊ฐ๋„๋Š” ๊ณ„์† ๋ˆ„์ ํ•˜์—ฌ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  drawPolygonalSpiral(nextLength, step + 1);
}

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

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