import java.awt.*; /** Program: IteratedPolynomials Purpose: @author: Paul Garrett, garrett@math.umn.edu @version: 25 Sept 1997 */ public class IteratedPolynomials extends java.applet.Applet implements Runnable { Thread timerThread; /* * the following are read in as parameters from the HTML page */ int background_color; // int to specify hex color int foreground_color; // int to specify hex color int pauseAmount; // int specifying milliseconds between steps int allowedNumberCycles; // int specifying number cycles each value int height; int width; /*******************/ final double initialConstant = -.8; // seems good? int drawHeight; int drawWidth; final int inset = 35; final int inc = 4; int range; // want inc * range = drawWidth double xInset = 5d; // amount showing to left double xRange = 10d; // whole range of x double yInset = 3d; // amount showing below double yRange = 6d; // whole range of x final double zoomFactor = 1.2; double theCoefficient; // for x^2+c TextField coefField; // for the constant TextField cyclesField; // to tell how many cycles final String theURL = "http://www.math.umn.edu/~garrett/qy/IteratedPolynomials.html"; final boolean restrictAccess = false; // switch restrictions on URL double theX; // current value double nextX; // the next value double theY; double nextY; int elapsedTime; // elapsed time boolean queryRunMovie; // see "start()" below boolean queryJustDraw; // true if just incremental drawing int colorIndex; // to gradually darken... or whatever /********************************************* *********************************************/ public synchronized void init () { queryRunMovie = false; queryJustDraw = false; colorIndex = 79; elapsedTime = 0; try { background_color = Integer.parseInt(getParameter("background_color"), 16); height = Integer.parseInt(getParameter("height")); allowedNumberCycles = Integer.parseInt(getParameter("allowedNumberCycles")); foreground_color = Integer.parseInt(getParameter("foreground_color"), 16); pauseAmount = Integer.parseInt(getParameter("pauseAmount")); width = Integer.parseInt(getParameter("width")); } catch (Exception e) { background_color = 0x302030; height = 320; width = 550; foreground_color = 0xbbbbbb; pauseAmount = 500; allowedNumberCycles = 100; } drawHeight = height - 2*inset; drawWidth = width - 2*inset; range = drawWidth / inc; theCoefficient = initialConstant; setLayout(new BorderLayout()); Panel p = new Panel(); Panel q = new Panel(); add("North", q); add("South", p); cyclesField = new TextField("000",3); cyclesField.setEditable(false); p.add(new Label("Cycles =")); p.add(cyclesField); p.add(new Label(" y = x * x +")); coefField = new TextField(""+theCoefficient,4); p.add(coefField); p.add(new Label("'Return' updates constant and redraws")); // following added 10-08-97 Button bClear = new Button("Clear"); q.add(new Label("Click anywhere along the x-axis to start")); q.add(bClear); /* the following repairs some damage in case of other windows overlapping, moved to this spot 10-08-97 to work around bugs in some implementations' components' inheritance of these colors */ setBackground(new Color(background_color)); setForeground(new Color(foreground_color)); p.setBackground(new Color(0x808080)); p.setForeground(new Color(0x000000)); q.setBackground(new Color(0x808080)); q.setForeground(new Color(0x000000)); repaint(); } // end of init() /************************************/ 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(); 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 } synchronized void drawAxes(Graphics g, Color c) { // draws x-axis and y-axis g.setColor(c); g.fillRect(inset, yToPix(0d), drawWidth, 2); // x-axis // arrowhead on x-axis int[] xs = {inset + drawWidth + 2, inset + drawWidth - 10, inset + drawWidth - 6, inset + drawWidth - 10}; int[] ys = {yToPix(0d), yToPix(0d)-6, yToPix(0d), yToPix(0d)+6}; g.fillPolygon(new Polygon(xs, ys, 4)); // arrowhead on y-axis g.fillRect(xToPix(0d)-1, inset, 2, drawHeight); // y-axis int[] xs2 = {xToPix(0d), xToPix(0d) - 6, xToPix(0d), xToPix(0d) + 6}; int[] ys2 = {inset - 2, inset + 10, inset + 6, inset + 10}; g.fillPolygon(new Polygon(xs2, ys2, 4)); } synchronized int xToPix(double x) { // rescales x-values to horizontal on screen double w = (double) drawWidth; return inset + (int) ( (x + xInset) * w / xRange); } synchronized double pixToX (int p) { // inverse of previous map return ((double) p - inset) * xRange / ((double) drawWidth) - xInset; } synchronized int yToPix (double y) { // rescales y-values to vertical on screen return inset + (drawHeight - ((int) ( (y + yInset) * ((double) drawHeight) / yRange))); } synchronized double tTox (int t) { // rescales [0,range] to [-xInset, xRange-xInset] return ((double) (t * inc)) * xRange / ((double) drawWidth) - xInset; } synchronized void repeatProcess() { queryJustDraw = true; elapsedTime++; cyclesField.setText(""+elapsedTime); colorIndex += 20; // a novelty item theX = nextX; theY = nextY; nextY = theFunction(theX); nextX = nextY; repaint(); if (elapsedTime > allowedNumberCycles) { queryRunMovie = false; stop(); repaint(); // to redraw labels at end, in case of damage } } final synchronized double theFunction(double x) { return (double) (theCoefficient + x * x); } final synchronized 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); } g.drawLine(xToPix(-xInset), yToPix(-xInset), xToPix(xRange-xInset), yToPix(xRange-xInset)); // the diagonal } final public synchronized void paint(Graphics g) { if (queryJustDraw) { // draw the progress of the iteration queryJustDraw = false; if (Math.abs(theX) < 200 && Math.abs(theY) < 200 && Math.abs(nextX) < 200 && Math.abs(nextY) < 200) { // Color adjustment here g.setColor(new Color(150 + (25+colorIndex)%100, 255, 100)); g.drawLine(xToPix(theX), yToPix(theY), xToPix(theX), yToPix(nextY)); g.drawLine(xToPix(theX), yToPix(nextY), xToPix(nextX), yToPix(nextY)); } // this rewrites report of iteration } else { // just redraw the background, effectively erasing g.setColor(new Color(background_color)); g.fillRect(0,0,width,height); drawAxes(g, new Color(foreground_color)); drawCurve(g, Color.red); } } public final synchronized boolean mouseDown(Event e, int i, int j) { if (i>=inset && i<= inset+drawWidth && Math.abs((height-j)-yToPix(0d)) <= 40) { stop(); //__EDITED__ 10-08-97 theX = pixToX(i); theY = 0d; nextY = theFunction(theX); nextX = nextY; 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; // Could be delay in thread death!? queryRunMovie = true; start(); } return true; } else { return false; // absence caused un-necessary repaint... } } public final synchronized boolean action(Event e, Object arg) { if (e.target instanceof TextField) { try { theCoefficient = Double.valueOf(coefField.getText()).doubleValue(); queryRunMovie = false; queryJustDraw = false; colorIndex = 79; elapsedTime = 0; stop(); repaint(); } catch (NumberFormatException nfe) { } return true; } else if (e.target instanceof Button) { if (arg.toString().equals("Zoom In")) { stop(); queryRunMovie = false; queryJustDraw = false; colorIndex = 79; elapsedTime = 0; xInset /= zoomFactor; xRange /= zoomFactor; yInset /= zoomFactor; yRange /= zoomFactor; repaint(); return true; } else if (arg.toString().equals("Zoom Out")) { stop(); queryRunMovie = false; queryJustDraw = false; colorIndex = 79; elapsedTime = 0; xInset *= zoomFactor; xRange *= zoomFactor; yInset *= zoomFactor; yRange *= zoomFactor; repaint(); return true; } else if (arg.toString().equals("Clear")) { stop(); queryRunMovie = false; queryJustDraw = false; colorIndex = 79; elapsedTime = 0; repaint(); return true; } else { return false; } } else { return false; } } } //__THE__END__