//ParticleSystem ps;
ArrayList psystems;
 
 
void setup() {
  size(720,480);
  frameRate(30);
  //colorMode(RGB,255,255,255,100);
  //ps = new ParticleSystem(1,new Vector3D(width/2,height/2,0));
  psystems = new ArrayList();
  smooth();
}
 
void draw() {
  background(255);
  /* *yk*
  //ps = new ParticleSystem(1,new Vector3D(mouseX,mouseY,0));
  //ps.run();
  // *yk*
  // Calculate a "wind" force based on mouse horizontal position
  float dx = (mouseX - width/2) / 1000.0;
  Vector3D wind = new Vector3D(dx,0,0);
  ps.add_force(wind);
  ps.run();
  for (int i = 0; i < 2; i++) {
    ps.addParticle();
  }
  //ps.addParticle();
  // *yk*/
  // Cycle through all particle systems, run them and delete old ones
  //if (mousePressed == true) {
 
 float dx;
 dx = -.166;
  Vector3D wind = new Vector3D(dx,0,0);
  // Particle System 1
    psystems.add(new ParticleSystem(int(random(5,25)),new Vector3D(93,161,0)));
  // Particle System 2
    psystems.add(new ParticleSystem(int(random(5,25)),new Vector3D(374,235,0)));
  // Particle System 3
    psystems.add(new ParticleSystem(int(random(5,25)),new Vector3D(122,360,0)));
  // Particle System 4
    psystems.add(new ParticleSystem(int(random(5,25)),new Vector3D(615,66,0)));
  // Particle System 5
    psystems.add(new ParticleSystem(int(random(5,25)),new Vector3D(638,218,0)));
  // Particle System 5
    psystems.add(new ParticleSystem(int(random(5,25)),new Vector3D(627,422,0)));
 
  //float dx = (mouseX - width/2) / 1000.0;
 
 
    for (int i = psystems.size()-1; i >= 0; i--) {
    ParticleSystem psys = (ParticleSystem) psystems.get(i);
    psys.add_force(wind);
    psys.run();
    if (psys.dead()) {
      psystems.remove(i);
    }
  }
}
 
// When the mouse is pressed, add a new particle system
void mousePressed() {
  psystems.add(new ParticleSystem(int(random(5,25)),new Vector3D(mouseX,mouseY)));
}
 
 
// A subclass of Particle
 
// Created 2 May 2005
 
class CrazyParticle extends Particle {
 
  // Just adding one new variable to a CrazyParticle
  // It inherits all other fields from "Particle", and we don't have to retype them!
  float theta;
 
  // The CrazyParticle constructor can call the parent class (super class) constructor
  CrazyParticle(Vector3D l) {
    // "super" means do everything from the constructor in Particle
    super(l);
    // One more line of code to deal with the new variable, theta
    theta = 0.0;
 
  }
 
  // Notice we don't have the method run() here; it is inherited from Particle
 
  // This update() method overrides the parent class update() method
  void update() {
    super.update();
    // Increment rotation based on horizontal velocity
    float theta_vel = (vel.x * vel.magnitude()) / 10.0f;
    theta += theta_vel;
  }
 
  // Override timer
  void timer() {
    timer -= .5;
  }
 
  // Method to display
  void render() {
    // Render the ellipse just like in a regular particle
    super.render();
 
    // Then add a rotating line
    pushMatrix();
    translate(loc.x,loc.y);
    rotate(theta);
    stroke(255,timer);
    line(0,0,25,0);
    popMatrix();
  }
}
 
// A simple Particle class
 
class Particle {
  Vector3D loc;
  Vector3D vel;
  Vector3D acc;
  float r;
  float timer;
 
  // One constructor
  Particle(Vector3D a, Vector3D v, Vector3D l, float r_) {
    acc = a.copy();
    vel = v.copy();
    loc = l.copy();
    r = r_;
    timer = 50.0;
  }
 
  // Another constructor (the one we are using here)
  Particle(Vector3D l) {
    acc = new Vector3D(0,0.05,0);
    vel = new Vector3D(random(-1,1),random(-2,0),0);
    loc = l.copy();
    r = 10.0;
    timer = 50.0;
  }
 
 
  void run() {
    update();
    render();
  }
 
  // Method to update location
  void update() {
    vel.add(acc);
    loc.add(vel);
    timer -= 1.0;
  }
 
  // Method to display
  void render() {
    ellipseMode(CENTER);
    noStroke();
    fill(50,timer);
    ellipse(loc.x,loc.y,r,r);
  }
 
