/** * Bifurcation Map Generator for x = x**2 + c * @author Robert J Morton * @version 4 November 1997 */ import java.awt.*; import javax.swing.*; import java.awt.image.BufferedImage; public class bifgraph2 extends JPanel implements Runnable { private int XE, // Horizontal extent of window and JFrame. YE, // Vertical extent of window and JFrame. X = 250, // X-dimension of display window (in pixels) Y = 400, // Y-dimension of display window (in pixels) Xb = 210, // set horizontal 'real' origin (pixel bias) Yb = Y >> 1, // set vertical 'real' origin (pixel bias) x = X - 1, // set to start map from right-hand edge y, // co-ords of resultant plot (in pixels) N = 10000, // number of iterations of x = x * x + c i; // current iteration number of the above private long tf = 60, // inter-plot time frame (milliseconds) t; // system time at which a new plot is due to begin private double r, // the 'real' variable c, // the 'real' constant Scale = 100; // pixels per unit 'real' (100 pixels = 1.00) private boolean finished = false; // indicates when the map has been completed // Spacer for axis numbering: private String Sp[] = {""," "," "," "," "}; private Color bg = Color.black, // set background colour ac = new Color(0,160, 96), // create the trace colour tc = new Color(0,255,128), // create the attractor colour A[][] = new Color[X][Y]; // create a pixel array for pixels; 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 bifgraph2(int XE, int YE) { // NORMAL APPLET INITIALISER METHOD 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 background colour here so they can be checked in order to to avoid having to call drawLine() unnecessarily. */ for(j = 0; j < X; j++) for(k = 0; k < Y; k++) A[j][k] = bg; gi.setColor(Color.gray); // set colour for scale lines gi.drawLine(Xb,Yb-200,Xb,Yb+200); // draw vertical scale for(i=50-Yb, j=Xb-5, k=Xb+5; i < Yb; i += 50) gi.drawLine(j,Yb+i,k,Yb+i); gi.drawLine(0,Yb,Xb+150,Yb); // draw horizontal scale for(i=10-Xb, j=Yb-5, k=Yb+5; i < 150; i += 50) gi.drawLine(Xb+i,j,Xb+i,k); // Choose the font for the text in this application Font font = new Font("Dialog", Font.PLAIN,12); gi.setFont(font); ShowNum(gi,+1.5,180, 50); // number vertical axis ShowNum(gi,+1.0,180,100); ShowNum(gi,+0.5,180,150); ShowNum(gi,-0.5,218,250); ShowNum(gi,-1.0,218,300); ShowNum(gi,-1.5,218,350); ShowNum(gi,-0.5,150,191); // number horizontal scale ShowNum(gi,-1.0,100,191); gi.setColor(Color.black); gi.drawString("x",202,13); // label the vertical axis gi.drawString("c",240,196); // label the horizontal t = System.currentTimeMillis() + tf; // end of first plot's time frame T = new Thread(this); // creating the thread object T.start(); // and starting it running by } public void paint(Graphics g) { //PAINT THE PICTURE SO FAR g.drawImage(I,20,23,null); //draw from the off-screen image buffer } // DISPLAY A NUMERIC VALUE FOR A FIELD void ShowNum(Graphics g, double a, int x, int y) { String sign = "+"; // means number to be displayed is positive if(a < 0) { // if the number is negative a = -a; // make it positive sign = "-"; // make its sign "minus" } String s = String.valueOf(a); // convert the integer to a string i = 3 - s.length(); if(i < 0) i = 0; // get number of leading zeros needed g.drawString(Sp[i]+sign+s,x,y); //display the string } public void newPlot() { // called in response a request for a repaint() Color C = tc; // set current colour to aura trace colour if(x > 0) { // if not yet reached left side of window // The c value (real) corresponding to the current x-pixel (int) c = (double)(x-Xb)/Scale; r = 0; // Reset r for a new iteration run for(i = 0; i < N; i++) { // for N (further) iterations if(i > 100) // when r has had time to settle C = ac; // change colour r = r * r + c; // advance the iteration process if(r > 2 || r < -1.99) // if r gone off screen... break; // bail out. y = Yb - (int)(r * Scale); // y-plot for current value of r Color K = A[x][y]; // current colour of corresponding pixel // If the pixel needs to be drawn... if(K == bg || C == ac && K == tc) { A[x][y] = C; // set pixel element to appropriate trace colour gi.setColor(C); // set colour for final stretch of current line // draw line from start dot to previous dot gi.drawLine(x,y,x,y); } } x--; // advance left one pixel repaint(); // get graphics context and call 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 next vertical line of plots on hidden IMAGE // get 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{T.sleep(s);} // sleep for the time remaining in this time-frame catch (InterruptedException e){} // any catch interrupt from GUI // Set the system time at which the next plot's time frame must end t = System.currentTimeMillis() + tf; } } }