> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.LyCDXev-AaG/rev.944
 * 
 * authors: 
 *   Stephen Boyd

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



/*
   Stephen Boyd, @sspboyd

   Built while reading @shiffman's The Nature of Code book
   https://www.kickstarter.com/projects/shiffman/the-nature-of-code-book-project

   Instructions:
   Click and drag to move the white attractor around the screen
   Press "c" to clear the screen and re-initialize the sketch
   Press "s" to render out an image of the screen.
   
   Try playing with variables to get different results, specifically:
    Attractor mass, 
    gravity constant (g)
    topSpeed
    distance max mins (5, and 30 now) 
    the number of particles (NUM_PARTICLES)
    MIN_PARTICLE_SIZE and MAX_PARTICLE_SIZE
*/  
   

int NUM_PARTICLES = 100;
float MIN_PARTICLE_SIZE = .9;
float MAX_PARTICLE_SIZE = 12;

Mover[] movers = new Mover[NUM_PARTICLES];
Attractor a;

void setup() {
  size(900,800);
  smooth();
  colorMode(RGB, 100);
  frameRate(30);
  background(100, 100, 100);
  init();
}

void init(){
  background(100, 100, 100);
  for(int i=0; i<movers.length; i++) {
    movers[i] = new Mover(random(MIN_PARTICLE_SIZE, MAX_PARTICLE_SIZE), random(width), random(height));
  }

  a = new Attractor();
    
    }

void draw() {
    for(int i=0; i<movers.length; i++) {
        Mover mi = movers[i];
        for(int j=0; j<movers.length; j++) {
        Mover mj = movers[j];

        if(i != j) {
            PVector force = mj.attract(mi);
            mi.applyForce(force);
        }
    }
    PVector attractorForce = a.attract(mi);
    mi.applyForce(attractorForce);

    PVector wind = new PVector(.01, 0);
    mi.applyForce(wind);
    mi.update();
    mi.display();
    }
    a.display();
}


class Attractor {
  float mass;
  PVector location;
  float g;

  Attractor() {
    location = new PVector(width/2, height/2.5);
    mass = 400;
    g = 0.8;
  }

  PVector attract(Mover m) {
    PVector force = PVector.sub(location, m.location);
    float distance = force.mag();
    distance = constrain(distance, 5, 30);
    force.normalize();
    float strength = (G * mass * m.mass) / (distance * distance);
    force.mult(strength);

    return force;
  }

  void display() {
    noStroke();
    fill(100,100,100,15);

    if(mousePressed){
      location.x=mouseX;
      location.y=mouseY;
    }
      ellipse(location.x, location.y, mass*.5, mass*.5);
    }
}


class Mover {
  PVector location;
  PVector velocity;
  PVector acceleration;
  float topSpeed;
  float mass;
  color c;

  Mover(float m, float x, float y) {
    location = new PVector(x, y);
    velocity = new PVector(0,0);
    acceleration = new PVector(0,0);
    topSpeed = 50;
    mass = m;    
  }

  PVector attract(Mover m) {
    PVector force = PVector.sub(location, m.location);
    float distance = force.mag();
    distance = constrain(distance, 5, 30);
    force.normalize();

    float strength = (G * mass * m.mass) / (distance * distance);
    force.mult(strength);

    return force;
  }

  void update() {
    velocity.add(acceleration);
    velocity.limit(topSpeed);
    location.add(velocity);
    acceleration.mult(0);
  }

  void applyForce(PVector force) {
    PVector f = force.get();
    f.div(mass);
    acceleration.add(f);
  }

  void display() {
    noStroke();
    color c;
    if(velocity.mag() > topSpeed/2) {
      c = color(95,83,0,30);
    } 
    else {
      c = color(24,72,87,30);
    }
    fill(c,100);
    ellipse(location.x, location.y, mass*1, mass*1);
    }
}


// Keyboard UI
void keyPressed() {
  if(key == 'C' || key == 'c'){ // C = clear the screen and re-initialize the sketch
      init();
  }

  if (key == 'S' || key == 's') { // S = save the sketch as an image
    String filePrefix = "Attractor_Duotone";
    String fileType = ".tif";
    String dateStamp = year() + "" + nf(month(),2) + "" + nf(day(),2) + "-" + nf(hour(),2) + "" + nf(minute(),2) + "" + nf(second(),2);
    String fileName = filePrefix + dateStamp  + fileType;
    save(fileName);
    println("saved file: " + fileName);
  }
}