  // Method to apply a force vector to the Particle object
  // Note we are ignoring "mass" here
  void add_force(Vector3D f) {
    acc.add(f);
  }
 
  // Is the particle still useful?
  boolean dead() {
    if (timer <= 0.0) {
      return true;
    } else {
      return false;
    }
  }
}
 
 
// A class to describe a group of Particles
// An ArrayList is used to manage the list of Particles
 
class ParticleSystem {
 
  ArrayList particles;    // An arraylist for all the particles
  Vector3D origin;        // An origin point for where particles are birthed
 
  ParticleSystem(int num, Vector3D v) {
    particles = new ArrayList();              // Initialize the arraylist
    origin = v.copy();                        // Store the origin point
 
    for (int i = 0; i < num; i++) {
      particles.add(new Particle(origin));    // Add "num" amount of particles to the arraylist
    }
  }
 
  void run() {
    // Cycle through the ArrayList backwards b/c we are deleting
    for (int i = particles.size()-1; i >= 0; i--) {
      Particle p = (Particle) particles.get(i);
      p.run();
      if (p.dead()) {
        particles.remove(i);
      }
    }
  }
 
  // Method to add a force vector to all particles currently in the system
  void add_force(Vector3D dir) {
    for (int i = particles.size()-1; i >= 0; i--) {
      Particle p = (Particle) particles.get(i);
      p.add_force(dir);
    }
 
  }
 
  void addParticle() {
    particles.add(new Particle(origin));
  }
 
  void addParticle(Particle p) {
    particles.add(p);
  }
 
 
  // A method to test if the particle system still has particles
  boolean dead() {
    if (particles.isEmpty()) {
      return true;
    } else {
      return false;
    }
  }
 
}
 
 
// Simple Vector3D Class
 
public class Vector3D {
  public float x;
  public float y;
  public float z;
 
  Vector3D(float x_, float y_, float z_) {
    x = x_; y = y_; z = z_;
  }
 
  Vector3D(float x_, float y_) {
    x = x_; y = y_; z = 0f;
  }
 
  Vector3D() {
    x = 0f; y = 0f; z = 0f;
  }
 
  void setX(float x_) {
    x = x_;
  }
 
  void setY(float y_) {
    y = y_;
  }
 
  void setZ(float z_) {
    z = z_;
  }
 
  void setXY(float x_, float y_) {
    x = x_;
    y = y_;
  }
 
  void setXYZ(float x_, float y_, float z_) {
    x = x_;
    y = y_;
    z = z_;
  }
 
  void setXYZ(Vector3D v) {
    x = v.x;
    y = v.y;
    z = v.z;
  }
  public float magnitude() {
    return (float) Math.sqrt(x*x + y*y + z*z);
  }
 
  public Vector3D copy() {
    return new Vector3D(x,y,z);
  }
 
  public Vector3D copy(Vector3D v) {
    return new Vector3D(v.x, v.y,v.z);
  }
 
  public void add(Vector3D v) {
    x += v.x;
    y += v.y;
    z += v.z;
  }
 
  public void sub(Vector3D v) {
    x -= v.x;
    y -= v.y;
    z -= v.z;
  }
 
  public void mult(float n) {
    x *= n;
    y *= n;
    z *= n;
  }
 
  public void div(float n) {
    x /= n;
    y /= n;
    z /= n;
  }
 
  public void normalize() {
    float m = magnitude();
    if (m > 0) {
       div(m);
    }
  }
 
  public void limit(float max) {
    if (magnitude() > max) {
      normalize();
      mult(max);
    }
  }
 
  public float heading2D() {
    float angle = (float) Math.atan2(-y, x);
    return -1*angle;
  }
 
  public Vector3D add(Vector3D v1, Vector3D v2) {
    Vector3D v = new Vector3D(v1.x + v2.x,v1.y + v2.y, v1.z + v2.z);
    return v;
  }
 
  public Vector3D sub(Vector3D v1, Vector3D v2) {
    Vector3D v = new Vector3D(v1.x - v2.x,v1.y - v2.y,v1.z - v2.z);
    return v;
  }
 
  public Vector3D div(Vector3D v1, float n) {
    Vector3D v = new Vector3D(v1.x/n,v1.y/n,v1.z/n);
    return v;
  }
 
  public Vector3D mult(Vector3D v1, float n) {
    Vector3D v = new Vector3D(v1.x*n,v1.y*n,v1.z*n);
    return v;
  }
 
  public float distance (Vector3D v1, Vector3D v2) {
    float dx = v1.x - v2.x;
    float dy = v1.y - v2.y;
    float dz = v1.z - v2.z;
    return (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
  }
 
}