import java.awt.*; // to get Graphics object. import java.net.*; // for getImage import java.applet.*; public class EarthCanvas extends Canvas implements Runnable { // This should be two interfering sine waves // so we can see the difference between group velocity // and phase velocity. Image newtmtn; MediaTracker tracker; int backgroundLoaded; Image offscreen; // This is the offscreen image so we can double buffer. 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. Rectangle oldBallRect,ballRect; static final int ballRadius = 5; double velocity; // Requested velocity for the next shot. double e; // The eccentricity for this shot. double angle,time; // The current position of the projectile. double delta_time; // How fast the simulation is. int centerx, centery, startRadius, earthRadius; AudioClip fire,crash,oops; Polygon mountain; // The constructor makes the curve object with default values. // They can be resized. EarthCanvas(Image mountainImage,AudioClip afire,AudioClip acrash, AudioClip aoops) { newtmtn = mountainImage; fire = afire; crash = acrash; oops = aoops; backgroundLoaded = 0; tracker = new MediaTracker(this); tracker.addImage(newtmtn,0); if (tracker.statusID(0,true)!=MediaTracker.COMPLETE) { imagewidth = 400; imageheight = 400; } else { imagewidth = newtmtn.getWidth(this); imageheight = newtmtn.getHeight(this); } delta_time = 0.05; time = 0.0; angle = Math.PI/2.0; velocity = -0.2; initializeSizes(); initializeShot(velocity); } public void initializeSizes() { centerx = imagewidth/2; centery = imageheight/2; double srad = centery*0.67657; startRadius = (int) (srad/(1-e)); earthRadius = (int) (centery*0.56514); mountain = new Polygon(); double delta = .2; mountain.addPoint((int)(centerx-earthRadius*Math.sin(delta)),(int)(centery-earthRadius*Math.cos(delta))); mountain.addPoint((int)(centerx+earthRadius*Math.sin(delta)),(int)(centery-earthRadius*Math.cos(delta))); // mountain.addPoint(centerx-10,(int)(centery-earthRadius)); // mountain.addPoint(centerx+10,(int)(centery-earthRadius)); mountain.addPoint(centerx,(int)(centery-srad)); // System.err.println("center: ("+centerx+","+centery+") startRadius: "+startRadius); } public void initializeShot(double velocity) { angle = Math.PI/2.0; e = velocity; // This should be a function of the velocity and startRadius. startRadius = (int) (centery*0.7/(1-e)); if (fire!=null) fire.play(); } // An 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 newvelocity) { velocity = newvelocity; } // 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(imagewidth,imageheight); return d; } public Dimension getMinimumSize() { Dimension d = new Dimension(imagewidth,imageheight); 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 fireButton() { angle = Math.PI/2.0; initializeShot(velocity); if (animator != null) { ; } else { please_stop = false; enable(); } } public void stopButton() { if (animator != null) { please_stop = 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(); // System.err.println("Killed an offscreen image."); // 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; } initializeSizes(); // Create an image from the size of this canvas using a method // from the component class, I think. offscreen = createImage(imagewidth, imageheight); ballRect = new Rectangle(centerx-ballRadius,centery-ballRadius,2*ballRadius,2*ballRadius); oldBallRect = new Rectangle(centerx-ballRadius,centery-ballRadius,2*ballRadius,2*ballRadius); backgroundLoaded = 0; } Graphics g = offscreen.getGraphics(); if (tracker.statusID(0,true)!=MediaTracker.COMPLETE) { //Fill background // System.err.println("Graphics not yet loaded."); g.setColor(Color.white); g.clearRect(0,0,imagewidth,imageheight); g.drawString("Please wait. Loading graphics.",20,160); repaint(); } else { // System.err.println("Time stepping. angle: "+angle+" time: "+time); double radius = (1-e*e)/(1+e*Math.sin(angle)); double delta_angle=Math.sqrt(1-e*e)/(radius*radius)*delta_time; time=time+delta_time; angle = angle-delta_angle; // Minus so we can shoot right. int x = centerx + (int) (startRadius*radius*Math.cos(angle)); int y = centery - (int) (startRadius*radius*Math.sin(angle)); // System.err.println("x: "+x+" y: "+y); // int x = centerx + (int) (startRadius*Math.cos(time)); // int y = centery - (int) (startRadius*Math.sin(time)); if (x>imagewidth || x<0 || y>imageheight || y<0) { please_stop = true; oops.play(); } else if (radius*startRadius1) { g.clipRect(clipping.x,clipping.y,clipping.width,clipping.height); } else { backgroundLoaded = backgroundLoaded+1; // System.err.println("Loading bg--x: "+imagewidth+" y: "+imageheight); clipping.reshape(0,0,imagewidth,imageheight); g.clipRect(0,0,imagewidth,imageheight); } // g.drawImage(newtmtn,0,0,imagewidth,imageheight,this); g.setColor(Color.white); g.fillRect(clipping.x,clipping.y,clipping.width,clipping.height); g.setColor(Color.blue); g.fillOval(centerx-earthRadius,centery-earthRadius,2*earthRadius,2*earthRadius); g.setColor(Color.green); g.fillPolygon(mountain); // g.setColor(Color.green); // g.fillOval(centerx-2,centery-2,4,4); g.setColor(Color.red); g.fillOval(ballRect.x,ballRect.y,ballRadius*2,ballRadius*2); // Request that the system call paint sometime soon. // It will invalidate only the specified clipping rectangle. repaint(clipping.x,clipping.y,clipping.width,clipping.height); } // wait a tenth of a second, then draw it again! try { Thread.sleep(Math.max(0,startTime-System.currentTimeMillis())); } catch (InterruptedException e) { ; } } animator = null; } }