001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package algs61; // section 6.1
import stdlib.*;
import java.awt.Color;
/* ***********************************************************************
 *  Compilation:  javac Particle.java
 *
 *  A particle moving in the unit box with a given position, velocity,
 *  radius, and mass.
 *
 *************************************************************************/

public class Particle {
  private static final double INFINITY = Double.POSITIVE_INFINITY;

  private double rx, ry;    // position
  private double vx, vy;    // velocity
  private final double radius;    // radius
  private final double mass;      // mass
  private final Color color;      // color
  private int count;        // number of collisions so far


  // create a new particle with given parameters
  public Particle(double rx, double ry, double vx, double vy, double radius, double mass, Color color) {
    this.vx = vx;
    this.vy = vy;
    this.rx = rx;
    this.ry = ry;
    this.radius = radius;
    this.mass   = mass;
    this.color  = color;
  }

  // create a random particle in the unit box (overlaps not checked)
  public Particle() {
    rx     = Math.random();
    ry     = Math.random();
    vx     = 0.01 * (Math.random() - 0.5);
    vy     = 0.01 * (Math.random() - 0.5);
    radius = 0.01;
    mass   = 0.5;
    //color  = Color.BLACK;
    color  = new Color (StdRandom.uniform(200), StdRandom.uniform(200), StdRandom.uniform(200));
  }

  // updates position
  public void move(double dt) {
    rx += vx * dt;
    ry += vy * dt;
  }

  // draw the particle
  public void draw() {
    StdDraw.setPenColor(color);
    StdDraw.filledCircle(rx, ry, radius);
  }

  // return the number of collisions involving this particle
  public int count() { return count; }


  // how long into future until collision between this particle a and b?
  public double timeToHit(Particle b) {
    Particle a = this;
    if (a == b) return INFINITY;
    double dx  = b.rx - a.rx;
    double dy  = b.ry - a.ry;
    double dvx = b.vx - a.vx;
    double dvy = b.vy - a.vy;
    double dvdr = dx*dvx + dy*dvy;
    if (dvdr > 0) return INFINITY;
    double dvdv = dvx*dvx + dvy*dvy;
    double drdr = dx*dx + dy*dy;
    double sigma = a.radius + b.radius;
    double d = (dvdr*dvdr) - dvdv * (drdr - sigma*sigma);
    // if (drdr < sigma*sigma) StdOut.println("overlapping particles");
    if (d < 0) return INFINITY;
    return -(dvdr + Math.sqrt(d)) / dvdv;
  }

  // how long into future until this particle collides with a vertical wall?
  public double timeToHitVerticalWall() {
    if      (vx > 0) return (1.0 - rx - radius) / vx;
    else if (vx < 0) return (radius - rx) / vx;
    else             return INFINITY;
  }

  // how long into future until this particle collides with a horizontal wall?
  public double timeToHitHorizontalWall() {
    if      (vy > 0) return (1.0 - ry - radius) / vy;
    else if (vy < 0) return (radius - ry) / vy;
    else             return INFINITY;
  }

  // update velocities upon collision between this particle and that particle
  public void bounceOff(Particle that) {
    double dx  = that.rx - this.rx;
    double dy  = that.ry - this.ry;
    double dvx = that.vx - this.vx;
    double dvy = that.vy - this.vy;
    double dvdr = dx*dvx + dy*dvy;             // dv dot dr
    double dist = this.radius + that.radius;   // distance between particle centers at collison

    // normal force F, and in x and y directions
    double F = 2 * this.mass * that.mass * dvdr / ((this.mass + that.mass) * dist);
    double fx = F * dx / dist;
    double fy = F * dy / dist;

    // update velocities according to normal force
    this.vx += fx / this.mass;
    this.vy += fy / this.mass;
    that.vx -= fx / that.mass;
    that.vy -= fy / that.mass;

    // update collision counts
    this.count++;
    that.count++;
  }

  // update velocity of this particle upon collision with a vertical wall
  public void bounceOffVerticalWall() {
    vx = -vx;
    count++;
  }

  // update velocity of this particle upon collision with a horizontal wall
  public void bounceOffHorizontalWall() {
    vy = -vy;
    count++;
  }

  // return kinetic energy associated with this particle
  public double kineticEnergy() { return 0.5 * mass * (vx*vx + vy*vy); }
}