๐ŸŽ‡ Foam Bubble Density Structure

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

p5.js Art

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

๐Ÿ“ p5.js

const CONFIG = {
  canvasSize: 1000,
  bgColor: 15,
  fillColor: [30, 190, 180],
  maxAttempts: 5000,
  minRadius: 5,
};

let circles = [];
let attempts = 0;

function setup() {
  createCanvas(CONFIG.canvasSize, CONFIG.canvasSize);
  background(CONFIG.bgColor);
  noStroke();
}

function draw() {
  if (attempts >= CONFIG.maxAttempts) {
    noLoop();
    return;
  }

  const pos = { x: random(width), y: random(height) };
  const maxR = findMaxRadius(pos.x, pos.y);

  if (maxR >= CONFIG.minRadius) {
    addCircle(pos.x, pos.y, maxR);
    attempts = 0;
  } else {
    attempts++;
  }
}

function addCircle(x, y, r) {
  circles.push({ x, y, r });
  fill(...CONFIG.fillColor, random(100, 255));
  circle(x, y, r * 2);
}

function findMaxRadius(x, y) {
  let maxR = min(x, y, width - x, height - y);

  for (const c of circles) {
    const d = dist(x, y, c.x, c.y);
    maxR = min(maxR, d - c.r);
  }

  return max(maxR, 0);
}

function mousePressed() {
  circles = [];
  attempts = 0;
  background(CONFIG.bgColor);
  loop();
}

๐Ÿ“ p5.js + Comment

// ์ „์—ญ ์„ค์ • ๊ฐ์ฒด
const CONFIG = {
  canvasSize: 1000,          // ์บ”๋ฒ„์Šค ๊ฐ€๋กœยท์„ธ๋กœ ๊ธธ์ด (์ •์‚ฌ๊ฐํ˜•)
  bgColor: 15,               // ๋ฐฐ๊ฒฝ์ƒ‰ (0-255 grayScale ๊ฐ’, 15 = ๋งค์šฐ ์–ด๋‘์šด ํšŒ์ƒ‰)
  fillColor: [30, 190, 180], // ์ฑ„์šฐ๊ธฐ ์ƒ‰์ƒ (RGB ๊ธฐ๋ฐ˜)
  maxAttempts: 5000,         // ํ•œ ํ”„๋ ˆ์ž„์— ์›์„ ๊ทธ๋ฆด ๋•Œ ํ—ˆ์šฉํ•  ์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜
  minRadius: 5,              // ๊ทธ๋ ค์งˆ ์›์˜ ์ตœ์†Œ ๋ฐ˜์ง€๋ฆ„ (์ด๋ณด๋‹ค ์ž‘์œผ๋ฉด ํฌ๊ธฐ)
};

// ์ด๋ฏธ ์ƒ์„ฑ๋œ ์›(์ขŒํ‘œ+๋ฐ˜์ง€๋ฆ„)๋“ค์„ ์ €์žฅํ•  ๋ฐฐ์—ด
let circles = [];

// ํ˜„์žฌ ํ”„๋ ˆ์ž„์—์„œ ์—ฐ์†์œผ๋กœ โ€˜์‹คํŒจโ€™ํ•œ ํšŸ์ˆ˜๋ฅผ ์…€ ๋ณ€์ˆ˜
let attempts = 0;

function setup() {
  // p5.js๊ฐ€ ์ œ๊ณตํ•˜๋Š” createCanvas๋กœ 1000ร—1000 ํ”ฝ์…€ ์ง์‚ฌ๊ฐํ˜• ์บ”๋ฒ„์Šค ์ƒ์„ฑ
  createCanvas(CONFIG.canvasSize, CONFIG.canvasSize);
  
  // ๋ฐฐ๊ฒฝ ํ•œ ๋ฒˆ ์น ํ•˜๊ธฐ (grayScale 15 = ๊ฑฐ์˜ ๊ฒ€์€์ƒ‰์— ๊ฐ€๊นŒ์šด ํšŒ์ƒ‰)
  background(CONFIG.bgColor);
  
  // stroke(์œค๊ณฝ์„ )์„ ๋„๋ฉด ์›์ด ํ…Œ๋‘๋ฆฌ ์—†์ด ๊น”๋”ํ•˜๊ฒŒ ์ฑ„์›Œ์ง
  noStroke();
}

