import java.awt.*; /** Program: TheNewton Purpose: animation and 2D graphics... @author: Paul Garrett, garrett@math.umn.edu @version: Sat, 20 Sept 1997 */ /* * Threaded, html-parameter configurable * * Allows html parameters to set speed, how close an approx before stopping * background color * * _and_ the coefficients of the cubic in question.... * with defaults in all cases */ public class TheNewton extends java.applet.Applet implements Runnable { /* * The first section just below is boilerplate set up for the basic * drawing and timing... */ final int BIG = 200; // truncation limit on function values to // avoid screen wrap-around due to overflow... Thread timerThread; /* the following are read in as parameters from the HTML page */ int background_color; // int to specify hex color int pauseAmount; // int specifying milliseconds between steps double approxThreshhold; // how close to get before stopping iteration final int height = 320; // this is for low-res browsers final int width = 550; // even for low-res... final int drawHeight = 310; final int drawWidth = 540; final int inset = 5; final int inc = 4; final int range = 135; // want inc * range = drawWidth or so int xaxInset; // inset of x-axis from bottom of drawing area int yaxInset; // inset of y-axis from left of drawing area final double xInset = 0.5; // amount showing to left final double xRange = 3.0; final double yInset = 0.5; // amount showing below final double yRange = 3.0; double[] theCoefficients; // = {0.43, 2.0, -3.0, 1.0}; // in ascending order final double scale = 1.5; final int degree = 3; /******************************************* *** Now the instance-specific declarations... ********************************************/ final String theURL = "http://www.math.umn.edu/~garrett/qy/Newton.html"; final boolean restrictAccess = false; // switch restrictions on URL double theX; // current value of guess of root double nextX; // the next value int elapsedTime; // elapsed time boolean queryRunMovie; // see "start()" below boolean queryJustDraw; // true if just incremental drawing int colorIndex; // to gradually darken... or whatever /********************************************* *** Boilerplate methods... *********************************************/ public void init() { if (! restrictAccess || this.getDocumentBase().toString().equals(theURL)) { xaxInset = drawHeight - yToPix(0.0); yaxInset = xToPix(0.0); } else { xaxInset = 500; yaxInset = 400; } try { background_color = Integer.parseInt(getParameter("background_color"), 16); } catch (NumberFormatException e) { background_color = 0x505060; } try { pauseAmount = Integer.parseInt(getParameter("pauseAmount")); } catch (NumberFormatException e) { pauseAmount = 500; } try { approxThreshhold = Double.valueOf(getParameter("approxThreshhold")).doubleValue(); } catch (NumberFormatException e) { approxThreshhold = 0.0001d; } theCoefficients = new double[4]; try { theCoefficients[0] = Double.valueOf(getParameter("constantCoef")).doubleValue(); } catch (NumberFormatException e) { theCoefficients[0] = 0.43d; } try { theCoefficients[1] = Double.valueOf(getParameter("linearCoef")).doubleValue(); } catch (NumberFormatException e) { theCoefficients[1] = 2.0d; } try { theCoefficients[2] = Double.valueOf(getParameter("quadraticCoef")).doubleValue(); } catch (NumberFormatException e) { theCoefficients[2] = -3.0d; } try { theCoefficients[3] = Double.valueOf(getParameter("cubicCoef")).doubleValue(); } catch (NumberFormatException e) { theCoefficients[3] = 1.0d; } myInit(); } public synchronized void start() { if (timerThread == null && queryRunMovie) { // don't start merely when applet is initialized // queryRunMovie is set to 'true' only with mouseDown timerThread = new Thread(this); timerThread.start(); } } public synchronized void stop() { if (timerThread != null) { timerThread.stop(); /* try { timerThread.join(); } catch (InterruptedException e) { } */ timerThread = null; } queryRunMovie = false; } void pause(int time) { try { Thread.sleep(time); } catch ( InterruptedException e) { } } public void run() { // do _not_ synchronize _this_!!! while (true) { pause(pauseAmount); // pause to avoid too tight a loop // so that screen never gets to repaint itself repeatProcess(); // okey-dokey! } } public void update(Graphics g) { paint(g); // no gratuitous screen-clearing } void drawAxes(Graphics g, Color c) { // draws x-axis and y-axis g.setColor(c); g.fillRect(inset, drawHeight - xaxInset, drawWidth, 2); // x-axis int[] xs = {inset + drawWidth + 2, inset + drawWidth - 10, inset + drawWidth - 6, inset + drawWidth - 10}; int[] ys = {drawHeight - xaxInset, drawHeight - xaxInset-6, drawHeight - xaxInset, drawHeight - xaxInset+6}; g.fillPolygon(new Polygon(xs, ys, 4)); // arrowhead on x-axis g.fillRect(yaxInset-1, inset, 2, drawHeight); // y-axis int[] xs2 = {yaxInset, yaxInset - 6, yaxInset, yaxInset + 6}; int[] ys2 = {inset - 2, inset + 10, inset + 6, inset + 10}; g.fillPolygon(new Polygon(xs2, ys2, 4)); // arrowhead on y-axis } int xToPix (double x) { // rescales x-values to horizontal on screen return (int) ( (x + xInset) * ((double) drawWidth) / xRange) + inset; } double pixToX (int p) { // inverse of previous map return ((double) p - inset) * xRange / ((double) drawWidth) - xInset; } int yToPix (double y) { // rescales y-values to vertical on screen return inset + drawHeight - ((int) ( (y + yInset) * ((double) drawHeight) / yRange)); } double tTox (int t) { // rescales [0,range] to [-xInset, xRange-xInset] return ((double) (t * inc)) * xRange / ((double) drawWidth) - xInset; } /***************************************************** * * Below are instance-specific methods... * *********************************************************/ void myInit() { queryRunMovie = false; queryJustDraw = false; colorIndex = 79; elapsedTime = 0; } synchronized void repeatProcess() { queryJustDraw = true; elapsedTime++; double tmpX = nextX; nextX = computeNextX(nextX); theX = tmpX; repaint(); if (Math.abs(theX-nextX) < approxThreshhold) { queryRunMovie = false; stop(); } } double theFunction(double x) { // should roughly map -0.5 < x < 2.5 // to -0.5 < y < 2.5 double out = 0.0; for (int i=degree; i>0; i--) { out = (out + theCoefficients[i]) * x; } out += theCoefficients[0]; out *= scale; return out; } double theDerivative(double x) { double out = 0.0; for (int i=degree; i>0; i--) { out = out * x + ((double) i) * theCoefficients[i]; } out *= scale; return out; } void drawCurve(Graphics g, Color c) { g.setColor(c); for (int i=0; i<= range; i++) { g.drawLine(xToPix(tTox(i)), yToPix(theFunction(tTox(i))), xToPix(tTox(i+1)), yToPix(theFunction(tTox(i+1)))); g.drawLine(xToPix(tTox(i)), yToPix(theFunction(tTox(i)))+1, xToPix(tTox(i+1)), yToPix(theFunction(tTox(i+1)))+1); } } double computeNextX(double x) { /* the cutoffs to avoid over-running the implementation limit certainly depend on the scale, etc. */ double val = x - theFunction(x)/theDerivative(x); if (val > BIG) { return BIG; } else if (val < -BIG) { return -BIG; } else { return val; } } public synchronized void paint(Graphics g) { if (queryJustDraw) { // draw the progress of the Newton method queryJustDraw = false; g.setColor(new Color(155 + (7*colorIndex)%100, 105 + (3*colorIndex)%100, 155 + (5*colorIndex)%100)); g.drawLine(xToPix(theX), yToPix(0.0), xToPix(theX), yToPix(theFunction(theX))); g.drawLine(xToPix(theX), yToPix(theFunction(theX)), xToPix(nextX), yToPix(0.0)); g.setColor(new Color(background_color)); g.fillRect(120,100,300,20); g.setColor(Color.black); g.drawString(elapsedTime+"th approximation = "+theX, 125, 118); } else { // just redraw the background, effectively erasing all Newton... // AND note that queryRunMovie is set to 'false' // queryRunMovie = false; // ? // queryJustDraw = false; // ? g.setColor(new Color(background_color)); g.fillRect(0,0,width,height); g.setColor(Color.black); drawAxes(g, Color.black); drawCurve(g, Color.red); g.setColor(Color.black); g.drawString("y = ("+theCoefficients[3]+ ") x^3 + ("+theCoefficients[2]+ ") x^2 + ("+theCoefficients[1]+ ") x + ("+theCoefficients[0]+ ")", 200, height-30); g.drawString("Click anywhere along the x-axis to give a first approximation.", 120, 20); g.drawString("Subsequent clicks near the x-axis restart the process.", 120, 40); g.drawString("while leaving the previous processes on the picture.", 140, 60); g.drawString("Each approximation process stops when within 0.0001", 140, 80); g.drawRect(30,30,60,20); g.drawString("Clear", 35, 47); } } public final boolean mouseDown(Event e, int i, int j) { if (i>=inset && i<= inset+drawWidth && Math.abs((height-j)-xaxInset) <= 40) { theX = pixToX(i); nextX = computeNextX(theX); elapsedTime = 0; colorIndex = colorIndex + 20; queryJustDraw = true; // evidently the background is already drawn repaint(); // at least this _one_ repaint seems to get done if (queryRunMovie == false) { // it's not already running... timerThread = null; // hmmm!? Could be delay in thread death!? queryRunMovie = true; start(); } } else if (i>=30 && i<= 90 && j>=30 && j<=50) { myInit(); // resets boolean switches and colorIndex stop(); repaint(); } return true; } } //__THE__END__