content: /*************************************************************************
   *  Compilation:  javac CollisionSystem.java
   *  Execution:    java CollisionSystem N               (N random particles)
   *                java CollisionSystem < input.txt     (from a file) 
   *  
   *  Creates N random particles and simulates their motion according
   *  to the laws of elastic collisions.
   *
   *************************************************************************/
  
  import java.awt.Color;
  
  public class CollisionSystem {
      private MinPQ<Event> pq;        // the priority queue
      private double t  = 0.0;        // simulation clock time
      private double hz = 0.5;        // number of redraw events per clock tick
      private Particle[] particles;   // the array of particles
  
      // create a new collision system with the given set of particles
      public CollisionSystem(Particle[] particles) {
          this.particles = particles;
      }
  
      // updates priority queue with all new events for particle a
      private void predict(Particle a, double limit) {
          if (a == null) return;
  
          // particle-particle collisions
          for (int i = 0; i < particles.length; i++) {
              double dt = a.timeToHit(particles[i]);
              if (t + dt <= limit)
                  pq.insert(new Event(t + dt, a, particles[i]));
          }
  
          // particle-wall collisions
          double dtX = a.timeToHitVerticalWall();
          double dtY = a.timeToHitHorizontalWall();
          if (t + dtX <= limit) pq.insert(new Event(t + dtX, a, null));
          if (t + dtY <= limit) pq.insert(new Event(t + dtY, null, a));
      }
  
      // redraw all particles
      private void redraw(double limit) {
          StdDraw.clear();
          for (int i = 0; i < particles.length; i++) {
              particles[i].draw();
          }
          StdDraw.show(20);
          if (t < limit) {
              pq.insert(new Event(t + 1.0 / hz, null, null));
          }
      }
  
        
     /********************************************************************************
      *  Event based simulation for limit seconds
      ********************************************************************************/
      public void simulate(double limit) {
          
          // initialize PQ with collision events and redraw event
          pq = new MinPQ<Event>();
          for (int i = 0; i < particles.length; i++) {
              predict(particles[i], limit);
          }
          pq.insert(new Event(0, null, null));        // redraw event
  
  
          // the main event-driven simulation loop
          while (!pq.isEmpty()) { 
  
              // get impending event, discard if invalidated
              Event e = pq.delMin();
              if (!e.isValid()) continue;
              Particle a = e.a;
              Particle b = e.b;
  
              // physical collision, so update positions, and then simulation clock
              for (int i = 0; i < particles.length; i++)
                  particles[i].move(e.time - t);
              t = e.time;
  
              // process event
              if      (a != null && b != null) a.bounceOff(b);              // particle-particle collision
              else if (a != null && b == null) a.bounceOffVerticalWall();   // particle-wall collision
              else if (a == null && b != null) b.bounceOffHorizontalWall(); // particle-wall collision
              else if (a == null && b == null) redraw(limit);               // redraw event
  
              // update the priority queue with new collisions involving a or b
              predict(a, limit);
              predict(b, limit);
          }
      }
  
  
     /*************************************************************************
      *  An event during a particle collision simulation. Each event contains
      *  the time at which it will occur (assuming no supervening actions)
      *  and the particles a and b involved.
      *
      *    -  a and b both null:      redraw event
      *    -  a null, b not null:     collision with vertical wall
      *    -  a not null, b null:     collision with horizontal wall
      *    -  a and b both not null:  binary collision between a and b
      *
      *************************************************************************/
      private class Event implements Comparable<Event> {
          private final double time;         // time that event is scheduled to occur
          private final Particle a, b;       // particles involved in event, possibly null
          private final int countA, countB;  // collision counts at event creation
                  
          
          // create a new event to occur at time t involving a and b
          public Event(double t, Particle a, Particle b) {
              this.time = t;
              this.a    = a;
              this.b    = b;
              if (a != null) countA = a.count();
              else           countA = -1;
              if (b != null) countB = b.count();
              else           countB = -1;
          }
  
          // compare times when two events will occur
          public int compareTo(Event that) {
              if      (this.time < that.time) return -1;
              else if (this.time > that.time) return +1;
              else                            return  0;
          }
          
          // has any collision occurred between when event was created and now?
          public boolean isValid() {
              if (a != null && a.count() != countA) return false;
              if (b != null && b.count() != countB) return false;
              return true;
          }
     
      }
  
  
  
     /********************************************************************************
      *  Sample client
      ********************************************************************************/
      public static void main(String[] args) {
  
          // remove the border
          StdDraw.setXscale(1.0/22.0, 21.0/22.0);
          StdDraw.setYscale(1.0/22.0, 21.0/22.0);
  
          // turn on animation mode
          StdDraw.show(0);
  
          // the array of particles
          Particle[] particles;
  
          // create N random particles
          if (args.length == 1) {
              int N = Integer.parseInt(args[0]);
              particles = new Particle[N];
              for (int i = 0; i < N; i++) particles[i] = new Particle();
          }
  
          // or read from standard input
          else {
              int N = StdIn.readInt();
              particles = new Particle[N];
              for (int i = 0; i < N; i++) {
                  double rx     = StdIn.readDouble();
                  double ry     = StdIn.readDouble();
                  double vx     = StdIn.readDouble();
                  double vy     = StdIn.readDouble();
                  double radius = StdIn.readDouble();
                  double mass   = StdIn.readDouble();
                  int r         = StdIn.readInt();
                  int g         = StdIn.readInt();
                  int b         = StdIn.readInt();
                  Color color   = new Color(r, g, b);
                  particles[i] = new Particle(rx, ry, vx, vy, radius, mass, color);
              }
          }
  
          // create collision system and simulate
          CollisionSystem system = new CollisionSystem(particles);
          system.simulate(10000);
      }
        
  }
  