Clinton Montague

Developer, learner of things, functional programming enthusiast, hacker, and all round inquisitor.

Generative art experiment 4: colourful roots

February 26, 2013

It’s been a while since I’ve posted about anything interesting that I’ve made in Processing. Time to put an end to that! Allow me to present “colourful roots”. It started off as a simple particle system with the usual fade effect, but when I let the particle shrink and stay fully opaque, I was delighted to see what it looked like!

The processing code

// canvas colour
int background_color = color(70, 70, 70);
// particle colours
color[] colors = {
      color (227, 211, 31), // yellow
      color (227, 31, 123), // pink
      color (31, 188, 227) // blue
    };
// colour of the stroke around the particles
color stroke_color = color(0, 70); // black, 70% alpha

// should the mouse draw particles when moved?
Boolean INTERACTIVE = true;
// should the canvas be cleared on each particle iteration
Boolean CLEAR = false;

//-----------------------------------------------------------------//
//-----------------------------------------------------------------//
//-----------------------------------------------------------------//
//-----------------------------------------------------------------//
ArrayList particles;

// reset on mouse click
void mouseClicked () {
  setup();
  mouseMoved();
}

// save on press spacebar
void keyPressed () {
  if (keyCode == RETURN || keyCode == ENTER) {
    save("Colourful entrails " + year() + "-" + month() + "-" + day() + " " + hour() + ":" + minute() + ":" + second() + ".png");
  }
}

// add particles on mouse move
void mouseMoved () {
  if (!INTERACTIVE) return;
  int count = int(random(2, 5)); 
  while(count-- > 0) {
    addParticle(new PVector(mouseX, mouseY));
  }
}

void setup() {
  size(800, 400, P2D);
  blendMode(ADD);
  stroke(stroke_color);  
  background(background_color);

  particles = new ArrayList();

  // create a particle which'll go around making particles if it's not interactive  
  if (!INTERACTIVE) {
    Particle generator = new Particle(new PVector(width/2, height/2));
    generator.size = 10000;
    while(generator.isAlive()) {
      int count = int(random(2,5));
      generator.update();
      while (count-- > 0) {
        addParticle(generator.position);
      }
    }
  }
}

// add a new particle to the particles arraylist with specified position
void addParticle (PVector position) {
  // clone the vector 
  PVector position_clone = new PVector(position.x, position.y);
  particles.add(new Particle(position_clone));
}

void draw() {
  // keep track of dead particles - they'll be removed after the loop
  ArrayList dead = new ArrayList();

  if (CLEAR) {
    fill(background_color);
    rect(0, 0, width, height);
  }

  // go through each of the particles and update/draw
  ListIterator it = particles.listIterator();
  while (it.hasNext()) {
    Particle particle = (Particle) it.next();
    // only draw if it's alive
    if (particle.isAlive()) {
      particle.update();
      particle.draw();
    } else { // otherwise add to dead list
      dead.add(particle);
    }
  }
  // remove all dead particles
  particles.removeAll(dead);
}

// a tad messy, building it up as i go along
class Particle 
{
  PVector position;
  PVector velocity;
  PVector force;
  float size;
  color c;
  int life;

  Particle (PVector p) {
    life = 0;
    position = p;
    size = random(30, 50);
    velocity = new PVector(random(2, 5), 0);
    rotate2D(velocity, random(0, TWO_PI));
    c = colors[int(random(colors.length))];
    force = new PVector(0, 1);
  }

  void update () {
    life++;
    if (life == 20) {
      velocity.mult(1.1);
    }
    if (life < 20) {
      size *= 0.99;
    } else {
      size *= 0.95;
    }

    position.add(velocity);
    velocity.rotate(random(-QUARTER_PI, QUARTER_PI));
  }
  void draw () {
    pushMatrix();
    translate(position.x, position.y);
    fill (c);
    ellipse(0, 0, size, size);
    popMatrix();
  }
  Boolean isAlive() {
    return size >= 1;
  }
}

void rotate2D(PVector v, float theta) {
  float xTemp = v.x;
  v.x = v.x*cos(theta) - v.y*sin(theta);
  v.y = xTemp*sin(theta) + v.y*cos(theta);
}