

// * Dandelion Generative Art
// ===============================================
// [1. GLOBAL CONFIGURATION]
// ===============================================
const MAX_GEN = 7; // ์ต๋ ์ฑ์ฅ ๋จ๊ณ (์ธ๋)
const START_BRANCH_COUNT = 6; // ์ด๊ธฐ ์ค์ฌ์์ ๋ป์ด ๋๊ฐ๋ ๊ฐ์ง ์
const SUB_BRANCH_COUNT = 4; // ์ดํ ๊ฐ ๋ง๋์์ ๋ถ๊ธฐ๋๋ ๊ฐ์ง ์
const MIN_LINE_LEN = 80; // ์ต์ ๊ฐ์ง ๊ธธ์ด (px)
const MAX_LINE_LEN = 90; // ์ต๋ ๊ฐ์ง ๊ธธ์ด (px)
const MIN_DOT_SIZE = 2; // ์ค์ฌ๋ถ ๋
ธ๋ ์ต์ ํฌ๊ธฐ
const MAX_DOT_SIZE = 5; // ๋ง๋จ ์จ์ ์ต๋ ํฌ๊ธฐ
const BG_COLOR = "#432f2f"; // ๋ฐฐ๊ฒฝ ์์ HEX
const LINE_COLOR = [255, 180]; // ์ ์์ ๋ฐ ํฌ๋ช
๋ [R, G, B, Alpha] ๋๋ [Gray, Alpha]
const DOT_COLOR = [255, 220]; // ์ ์์ ๋ฐ ํฌ๋ช
๋
// ===============================================
// [2. STATE VARIABLES]
// ===============================================
let nodes = []; // ์ ์ฒด ๋
ธ๋ ์ ์ฅ ๋ฐฐ์ด (๋ ๋๋ง์ฉ)
let activeNodes = []; // ํ์ฌ ๋ถ๊ธฐ ์ค์ธ ๋ง๋จ ๋
ธ๋ ๋ฐฐ์ด (์ฐ์ฐ์ฉ)
let lastBranchTime = 0; // ๋ง์ง๋ง ๋ถ๊ธฐ ์์ ๊ธฐ๋ก
const BRANCH_INTERVAL = 500; // ๋ถ๊ธฐ ๊ฐ๊ฒฉ (๋ฐ๋ฆฌ์ธ์ปจ๋)
// ===============================================
// [3. P5.JS CORE]
// ===============================================
function setup() {
createCanvas(1200, 1200);
background(BG_COLOR);
// ์ด๊ธฐ ๋ฃจํธ ๋
ธ๋ ์ค์ (์บ๋ฒ์ค ์ ์ค์)
const root = {
x: width / 2,
y: height / 2,
parent: null,
generation: 0,
angle: 0,
};
nodes.push(root);
activeNodes.push(root);
lastBranchTime = millis();
}
function draw() {
background(BG_COLOR);
// ์ ์ฒด ๋
ธ๋ ์ํ ๋ฐ ๋ ๋๋ง
for (let i = 0; i < nodes.length; i++) {
const n = nodes[i];
// 1. ๋ผ์ธ ๋ ๋๋ง
if (n.parent) {
// ์ธ๋๊ฐ ์งํ๋ ์๋ก ์ ์ด ๊ฐ๋์ด์ง๋๋ก ์ค์
const sw = map(n.generation, 1, MAX_GEN, 1.2, 0.2);
stroke(LINE_COLOR);
strokeWeight(sw);
line(n.x, n.y, n.parent.x, n.parent.y);
}
// 2. ๋
ธ๋(์ ) ๋ ๋๋ง
noStroke();
fill(DOT_COLOR);
// ์ธ๋์ ๋ฐ๋ผ ์ ํฌ๊ธฐ ๊ฒฐ์ (๋ง๋จ์ผ์๋ก MAX_DOT_SIZE์ ์๋ ด)
const dotSize = map(n.generation, 0, MAX_GEN, MIN_DOT_SIZE, MAX_DOT_SIZE);
circle(n.x, n.y, dotSize);
}
// 3. ๋ถ๊ธฐ ํ์ด๋ฐ ์ฒดํฌ ๋ฐ ์คํ
if (millis() - lastBranchTime > BRANCH_INTERVAL) {
if (activeNodes.length > 0 && activeNodes[0].generation < MAX_GEN) {
branchOut();
lastBranchTime = millis();
} else if (
activeNodes.length > 0 &&
activeNodes[0].generation === MAX_GEN
) {
// ๋ชจ๋ ์ฑ์ฅ์ด ๋๋ฌ์ ๋ ๋ถํ์ํ ๋ฃจํ ์ ์ง (์ฑ๋ฅ ์ต์ ํ)
console.log("Growth Complete. Loop stopped.");
noLoop();
}
}
}
// ===============================================
// [4. LOGIC FUNCTIONS]
// ===============================================
function branchOut() {
const nextActiveNodes = [];
for (let i = 0; i < activeNodes.length; i++) {
const p = activeNodes[i];
// ์บ๋ฒ์ค ๊ฒฝ๊ณ๋ฅผ ๋ฒ์ด๋๋ฉด ์์ ๋
ธ๋๋ฅผ ์์ฑํ์ง ์์ (๋ฉ๋ชจ๋ฆฌ ํจ์จ)
if (p.x < 0 || p.x > width || p.y < 0 || p.y > height) continue;
const currentGen = p.generation;
const isRoot = currentGen === 0;
const count = isRoot ? START_BRANCH_COUNT : SUB_BRANCH_COUNT;
// ๋ถ์ฐ ๊ฐ๋ ์ค์ (์ฒซ ๋ถ๊ธฐ๋ 360๋, ์ดํ๋ 72๋ ๋ฒ์ ๋ด ๋ถ์ฌ)
const spreadAngle = isRoot ? TWO_PI : PI * 0.4;
for (let j = 0; j < count; j++) {
let angle;
if (isRoot) {
angle = (TWO_PI / START_BRANCH_COUNT) * j;
} else {
// ๋ถ๋ชจ์ ๊ฐ๋๋ฅผ ์ค์ฌ์ผ๋ก ์ข์ฐ spreadAngle ๋งํผ ๋ถ์ฐ
angle =
p.angle + map(j, 0, count - 1, -spreadAngle / 2, spreadAngle / 2);
}
const len = random(MIN_LINE_LEN, MAX_LINE_LEN);
const newNode = {
x: p.x + cos(angle) * len,
y: p.y + sin(angle) * len,
parent: p,
generation: currentGen + 1,
angle: angle,
};
nodes.push(newNode);
nextActiveNodes.push(newNode);
}
}
// ๋ค์ ์ฐ์ฐ์ ์ํด ๋ง๋จ ๋
ธ๋ ๊ฐฑ์
activeNodes = nextActiveNodes;
}