๐ŸŽ‡ Particle Contagion

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

p5.js Art

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

๐Ÿ“ p5.js

// ์ „์—ญ ๋ณ€์ˆ˜
let particles = [];
const PARTICLE_COUNT = 2000;
const YELLOW_PERCENTAGE = 0.01; // ์ดˆ๊ธฐ ๋…ธ๋ž€์ƒ‰ ๋น„์œจ (0~1)
const CANVAS_SIZE = 1000;
const BG_COLOR = 10;

let startTime;
let endTime = null;
let isInfectionComplete = false;

function setup() {
  createCanvas(CANVAS_SIZE, CANVAS_SIZE);

  // ํŒŒํ‹ฐํด ์ดˆ๊ธฐํ™”
  const yellowCount = Math.floor(PARTICLE_COUNT * YELLOW_PERCENTAGE);

  for (let i = 0; i < PARTICLE_COUNT; i++) {
    particles.push({
      x: random(CANVAS_SIZE),
      y: random(CANVAS_SIZE),
      vx: random(-1, 1),
      vy: random(-1, 1),
      r: floor(random(3, 5)),
      isYellow: i < yellowCount,
    });
  }

  startTime = millis();
}

function draw() {
  background(BG_COLOR);
  noStroke();

  // ํŒŒํ‹ฐํด ๊ฐœ์ˆ˜ ์นด์šดํŠธ
  let yellowCount = 0;
  let greenCount = 0;

  // ํŒŒํ‹ฐํด ์—…๋ฐ์ดํŠธ ๋ฐ ๊ทธ๋ฆฌ๊ธฐ
  for (let i = 0; i < particles.length; i++) {
    let p = particles[i];

    // ๊ฐœ์ˆ˜ ์นด์šดํŠธ
    if (p.isYellow) {
      yellowCount++;
    } else {
      greenCount++;
    }

    // ์ „์—ผ์ด ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜์„ ๋•Œ๋งŒ ์ด๋™
    if (!isInfectionComplete) {
      // ์œ„์น˜ ์—…๋ฐ์ดํŠธ
      p.x += p.vx;
      p.y += p.vy;

      // ๋ฒฝ ์ถฉ๋Œ ์ฒ˜๋ฆฌ
      if (p.x - p.r < 0 || p.x + p.r > CANVAS_SIZE) {
        p.vx *= -1;
        p.x = constrain(p.x, p.r, CANVAS_SIZE - p.r);
      }
      if (p.y - p.r < 0 || p.y + p.r > CANVAS_SIZE) {
        p.vy *= -1;
        p.y = constrain(p.y, p.r, CANVAS_SIZE - p.r);
      }

      // ํŒŒํ‹ฐํด ๊ฐ„ ์ถฉ๋Œ ์ฒ˜๋ฆฌ
      for (let j = i + 1; j < particles.length; j++) {
        let other = particles[j];
        let dx = other.x - p.x;
        let dy = other.y - p.y;
        let distSq = dx * dx + dy * dy;
        let minDist = p.r + other.r;

        if (distSq < minDist * minDist) {
          // ์ถฉ๋Œ ๋ฐœ์ƒ - ์ „์—ผ ๋กœ์ง
          if (p.isYellow && !other.isYellow) {
            other.isYellow = true;
          } else if (!p.isYellow && other.isYellow) {
            p.isYellow = true;
          }

          // ๋ฐ˜์‚ฌ ์ฒ˜๋ฆฌ
          let dist = sqrt(distSq);
          let nx = dx / dist;
          let ny = dy / dist;

          let dvx = p.vx - other.vx;
          let dvy = p.vy - other.vy;
          let dotProduct = dvx * nx + dvy * ny;

          p.vx -= dotProduct * nx;
          p.vy -= dotProduct * ny;
          other.vx += dotProduct * nx;
          other.vy += dotProduct * ny;

          // ๊ฒน์นจ ํ•ด์†Œ
          let overlap = minDist - dist;
          let separateX = nx * overlap * 0.5;
          let separateY = ny * overlap * 0.5;
          p.x -= separateX;
          p.y -= separateY;
          other.x += separateX;
          other.y += separateY;
        }
      }
    }

    // ๊ทธ๋ฆฌ๊ธฐ
    fill(p.isYellow ? color(255, 255, 0) : color(0, 100, 0));
    circle(p.x, p.y, p.r * 2);
  }

  // ์ „์—ผ ์™„๋ฃŒ ์ฒดํฌ
  if (!isInfectionComplete && greenCount === 0) {
    isInfectionComplete = true;
    endTime = millis();
  }

  // ํ•˜๋‹จ ์™ผ์ชฝ ์ •๋ณด ํ‘œ์‹œ
  fill(255);
  textSize(20);
  textAlign(LEFT);
  text(`Yellow Team : ${yellowCount}`, 20, CANVAS_SIZE - 40);
  text(`Green Team : ${greenCount}`, 20, CANVAS_SIZE - 15);

  // ํ•˜๋‹จ ์˜ค๋ฅธ์ชฝ ํƒ€์ด๋จธ ํ‘œ์‹œ
  textAlign(RIGHT);
  textSize(50);
  let elapsedTime;
  if (isInfectionComplete) {
    elapsedTime = (endTime - startTime) / 1000;
  } else {
    elapsedTime = (millis() - startTime) / 1000;
  }
  text(`Time : ${elapsedTime.toFixed(1)}s`, CANVAS_SIZE - 20, CANVAS_SIZE - 15);
}

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

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