function draw() {
  // 1. ์ตœ๋Œ€ ์‹œ๋„ ํšŸ์ˆ˜๋ฅผ ๋„˜๊ธฐ๋ฉด ๋” ์ด์ƒ ๊ทธ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฃจํ”„ ์ •์ง€
  if (attempts >= CONFIG.maxAttempts) {
    noLoop(); // p5 ๋ฃจํ”„ ์ค‘๋‹จ โ†’ draw()๊ฐ€ ๋” ์ด์ƒ ์ž๋™ ํ˜ธ์ถœ ์•ˆ ๋จ
    return;
  }

  // 2. ์บ”๋ฒ„์Šค ์•ˆ์˜ ์ž„์˜ ํ•œ ์  ์„ ํƒ (0 โ‰ค x < width, 0 โ‰ค y < height)
  const pos = { x: random(width), y: random(height) };

  // 3. ๊ทธ ์ ์—์„œ ๊ทธ๋ฆด ์ˆ˜ ์žˆ๋Š” โ€˜์ตœ๋Œ€ ๋ฐ˜์ง€๋ฆ„โ€™ ๊ณ„์‚ฐ (ํ™”๋ฉด ๊ฒฝ๊ณ„+๊ธฐ์กด ์›๊ณผ ๊ฒน์น˜์ง€ ์•Š๋„๋ก)
  const maxR = findMaxRadius(pos.x, pos.y);

  // 4. ์ตœ์†Œ ๋ฐ˜์ง€๋ฆ„ ์ด์ƒ์˜ ๊ณต๊ฐ„์ด ํ™•๋ณด๋˜๋ฉด ์‹ค์ œ ์› ์ƒ์„ฑ, ์•„๋‹ˆ๋ฉด ์‹œ๋„ ํšŸ์ˆ˜ ์ฆ๊ฐ€
  if (maxR >= CONFIG.minRadius) {
    addCircle(pos.x, pos.y, maxR); // ํ™”๋ฉด์— ๊ทธ๋ฆฌ๊ณ  ๋ฐฐ์—ด์— ์ €์žฅ
    attempts = 0;                  // ์—ฐ์† ์‹คํŒจ ํšŸ์ˆ˜ ์ดˆ๊ธฐํ™” (์„ฑ๊ณตํ–ˆ์œผ๋‹ˆ)
  } else {
    attempts++;                    // ์‹คํŒจ ๋ˆ„์ 
  }
}

function addCircle(x, y, r) {
  // ์ƒˆ ์› ์ •๋ณด๋ฅผ ๋ฐฐ์—ด์— push โ†’ ๋‚˜์ค‘์— ์ถฉ๋Œ ๊ฒ€์‚ฌ ๋•Œ ์žฌ์‚ฌ์šฉ
  circles.push({ x, y, r });

  // fill ์ƒ‰์ƒ์€ ์„ค์ •๊ฐ’(RGB) + ์•ŒํŒŒ๊ฐ’์€ 100~255 ์‚ฌ์ด ๋žœ๋ค
  fill(...CONFIG.fillColor, random(100, 255));

  // p5 circle ํ•จ์ˆ˜: (์ค‘์‹ฌx, ์ค‘์‹ฌy, ์ง€๋ฆ„) โ†’ ์ง€๋ฆ„ = ๋ฐ˜์ง€๋ฆ„ร—2
  circle(x, y, r * 2);
}

function findMaxRadius(x, y) {
  // ์ผ๋‹จ ํ™”๋ฉด ๋„ค ๋ณ€๊นŒ์ง€์˜ ์ตœ์†Œ ๊ฑฐ๋ฆฌ๋ฅผ ๊ตฌํ•ด โ€˜๊ฒฝ๊ณ„๋ฐ–์œผ๋กœ ์‚์ ธ๋‚˜๊ฐ€์ง€ ์•Š๋Š”โ€™ ์ตœ๋Œ€ ๋ฐ˜์ง€๋ฆ„ ์ดˆ๊ธฐ๊ฐ’ ์„ค์ •
  let maxR = min(x, y, width - x, height - y);

  // ๊ธฐ์กด์— ์ƒ์„ฑ๋œ ๋ชจ๋“  ์›๊ณผ์˜ ๊ฑฐ๋ฆฌ๋ฅผ ๋น„๊ตํ•ด ์„œ๋กœ ๊ฒน์น˜์ง€ ์•Š๋„๋ก โ€˜์—ฌ์œ โ€™ ํ™•๋ณด
  for (const c of circles) {
    const d = dist(x, y, c.x, c.y); // ๋‘ ์› ์ค‘์‹ฌ ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ
    maxR = min(maxR, d - c.r);    // โ€˜๋‚ด ๋ฐ˜์ง€๋ฆ„โ€™ = (์ค‘์‹ฌ๊ฐ„ ๊ฑฐ๋ฆฌ - ์ƒ๋Œ€ ๋ฐ˜์ง€๋ฆ„) ์ดํ•˜
  }

  // ์Œ์ˆ˜๊ฐ€ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ 0 ์ด์ƒ์œผ๋กœ ํด๋žจํ”„
  return max(maxR, 0);
}

function mousePressed() {
  // ๋งˆ์šฐ์Šค๋ฅผ ํด๋ฆญํ•˜๋ฉด ์ดˆ๊ธฐํ™”
  circles = [];                 // ์ €์žฅ๋œ ์› ์ •๋ณด ์ „๋ถ€ ์‚ญ์ œ
  attempts = 0;                 // ์‹คํŒจ ํšŸ์ˆ˜ ๋ฆฌ์…‹
  background(CONFIG.bgColor);   // ๋ฐฐ๊ฒฝ์ƒ‰์œผ๋กœ ๋‹ค์‹œ ์น ํ•˜๊ธฐ
  loop();                       // ํ˜น์‹œ noLoop ์ƒํƒœ์˜€๋‹ค๋ฉด draw ์žฌ์‹œ์ž‘
}
profile
Coding Art with Blender / oF / Processing / p5.js / nannou

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