/** * Bifurcation Map Generator for x = cx(1 - x) * @author Robert J Morton * @version 5 December 1997 */ import java.awt.*; import javax.swing.*; import java.awt.image.BufferedImage; public class bifgraph1 extends JPanel implements Runnable { private int XE, // Horizontal extent of window and JFrame. YE, // Vertical extent of window and JFrame. N, // number of iterations of x = x * x + c i, // current iteration number of the above H = 17, // top and left margins (in pixels) V = 15, // top and left margins (in pixels) X = 510, // X-dimension of display window (in pixels) Y = 195, // Y-dimension 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, // dimensions of the plotting area W = V + scale; // origin on the vertical scale private double r, // the 'real' variable c, // the 'real' constant Scale = (int)(scale); // pixels per unit 'real' (100 pixels = 1.00) private long tf = 60, // inter-plot time frame (milliseconds) t; // system time at which a new plot is due to begin private Color bg = Color.lightGray, // background colour tc = new Color(0,255,128), // trace colour ac = new Color(0,128,64), // attractor colour A[][] = new Color[cMAX][rMAX]; /* pixel array to minimise calls to drawLine() */ private boolean finished = false; // indicates when the map has been completed private BufferedImage I; // off-screen image on which to plot the graph private Graphics2D gi; // graphics reference for off-screen image private Thread T; // declare a thread reference variable public bifgraph1(int XE, int YE) { this.XE = XE; // horizontal [X] dimension of the JPanel this.YE = YE; // vertical [Y] dimension of the JPanel setBounds(0,0,XE,YE); // the JPanel fills the whole of the JFrame area setLayout(null); // JPanel has free-form layout [for graph] /* Create a Buffered Image "I" on which to plot Henon's Attractor off- screen and get its graphics context "gi" for realising the plots. */ I = new BufferedImage(X,Y,BufferedImage.TYPE_INT_RGB); gi = I.createGraphics(); gi.setColor(Color.black); // set background colour for off-screen image gi.fillRect(0,0,X,Y); // and fill it with background colour int j, k; // utility loop variables /* Set all the pixels of the colour raster to back- ground colour here so they can be checked in order to to avoid having to call drawLine() unnecessarily. */ for(j = 0; j < cMAX; j++) for(k = 0; k < rMAX; k++) A[j][k] = bg; gi.setColor(Color.white); // set colour for scale lines j = H - 5; // start of scale marks for vertical scale gi.drawLine(H,V,H,W); // draw the vertical scale line int di = scale / 5; // increment between scale marks on vertical scale /* At each marked position on vertical scale, draw the vertical scale's scale marks.*/ for(i = 0; i <= scale;i += di) gi.drawLine(j,V+i,H,V+i); N = scale * 3; // length of the horizontal scale j = W + 5; // bottom of scale marks for horizontal scale gi.drawLine(H,W,H+N,W); // draw the horizontal scale line di = scale / 2; // increment between scale marks for(i = 0; i <= N;i += di) // At each scale mark position gi.drawLine(H+i,W,H+i,j); // draw a scale mark. // Set the font for the text in this application. Font font = new Font("Dialog",Font.PLAIN,12); gi.setFont(font); int h = H - 15, // horizontal co-ordinate of vertical scale figures v = V + 3; // start position of vertical co-ordinates gi.drawString("1", h+4,v); // number the vertical axis gi.drawString(".8",h,v+(int)(scale*.2)); gi.drawString(".6",h,v+(int)(scale*.4)); gi.drawString(".4",h,v+(int)(scale*.6)); gi.drawString(".2",h,v+(int)(scale*.8)); gi.drawString("0", h+3,v+scale); h = H-2; // start position of horizontal scale's figures v = V+scale+16; // vertical co-ord of base of figures for horiz scale gi.drawString("1",h,v); // number horizontal scale gi.drawString("2",h+scale,v); gi.drawString("3",h+(int)(Scale*2),v); gi.drawString("4",h+(int)(Scale*3),v); gi.setColor(Color.black); // set color for lettering gi.drawString("x",H-2,V-4); // label the vertical axis gi.drawString("c",H+485,W+2); // label the horizontal axis x = 0; // start map from right-hand edge t = System.currentTimeMillis() + tf; // end of first plot's time frame T = new Thread(this); // create a run() thread T.start(); // and starting it running } public void paint(Graphics g) { // PAINT THE PICTURE SO FAR g.drawImage(I,10,15,null) ; // draw from the off-screen image buffer } void newPlot() { // PUT A VERTICAL LINE OF NEW PLOTS ON HIDDEN IMAGE Color C = tc; // set current colour to aura trace colour if(x < cMAX) { // if not yet reached end of c axis... // The c value (real) corresponding to the current x-pixel (int) c = (double)(x) / Scale + 1; r = .01; // Reset r for a new iteration run for(i = 0; i < 10000; i++){ // for N (further) iterations if(i > 100) // When r has had time to settle, C = ac; // change colour. r = c * r * (1 - r); // advance the iteration process if(r >= 1 || r <= 0) // bail out if r gone off screen break; y = (int)(r * Scale); // y-plot for current value of r Color K = A[x][y]; // current colour of corresponding pixel if(K == bg || C == ac && K == tc) { // if pixel needs to be drawn A[x][y] = C; // set pixel element to appropriate trace colour int h = H + x, // window co-ordinates of the new plot v = W - y; gi.setColor(C); // set colour for final stretch of current line // Draw a line from the start dot to the previous dot. gi.drawLine(h,v,h,v); } } x++; // advance right one pixel repaint(); // gets graphics context and calls update() } else finished = true; // else the attractor is now complete } public void run() { // run the Track Recorder painting thread while(true) { // permanent loop if(!finished) newPlot(); // paint in the next plot // Get the time left in this plot's time frame. long s = t - System.currentTimeMillis(); if(s < 5) s = 5; // in case machine isn't fast enough try{Thread.currentThread().sleep(s);} // sleep for the time remaining catch (InterruptedException e){ } // catch interrupt from GUI/browser // System time at which the next plot's time frame must end. t = System.currentTimeMillis() + tf; } } }