/** * Bifurcation Map Generator for x = cx(1 - x) * @author Robert J Morton YE572246C * @version 5 December 1997 */ import java.awt.*; public class bifmap1 extends java.applet.Applet implements Runnable { int H = 17, V = 15, // top and left margins (in pixels) X, Y, // dimensions of display window (in pixels) x, y, // co-ords of resultant plot (in pixels) scale = 160, // number of pixels per 1.0 real cMAX = scale * 3 + 1, // dimensions of the plotting area rMAX = scale + 1, W = V + scale, // origin on the vertical scale I, // number of iterations of x = x * x + c i; // current iteration number of the above long TF = 60, // inter-plot time frame (milliseconds) T; // future system time at which a new plot is due to begin double r, // the 'real' variable c, // the 'real' constant Scale = (int)(scale); // pixels per unit 'real' (100 pixels = 1.00) boolean finished = false; // indicates when the map has been completed Image buffer = null; // reference for an off-screen image buffer Graphics gc = null; // graphics context for the off-screen image Color bg, tc, ac; // background colour, trace colour, attractor colour Color A[][]; // pixel array to minimise calls to drawLine() Thread Bif; // thread reference variable for this program public void init() { // NORMAL APPLET INITIALISER METHOD int j, k; // utility loop variables bg = Color.lightGray; // set background colour ac = new Color( 0, 128, 64); // create the trace colour tc = new Color( 0, 255, 128); // create the attractor colour /* These two statements use deprecated forms: Dimension d = size(); // size of window given in HTML applet tag X = d.width; Y = d.height; // establish window width and height */ X = 510; Y = 195; // applet window: width=510 height=195 buffer = createImage(X, Y); // create the off-screen image buffer /* Get the graphics context reference for the off-screen image, use it to set background colour for the off-screen image then fill the off-screen image with the background colour. */ gc = buffer.getGraphics(); gc.setColor(Color.black); gc.fillRect(0, 0, X, Y); /* Create an array for pixels and set them all to light grey. Pixels are plotted in here so they can be checked to avoid having to call drawLine() unnecessarily. */ A = new Color[cMAX][rMAX]; for(j = 0 ; j < cMAX ; j++) for(k = 0 ; k < rMAX ; k++) A[j][k] = bg; gc.setColor(Color.white); // set colour for scale lines j = H - 5; // start of scale marks for vertical scale gc.drawLine(H, V, H, W); // draw the vertical scale line int di = scale / 5; // increment between scale marks on vertical scale /* At each mark position on vertical scale draw the vertical scale's scale marks. */ for(i = 0 ; i <= scale; i += di) gc.drawLine(j, V + i, H, V + i); I = scale * 3; // length of the horizontal scale j = W + 5; // bottom of scale marks for horizontal scale gc.drawLine(H, W, H + I, W); // draw the horizontal scale line di = scale / 2; // increment between scale marks //At each scale mark position draw a scale mark. for(i = 0 ; i <= I; i += di) gc.drawLine(H + i, W, H + i, j); //Create a writing font for and set it active for the applet pane. Font font = new Font("Dialog", Font.PLAIN, 12); gc.setFont(font); int h = H - 15, // horizontal co-ordinate of vertical scale figures v = V + 3; // start position of vertical co-ordinates // Add the scale numbers to the vertical axis. gc.drawString("1", h + 4, v); gc.drawString(".8", h, v + (int)(scale * .2)); gc.drawString(".6", h, v + (int)(scale * .4)); gc.drawString(".4", h, v + (int)(scale * .6)); gc.drawString(".2", h, v + (int)(scale * .8)); gc.drawString("0", h + 3, v + scale); h = H - 2; // start position of horizontal scale figures v = V + scale + 16; /* vertical coordinate of the base of the figures of the horizontal scale. */ // Add the scale numbers to the horizontal axis. gc.drawString("1", h, v); gc.drawString("2", h + scale, v); gc.drawString("3", h + (int)(Scale * 2), v); gc.drawString("4", h + (int)(Scale * 3), v); gc.setColor(Color.black); // set color for lettering gc.drawString("x", H - 2, V - 4); // label the vertical axis gc.drawString("c", H + 485, W + 2); // label the horizontal x = 0; // set to start map from right-hand edge T = System.currentTimeMillis() + TF; // end of first plot's time frame } // PAINT THE PICTURE SO FAR from the off-screen image buffer public void paint(Graphics g) { g.drawImage(buffer, 0, 0, null); } /* Called to redraw everything from the off-screen image buffer in response a request for a repaint(). */ public void update(Graphics g) { g.drawImage(buffer, 0, 0, null); } void newPlot() { // PUT A VERTICAL LINE OF NEW PLOTS ON THE HIDDEN IMAGE Color C = tc; // set current colour to aura trace colour /* If we have not yet reached the end of the 'c' axis, compute the real value that corresponds to the current x-pixel, then reset 'r' for a new iteration run. */ if(x < cMAX) { c = (double)(x) / Scale + 1; r = .01; for(i = 0; i < 10000; i++) { // for I (further) iterations // Change the trace colour when 'r' has had time to settle. if(i > 100) C = ac; /* Iterate the appropriate non-linear difference equation and bail out if 'r' goes off screen. */ r = c * r * (1 - r); if(r >= 1 || r <= 0) break; /* 'r' is still on-screen so compute the new y-pixel position for the new value of 'r' and set the appropriate colour for the plot.*/ y = (int)(r * Scale); Color K = A[x][y]; /* If the pixel needs to be drawn, set the pixel element to the appropriate trace colour, set applet window co-ordinates of new plot, set colour for final stretch of current line and draw a line from the start dot to the previous dot. */ if(K == bg || C == ac && K == tc) { A[x][y] = C; int h = H + x, v = W - y; gc.setColor(C); gc.drawLine(h, v, h, v); } } x++; // advance right one pixel repaint(); // gets graphics context and calls update() } else finished = true; // Else we've completed the attractor, } // so the map is finished. public void run() { // run the Track Recorder painting thread while(true) { // permanent loop if(!finished) newPlot(); // paint in the next plot /* Get the time remaining in this plot's time frame, making sure that it is at least 5 milliseconds. Then put the thread to sleep for this remaining time. */ long s = T - System.currentTimeMillis(); if(s < 5) s = 5; try { Thread.currentThread().sleep(s); } /* Catch but ignore any exceptions because there are expected legitimate GUI events. Then set 'T' to the finishing time of the next time frame. */ catch (InterruptedException e) { } T = System.currentTimeMillis() + TF; } } /* Start the program thread by creating the thread object and starting it running by requesting a call to run(). */ public void start() { Bif = new Thread(this); Bif.start(); } public void stop() { Bif=null; } // Stop this a program thread }