// File: Particle.java 
// Written by Michael Main (main@colorado.edu) -- Mar 16, 1999

import java.awt.*;          // Provides Graphics, Rectangle
import java.util.Vector;

class Particle extends Thread
{
    // Background color used to erase a moving Particle:
   public static final Color BACKGROUND = Color.lightGray;

   // Particle constants: (1) Milliseconds to sleep between movements; (2) A
   // dampening factor to reduce the speed after each movement; (3) Multiplicative
   // factors to increase the electric charge force and repulsive force.
   private static final int SLEEP_PERIOD = 20;
   private static final double DAMPENING_FACTOR = 0.99;
   private static final double ELECTRIC_CHARGE_FACTOR = 16000;
   private static final double REPULSIVE_FORCE_FACTOR = 320000;

   // Fixed properties about this Particle:
   private Vector particles;     // All the Particles that exert forces on this one
   private double mass;          // This Particle's mass as multiple of an electron
   private double charge;        // This Particle's charge, in units of e
   private Color color;          // This Particle's color (blue or red) 
   private int diameter;         // This Particle's diameter in pixels
   private double speed_limit;   // Maximum number of pixels for each movement

   // Properties about the Graphics area where this Particle is drawn: 
   private Graphics g;           // This Particle drawn here after each movement
   int left, right;              // Min and max x coordinate for drawing Particle
   int top, bottom;              // Min and max y coordinate for drawing Particle

   // Changing properties about this Particle:
   private double x, y;          // Current position of center corner
   private double dx, dy;        // Current x and y speeds in pixels per 100ms.

   public Particle(Graphics g, Vector particles, double mass, double charge)
   {
      // Copy the parameters into member variables for later use:       
      this.g = g;
      this.particles = particles;
      this.mass = mass;
      this.charge = charge;

      // Set the fixed properties of this Particle. These are determined by
      // formulas involving the mass and charge.
      diameter = 5 + (int) (0.67 * Math.pow(mass, 0.33));
      if (charge < 0)      color = Color.blue;
      else if (charge > 0) color = Color.red;
      else                 color = Color.green;
      speed_limit = 800.0 / (diameter * diameter);

      // Set the left, right, top, bottom bounds of the Graphics area:
      set_bounds( );

      // Provide a random initial position and zero initial speed:
      x = rand(left, right);  
      y = rand(top, bottom);
      dx = 0; 
      dy = 0;
   }

   public static double electric_charge_force(double d)
   {
      return ELECTRIC_CHARGE_FACTOR/(d*d);
   }
   
   public static double repulsive_force(double d)
   {
      return -REPULSIVE_FORCE_FACTOR/(d*d*d);
   }
   
   private void set_bounds( )
   { 
      Rectangle bounds = g.getClipBounds( );
      left = bounds.x + diameter;
      right = left + bounds.width - 2*diameter;
      top = bounds.y + diameter;
      bottom = top + bounds.height - 2*diameter;
   }
   
   public void run( )
   {
      int oldx, oldy;
      Particle other;
      int i;
      double force, distance, distx, disty, speed, acceleration;
      
      for (;;)
      {
         oldx = (int) x;
         oldy = (int) y;
         
         // Compute the new dx and dy:
         for (i = 0; i < particles.size( ); i++)
         {
            other = (Particle) particles.elementAt(i);
            distx = other.x - x;
            disty = other.y - y;
            distance = Math.sqrt(distx*distx + disty*disty);
            if (this != other)
            {
               force = -charge * other.charge * electric_charge_force(distance);
               
               force += repulsive_force(distance);
               acceleration = force / mass;   
               
               dx += acceleration * (distx / distance);
               dy += acceleration * (disty / distance);

            }
         }  
         
         dx *= DAMPENING_FACTOR;
         dy *= DAMPENING_FACTOR;
         
         // Lower the speed if it's gone too high:             
         speed = Math.sqrt(dx*dx + dy*dy)/speed_limit;
         if (speed > 1)
         {
            dx /= speed;
            dy /= speed;
         }

         // Compute the new coordinates:
         if ((y+dy <= bottom) && (y+dy >= top))
            y += dy;
         else
            dy = -dy;
         if ((x+dx <= right) && (x+dx > left))
            x += dx;
         else
            dx = -dx;
 
         set_bounds( );        
         if (x < left || x > right) x = rand(left, right);
         if (y < top || y > bottom) y = rand(top, bottom);
         
         // Redraw the old ball in the background color, and draw the new ball.
	 synchronized(g)
	 {
	     g.setColor(BACKGROUND);
	     g.fillOval(oldx, oldy, diameter, diameter);
	     g.setColor(color);
	     g.fillOval((int)x, (int)y, diameter, diameter);
         }
	 
         // Wait for the specified delay before drawing again:
         pause(SLEEP_PERIOD);
      }
   }
   
   public static void pause(long millisecs)
   {
      try 
      {  // Use the Thread.sleep method:
         sleep(millisecs);
      }
      catch (InterruptedException e)
      {
         // Resume
      }
   }
   
   public static int rand(int low, int high)
   {
      return low + (int) (Math.random( ) * (high - low + 1));
   }
}


