Java is an object-oriented programming language with a built-in application programming interface (API) that can handle graphics and user interfaces and that can be used to create applications or applets. Because of its rich set of API's, similar to Macintosh and Windows, and its platform independence, Java can also be thought of as a platform in itself. Java also has standard libraries for doing mathematics.
Much of the syntax of Java is the same as C and C++. One major difference is that Java does not have pointers. However, the biggest difference is that you must write object oriented code in Java. Procedural pieces of code can only be embedded in objects. In the following we assume that the reader has some familiarity with a programming language. In particular, some familiarity with the syntax of C/C++ is useful.
In Java we distinguish between applications, which are programs that perform the same functions as those written in other programming languages, and applets, which are programs that can be embedded in a Web page and accessed over the Internet. Our initial focus will be on writing applications. When a program is compiled, a byte code is produced that can be read and executed by any platform that can run Java.
To use this tutorial you should run and study each program as you read along, so that you can see how added features affect the programs.
public class Particle { double x, y, vx, vy, mass; }This class does not do anything (because it has no methods), but it can be used to describe a particle by its position, velocity, and mass. For simplicity, we will consider only two dimensions.
public class Particle { double x, y, vx, vy, mass; // these variables can be used by any method in the class // example of constructor method public Particle(double x, double y, double vx, double vy, double mass) { /* Use this keyword to make explicit that a method is accessing its own variables. */ this.x = x; // set instance variable x equal to value of x in parameter list this.y = y; this.vx = vx; this.vy = vy; this.mass = mass; } }The constructor Particle creates an object of the Particle class by specifying five parameters: the initial position and velocity of a particle and the value of its mass. We say that Particle is the constructor for the Particle class.
We have used the following properties of Java:
operator | example | meaning |
---|---|---|
+= | x += y | x = x + y |
-= | x -= y | x = x - y |
*= | x *= y | x = x*y |
/= | x /= y | x = x/y |
%= | x %= y | x = x % y |
The modulus operator % returns the remainder of a division. For example, 5 % 3 returns 2. The increment operator ++ works as follows:
i++;is equivalent to
i = i + 1;Similarly, the decrement operator -- works as follows:
i--;is equivalent to
i = i - 1;
double distance; distance = 10.0; int x; x = (int)distance; // example of a type castWhat would be the value of x in the above example if distance = 9.9?
public class Particle { double x, y, vx, vy, mass; // examples of multiple constructors public Particle() { this(0.0,0.0); } public Particle(double x, double y) { this(x,y,1.0); } public Particle(double x, double y, double m) { this(x,y,0.0,0.0,m); } public Particle(double x, double y, double vx, double vy) { this(x,y,vx,vy,1.0); } public Particle(double x, double y, double vx, double vy, double mass) { this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.mass = mass; } }
We next give an example of a Particle class with methods for computing the weight of a particle and its distance from the origin. We will omit the velocity components for now.
public class Particle { double x, y, mass; public Particle() { } public Particle(double x, double y) { this(x,y,1); } public Particle(double x, double y, double mass) { this.x = x; this.y = y; this.mass = mass; } public double getWeight() { return 9.8*mass; } public double distanceFromOrigin() { return Math.sqrt(x*x + y*y); } }
The following program creates two objects of type Particle.
public class MyApplication { public static void main(String[] args) { /* Need an instance of class Particle - a particle object; assign it to the variable a which is of type Particle. */ Particle a = new Particle(1.0,1.0); System.out.println("Distance of particle a from origin = " + a.distanceFromOrigin()); System.out.println("Mass of particle a = " + a.mass); Particle b = new Particle(); b.x = 2.0; b.y = 3.0; b.mass = 3; System.out.println("Distance of particle b from origin = " + b.distanceFromOrigin()); System.out.println("Mass of particle b = " + b.mass); System.out.println("Weight of particle b = " + b.getWeight()); } }
One reason to make a variable private is to restrict access to it. Access becomes an issue for threading which refers to the sequence of execution of program code. For example, we would want to avoid changing the value of a variable while another portion of the code is trying to read it. Make x private and see what happens when you run MyApplication.
If we declare x, y, and mass as private variables, we have to include explicit methods in Particle to allow another class to access the variable information in Particle. For simplicity, we will consider only the variables x and mass. Our Particle class becomes:
public class Particle { private double x; private double mass; public Particle(double x) { this(x, 1.0); } public Particle(double x, double mass) { this.x = x; this.mass = mass; } public double getX() { return x; } public void setX(double newX) { x = newX; } public double getWeight() { return 9.8*mass; } public double distanceFromOrigin() { return Math.abs(x); } }
Note the new methods getX and setX. They are used in the following.
public class MyApplication { public static void main(String[] args) { Particle p = new Particle(10.0, 2.0); System.out.println("Distance of particle from origin = " + p.distanceFromOrigin()); System.out.println("x-coordinate = " + p.getX()); // would have written p.x if x were public System.out.println(weight = " + p.getWeight()); p.setX(3.0); // change value of x System.out.println("new x-coordinate = " + p.getX()); System.out.println("new distance from origin = " + p.distanceFromOrigin()); } }In the following, we will not make variables such as x and y private, because they will be used in so many classes.
public class Particle { double x, y, mass; public Particle(double x, double y) { this(x, y, 1.0); } public Particle(double x, double y, double mass) { this.x = x; this.y = y; this.mass = mass; } public double distanceFrom(Particle a) { double r2 = Math.pow(this.x - a.x, 2) + Math.pow(this.y - a.y,2); return Math.sqrt(r2); } public double distanceFromOrigin() { return Math.sqrt(x*x + y*y); } }
public class MyApplication { public static void main(String[] args) { Particle a = new Particle(5.0, 7.0); Particle b = new Particle(1.0, 2.0); System.out.println("Distance of a from b = " + a.distanceFrom(b)); System.out.println("Distance of b from a = " + b.distanceFrom(a)); } }
public class ChargedParticle extends Particle { // magnitude of electron charge in Coulombs public static final double ELQ = 1.602e-19; private int charge; public ChargedParticle(double x,double y, double mass, int charge) { super(x, y, mass); // constructor for Particle this.charge = charge; } public int getCharge() { return charge*ELQ; } public void setCharge(int newCharge) { charge = newCharge; } public static int netCharge(ChargedParticle a, ChargedParticle b) { return a.charge + b.charge; } }An example of the use of this new class is given below.
public class MyApplication { public static void main(String[] args) { // particle charge is expressed in units of electron charge ChargedParticle a = new ChargedParticle(10.0,0,0,1); System.out.println("distance of a from origin = " + a.distanceFromOrigin()); System.out.println("charge of a = " + a.getCharge()); System.out.println("charge of a in coulombs = " + ChargedParticle.ELQ*a.getCharge()); ChargedParticle b = new ChargedParticle(-5.0,0,0,-1); System.out.println("distance of b from origin: " + b.distanceFromOrigin()); System.out.println("charge of b = " + b.getCharge()); System.out.println("net charge of a and b = " + ChargedParticle.netCharge(a,b)); b.setCharge(3); System.out.println("new charge of b = " + b.getCharge()); System.out.println("net charge of a and b = " + ChargedParticle.netCharge(a,b)); } }Note how the (named) constant ELQ, the charge of an electron, has been introduced.
public class StaticTest { public static int x = 0; public int y = 0; public String getCoordinates() { return "x = " + x + ", y = " + y; } public static void main(String[] args) { StaticTest a = new StaticTest(); StaticTest b = new StaticTest(); a.x = 5; // static variable a.y = 12; // instance variable System.out.println(a.getCoordinates()); // outputs 5, 12 b.x = 7; b.y = 13; System.out.println(a.getCoordinates()); // outputs 7, 12 System.out.println(b.getCoordinates()); // outputs 7, 13 StaticTest.x = 2; System.out.println(a.getCoordinates()); // outputs 2, 12 System.out.println(b.getCoordinates()); // outputs 2, 13 } }The (static) x member belongs to class Test and hence belongs to a and b. There is only one variable x and thus the value of x is always the one most recently specified. In contrast, y belongs to the instances a and b of the class Test and hence a.y and b.y are distinct variables.
The String type is unique among all classes because it is the only class to support an operator: the + operator can be used to concatenate strings. When a number is concatenated with a string, the number is converted to a string. Because a String variable is an object, it has methods. (The class String is different than other classes.) Some of its associated methods are summarized in Table 4.
An array must be created before it can be used. We first declare a reference or "handle" to an array that permits Java to locate the object in memory when it is needed. Then we create an array object to assign to the reference using the new operator. For example, we can write
An array object may be created and initialized when its reference is declared. For example,
Variables have either class scope or block scope. Methods and the instance variables of a class have class scope. Class scope begins at the opening left brace and ends at the closing right brace of the class definition. Class scope allows any method in the class to directly invoke any other method in the class and to directly access any instance variable of the class. In effect, instance variables are global variables within a class. Instance variables can be used to communicate between methods in a class or to retain information between calls of a given method in the class. A block is a compound statement and consists of all the statements between an opening and closing brace. Variables defined inside a block have block scope. Variables defined within a block have block scope and are visible within the block; they are not visible outside the block.
A variable can have the same name as an instance variable as an instance variable or method in the class in which the method is defined. In this case the instance variable is hidden from the method by the local variable. Example:
Note that {\tt If} statements are the same as in C/C++ except that
Java uses a boolean value to condition the execution. Summary of relational operators.
The step method implements the Euler-Richardson integration
algorithm. We need to compute the acceleration at the beginning of
the interval the first time the algorithm is used. Then we can
use the acceleration computed at the end of the previous interval.
Note how this is used in the step method to refer
to the Particle itself; in Force we use the
argument Particle to get the particle's mass.
The class that draws the trajectory is given below.
When the window of another application covers the
frame, Java automatically calls paint when the user returns to our original program. This action allows us to see the contents of the original window. However, in the above program it means that the trajectory must be recomputed. A better approach is to first draw the trajectory to an offscreen image buffer and then blast the latter to the screen. As long as the buffer is saved, no new calculations need to be done. First
we will show how this is done for the Simulation class and then we will
show an example where it is more useful.
// Image and Graphics do not use constructors.
Arrays
An array is a special object containing a group of contigious memory locations that have the same name and the same type and a separate variable containing an integer constant equal to the number of array elements. The elements of Java arrays are numbered starting from 0.
double x[]; // create an array reference
x = new double[5]; // create array object
Or we can create an array reference and an array object on a single line:
double x[] = new double[5];
The number of elements in the array x is x.length. The elements of the array are written as x[0], x[1], x[2], x[3], x[4].
double x[] = {1.0, 1.4, 1.6, 1.8, 3.0};
It is a good idea to declare array sizes using named constants (final variables) so that the length of the arrays can be easily changed.
final int ARRAY_SIZE = 1000;
double x[] = new double[ARRAY_SIZE];
Two-dimensional arrays
A two-dimensional array is implemented by creating a one-dimensional array each of whose elements is also an array. We first declare a reference to an array of arrays and then create the individual arrays associated with each element. For example
double x[][]; / create reference to an array of arrays
Then we can write for example,
x = new double[3][5]; create array objects
Methods and Scope
The general form of a method definition is
return-value-type method-name(parameter list)
{
declarations and statements
(return statement)
}
public class TestPassByValue
{
public static void main(String[] args)
{
// instantiate a TestPassByValue object
TestPassByValue t = new TestPassByValue();
int i = 1; // local variable
System.out.println("value of i before test = " + i);
// pass value of local variable
int j = t.test(i);
System.out.println("value of i after test = " + i);
System.out.println("value of j = " + j);
j = t.test2(j);
System.out.println("value of i after test2 = " + i);
System.out.println("value of j after test2 = " + j);
}
public int test (int i)
{
i = ++i; // same as i = i + 1;
System.out.println("value of i in test = " + i);
return i;
}
public int test2 (int k)
{
int i = k; // i refers to instance variable
System.out.println("value of k = " + k);
i = ++i;
System.out.println("value of i in test2 = " + i);
return i;
}
}
public class Point
{
// define instance data
public double x,y; // instance variables
// define constructors
public Point()
{
x = 1; // same variables as instance variables
y = 1;
}
public Point (double x, double y)
{
this.x = x; // this.x refers to instance variable
this.y = y;
this.x++;
System.out.println("this.x = " + this.x);
System.out.println("x = " + x); // parameter variable
}
public Point (double x, double y, boolean dummy)
{
x++;
System.out.println("x = " + x);
}
}
public class TestPoint
{
public static void main(String[] args)
{
/* Need an instance of class Particle - a particle object;
assign it to the variable a which is of type Particle. */
Point a = new Point(2.0,1.0);
System.out.println("x = " + a.x);
Point b = new Point();
b.x = 3.0;
System.out.println("xb = " + b.x);
}
}
Simple Graphics
A powerful feature of Java is its ability to do graphics relatively simply. We next introduce several methods in the Particle class to draw a representation of the particle.
import java.awt.*;
public class Particle
{
double x,y;
Color color; // part of AWT package
public Particle(double x, double y)
{
// constructor with color variable
this.x = x;
this.y = y;
color = Color.blue; // define default color
}
// Draw representation of particle in given graphics context
public void draw(Graphics g)
{
Color oldColor = g.getColor();
g.setColor(color);
g.fillOval((int)x, (int)y, 12, 12);
g.setColor(oldColor);
}
public Color getColor()
{
return color;
}
public void setColor(Color newColor)
{
color = newColor;
}
public void move(double dx, double dy)
{
x += dx; // same as x = x + dx;
y += dy;
}
}
The following simple class draws the particle:
import java.awt.*;
public class ParticleDrawer extends Frame
{
public static void main(String[] args)
{
// Create new frame
ParticleDrawer frame = new ParticleDrawer();
}
// Construct ParticleDrawer frame
public ParticleDrawer()
{
// predefined methods
setSize(512, 342); // units of coordinate system in pixels
setVisible(true);
}
// Paint particles
public void paint(Graphics g)
{
Particle p = new Particle(3.1, 4.1);
p.draw(g);
p.move(26.5, 35.8);
p.setColor(Color.red);
p.draw(g);
}
}
As an example we draw the trajectory of a projectile. To do so we add to Particle the method step
to integrate the equations of motion for one time step. The
arguments of step are the time step and the Force on the
particle, which is defined in another class. We also add other
useful methods.
public class Particle
{
private double x,y,vx,vy,ax,ay;
private double mass = 1.0;
private boolean firststep = true;
public Particle(double x, double y, double vx, double vy)
{
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
}
public double getMass()
{
return mass;
}
public void setMass(double mass)
{
this.mass = mass;
}
public double getX()
{
return x;
}
public double getY()
{
return y;
}
public double getVx()
{
return vx;
}
public double getVy()
{
return vy;
}
public double getAx()
{
return ax;
}
public double getAy()
{
return ay;
}
public void setX(double x)
{
this.x = x;
}
public void setY(double y)
{
this.y = y;
}
public void setVx(double vx)
{
this.vx = vx;
}
public void setVy(double vy)
{
this.vy = vy;
}
public void step(double dt, Force f)
{
if (firststep)
{
ax = f.getfx(x,y,vx,vy,this)/mass; // acceleration at beginning of interval
ay = f.getfy(x,y,vx,vy,this)/mass;
firststep = false;
}
// Euler-Richardson algorithm
double vxm = vx + 0.5*ax*dt; // velocity at middle of interval
double vym = vy + 0.5*ay*dt;
double xm = x + 0.5*vx*dt; // position at middle of interval
double ym = y + 0.5*vy*dt;
double axm = f.getfx(xm,ym,vxm,vym,this)/mass;
double aym = f.getfy(xm,ym,vxm,vym,this)/mass;
vx += axm*dt; // velocity at end of interval
vy += aym*dt;
x += vxm*dt; // position at end of interval
y += vym*dt;
ax = f.getfx(x,y,vx,vy,this)/mass; // acceleration at end of interval
ay = f.getfy(x,y,vx,vy,this)/mass;
}
}
public class Force
{
private final static double g = 9.8; // not convention
double b = 0; // used in drag force
public void setb(double b)
{
this.b = b;
}
public double getfx(double x, double y, double vx, double vy, Particle p)
{
return -b*vx;
}
public double getfy(double x, double y, double vx, double vy, Particle p)
{
return -b*vy - g*p.getMass();
}
}
import java.awt.*;
public class Simulation extends Frame
{
public static void main(String[] args)
{
Simulation sim = new Simulation(); // set up window for application
}
public Simulation()
{
setSize(512,342);
setVisible(true);
}
}
public void paint(Graphics g)
{
setBackground(Color.white);
calculateTrajectory(g);
}
private void calculateTrajectory(Graphics g)
{
final double tmax = 10.0;
final double dt = 0.5;
Particle p = new Particle(0.0, 200.0, 40.0, 25.0);
Force f = new Force();
g.setColor(Color.blue);
double time = 0.0;
while (time < tmax)
{
// draw circle of diameter 10 pixels. note use of casting
g.drawOval((int)p.getX(), getSize().height - (int)p.getY(), 10, 10);
p.step(dt,f);
time += dt;
}
}
}
for (int i = 0; i < 10; i++)
statement;
or
for (int i = 0; i < 10; i++)
{
statement 1;
...
statement n;
}
Semicolons are used to separate the fields inside the
parenthesis. The first field initializes a variable. Usually, the
variable is also declared here. The second field determines under
what condition the loop will continue. The third field lists how the
variable in the first field changes at the end of each pass through
the loop. More information about loops.
Offscreen buffers
import java.awt.*;
public class Simulation extends Frame
{
Image offscreen; // make image class variable so available to all methods
public static void main(String[] args)
{
Simulation sim = new Simulation(); // set up window for application
}
public Simulation()
{
setSize(512, 342);
setVisible(true);
offscreen = createImage(getSize().width, getSize().height);
calculateTrajectory();
}
public void paint(Graphics g)
{
setBackground(Color.white);
g.drawImage(offscreen, 0, 0, this); // draw image onto screen
}
public void calculateTrajectory()
{
final double tmax = 10.0;
final double dt = 0.5;
Graphics g = offscreen.getGraphics();
g.setColor(Color.blue);
Particle p = new Particle(0.0, 200.0, 40.0, 25.0);
Force f = new Force();
double time = 0.0;
while (time < tmax)
{
g.drawOval((int)p.getX(), getSize().height - (int)p.getY(), 10, 10);
p.step(dt,f);
time += dt;
}
}
}
import java.awt.*; import java.awt.event.*; // needed for actionListener class ParameterInput extends Frame { TextField tf; Label lb; Simulation sim; public FrictionInput (Simulation sim) // pass address of object { this.sim = sim; setUpFrame(); } public void setUpFrame() { setTitle("Friction Input"); setSize(200,100); setLocation(400,50); // location for frame setLayout(null); // not using any layout manager lb = new Label("Friction Coefficient"); // new label for textfield lb.setSize(150,20); lb.setLocation(30,70); add(lb); // add label tf = new TextField(); // new textfield tf.addActionListener(new actionTF()); // add listener tf.setSize(50,20); tf.setLocation(30,40); add(tf); // add textfield setVisible(true); } class actionTF implements ActionListener // example of Internal class // user has to tell ActionLister what to do so have to implement it. { public void actionPerformed(ActionEvent e) { Double R = new Double(tf.getText().trim()); // convert text to number // trim gets rid of extra zeros sim.calculateTrajectory(R.doubleValue()); } } }
The following modification of Simulation uses FrictionInput.
import java.awt.*; public class Simulation extends Frame { private Image offscreen; private int counter = 0; // used to change color for each trajecto public static void main(String[] args) { Simulation sim = new Simulation(); // set up window for application FrictionInput fi = new FrictionInput(sim); // set up second window } public Simulation() // constructor { setSize(512, 342); setVisible(true); offscreen = createImage(getSize().width, getSize().height); } public void paint(Graphics g) { setBackground(Color.white); g.drawImage(offscreen, 0, 0, this); // draw image on screen } public void calculateTrajectory(double b) { final double tmax = 10.0; final double dt = 0.5; Graphics g = offscreen.getGraphics(); // create buffer changeColor(g); Particle p = new Particle(0.0, 200.0, 40.0, 25.0); Force f = new Force(); f.setb(b); double time = 0.0; while (time < tmax) { g.drawOval((int)p.getX(), getSize().height - (int)p.getY(), 10, 10); p.step(dt,f); time += dt; } repaint(); } public void changeColor(Graphics g) { switch(counter++) { case 0: g.setColor(Color.red); break; case 1: g.setColor(Color.blue); break; case 2: g.setColor(Color.green); break; case 3: g.setColor(Color.yellow); break; case 4: g.setColor(Color.magenta); break; case 5: g.setColor(Color.orange); break; case 6: g.setColor(Color.cyan); break; case 7: g.setColor(Color.pink); break; default: g.setColor(Color.black); } } }
if (x < 0) { y = 20; x = 10; } if (x < 0) y = 10; // braces not needed for single statement else y = 20; if (x < 0) y = 10; else if (x > 0) y = 20; else y = 30;
We now return to a single trajectory and discuss animation.
Instead of showing the trajectory we will simply show a ball as it
moves. The following code does this so that there is very little
flicker. We have reduced the time step so that the motion of the ball
will be slowed down.
import java.awt.*; public class Simulation extends Frame { Image offscreen; public static void main(String[] args) { Simulation sim = new Simulation(); // set up window for application sim.calculateTrajectory(); } public Simulation() { // constructor setSize(512, 342); setVisible(true); offscreen = createImage(getSize().width, getSize().height); } public void paint(Graphics g) { setBackground(Color.white); g.drawImage(offscreen,0,0,this); } public void calculateTrajectory() { final double tmax = 10.0; final double dt = 0.005; Graphics b = offscreen.getGraphics(); Particle p = new Particle(0.0, 200.0, 40.0, 25.0); Force f = new Force(); double time = 0.0; while (time < tmax) { Rectangle oldRect = new Rectangle((int)p.getX(),getSize().height -(int)p.getY(),11,11); p.step(dt,f); time += dt; Rectangle newRect = new Rectangle((int)p.getX(),getSize().height -(int)p.getY(),11,11); // new region of ball Rectangle r = newRect.union(oldRect); // new plus old region // b is buffer. key to avoid flicker is not to call repaint b.clearRect(r.x,r.y,r.width,r.height); // clear new plus old region on buffer b.fillOval((int)p.getX(), getSize().height - (int)p.getY(), 10, 10); Graphics g = getGraphics(); g.drawImage(offscreen,0,0,this); } } }
The basic strategy used above is to define three rectangles. One containing the old position of the ball, one containing the new position, and one which is the union of the two. Then we clear the buffer of this latter rectangle, and draw our new ball. We then draw our image to the screen. Note how we grab the Graphics object for the screen within calculateTrajectory instead of using repaint. This avoids some of the flickering that would occur from clearing the Frame.
import java.awt.*; import java.awt.event.*; public class Simulation extends Frame implements MouseListener, Runnable { Image offscreen; Graphics gb; // graphics buffer Particle p; Force f; boolean running = false; Thread runner; public static void main(String[] args) { Simulation sim = new Simulation(); // set up window for application } public Simulation() { // constructor setSize(512, 342); setVisible(true); offscreen = createImage(getSize().width, getSize().height); p = new Particle(0.0, 200.0, 40.0, 25.0); f = new Force(); gb = offscreen.getGraphics(); start(); // method associated with thread addMouseListener(this); // this refer to frame class } public void paint(Graphics g) { setBackground(Color.white); g.drawImage(offscreen,0,0,this); } public void start() { runner = new Thread(this); // this refers to frame runner.start(); } public void stop() { runner.stop(); } public void run() { while(true) { try {Thread.sleep(5);} // delay 5 msec between updates catch (InterruptedException e){}; // would catch the moue click if(running) calculateTrajectory(); } } public void mouseClicked(MouseEvent e) { // see coordinates Graphics g = getGraphics(); g.clearRect(0,0,getSize().width, getSize().height); g.fillOval((int)p.getX(), getSize().height - (int)p.getY(), 10, 10); g.drawString("x = " + String.valueOf(p.getX()),e.getX(),e.getY()); g.drawString("y = " + String.valueOf(p.getY()),e.getX(),e.getY() + 30); running = false; } public void mouseEntered(MouseEvent e) {running = true;} // move inside frame, runs; otherwise stops public void mouseExited(MouseEvent e) {running = false;} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void calculateTrajectory() { // should be draw trajectory private final double dt = 0.005; Rectangle oldRect = new Rectangle((int)p.getX(),getSize().height -(int)p.getY(),11,11); p.step(dt, f); time += dt; Rectangle newRect = new Rectangle((int)p.getX(),getSize().height -(int)p.getY(),11,11); Rectangle r = newRect.union(oldRect); gb.clearRect(r.x,r.y,r.width,r.height); gb.fillOval((int)p.getX(), getSize().height - (int)p.getY(), 10, 10); Graphics g = getGraphics(); g.drawImage(offscreen,0,0,this); } }
We have modified calculateTrajectory so that it only computes and draws one step. We have eliminated the while statement. Instead control of the trajectory is given to the run method of the Thread runner. The try catch statement is used to interrupt the Thread for 5 milliseconds to check to see if any event occurred. The MouseListener checks for events. In this case we can stop the motion by moving the mouse outside the Frame, and start it again by moving it into the Frame and start it again by moving it into the Frame. If the mouse is clicked, we write the x and y coordinates of the ball at the locatiuon of the mouse click, redraw the ball, and stop the motion. Note that the interface methods mousePressed and mouseReleased must be defined even though they do not do anything in this example.
Please send comments and corrections to Jan Tobochnik, jant@kzoo.edu or Harvey Gould, hgould@physics.clarku.edu.
Updated 20 July 2000.