import java.awt.*; // to get Graphics object. public class WavePacketCanvas extends Canvas implements Runnable { // This should be two interfering sine waves // so we can see the difference between group velocity // and phase velocity. Image offscreen; // This is the offscreen image so we can double buffer. WavePacket curve = null; // The curve object knows the shape of the sines. int imagewidth = 200, imageheight = 100; // The canvas sizes. Thread animator = null; // This thread will run this object. boolean please_stop = false; // We only request a stop in case we are mid-draw. long startTime; // A guess at when we want the next redraw to begin. // The constructor makes the curve object with default values. // They can be resized. WavePacketCanvas() { imagewidth = 200; imageheight = 100; curve = new WavePacket(imagewidth,imageheight,12.0,2.0); } // A public function to allow people to change the velocity // of the longer wavelength. We pass this on to our curve object. public void setVelocity(double velocity) { curve.setVelocity(velocity); } public void setFrequency(double velocity) { curve.setFrequency(velocity); } // These two inform the layout what our minimum requirements are. // Without these, you may get a zero size object. public Dimension getPreferredSize() { Dimension d = new Dimension(150,50); return d; } public Dimension getMinimumSize() { Dimension d = new Dimension(150,50); return d; } // Start the animation public void enable() { super.enable(); // Start the thread. animator = new Thread(this); animator.start(); repaint(); } // Stop it. public void disable() { if (animator != null) animator.stop(); animator = null; super.disable(); } // This method draws the background and text at its current position. public void paint(Graphics g) { //Dimension d = this.size(); update(g); } public void update( Graphics g ) { // If the screen is invalid, don't redraw. Just copy from the // offscreen buffer. if (offscreen != null) { g = this.getGraphics(); g.drawImage(offscreen, 0, 0, this); } else { g.clearRect(0,0,imagewidth,imageheight); } } // Stop and start animating on mouse clicks. public boolean mouseDown(Event e, int x, int y) { if (e.target==this) { toggle(); } return true; } public void toggle() { // if running, stop it. Otherwise, start it. if (animator != null) please_stop = true; else { please_stop = false; enable(); } } // The body of the animator thread. public void run() { while(!please_stop) { // Mark the current time in case we take too long to process. startTime = System.currentTimeMillis()+100; Dimension d = this.size(); // Make sure the offscreen image is created and is the right size. if ((offscreen == null) || ((imagewidth != d.width) || (imageheight != d.height))) { // if (offscreen != null) offscreen.flush(); // Sometimes the image sizes are zero or negative before the frame // is drawn. if (d.width>1 && d.height>1) { imagewidth = d.width; imageheight = d.height; } // Create an image from the size of this canvas using a method // from the component class, I think. offscreen = createImage(imagewidth, imageheight); // Make our curve agree to the size. curve.setSize(imagewidth,imageheight); } // Compute does nothing but increment the timestep. curve.compute(); // Get the offscreen image. Graphics g = offscreen.getGraphics(); // Draw into the off-screen image. g.clearRect(0,0,d.width,d.height); curve.draw(g); // Request that the system call paint sometime soon. repaint(); // wait a tenth of a second, then draw it again! try { Thread.sleep(Math.max(0,startTime-System.currentTimeMillis())); } catch (InterruptedException e) { ; } } animator = null; } }