/* * Program: Chaos Theory Demonstrator * Programmer: Robert John Morton UK-YE572246C * Started: Wed 14 Jul 2019 10:16:25 -03 * Finished: Thu 25 Jul 2021 18:00:00 -03 This is an integration of a Difference Equation Iterator I previously trans- coded to 'C' with 4 other chaos-related Java programs I wrote some years ago. From the directory in which chaos.c resides, to compile: gcc chaos.c -L/usr/X11R6/lib -o chaos -lX11 -lm to run: ./chaos This program demonstrates an interesting aspect of Chaos Theory. It shows how by iterating a simple non-linear equation, you can reproduce the apparent chaotic behaviour of complex-dynamical systems like the global weather, animal and plant populations, social interactions, boom and bust in economies and the ups and downs of trading markets. The equations represent the protocol by which the ELEMENTS of a complex-dynamical system - such as molecules and people - interact. The program is pure 'C': it does not use any external widget libraries. A comprehensive discourse about this program and its use is at: https://robmorton.website/chaos/difeqn.html */ #include // contains the X11 grapical drawing functions #include // input output to/from the console and keyboard #include // general functions for the 'C' language #include // for initialising strings as character arrays #include // contains the function usleep() #include // for sine function used in TrackRec [Tab 5] #define WHITE 0xFFFFFF // colour white for 'selected menu item' lettering #define GREY 0xAAAAAA // colour grey for 'unselected menu item' lettering #define AXIS 0xCCCCCC // colour grey for graph axes #define DARK 0x444444 // colour for menu background #define BLACK 0x000000 // colour black for clearing graph areas #define YELLOW 0xFFFF00 // colour yellow for menu titles #define GREEN 0x60FF60 // colour green for graphical traces #define DARKG 0x008040 // dark green for graphical traces #define LM 35 // left margin of application #define TL 36 // Time Graph Left: left-most pixel column of plottable area #define TT 90 // top margin of Time Graph: first plottable pixel row #define TH 500 // Horizontal extent of plottable area of Time Graph #define TV 160 // Vertical extent of plottable area of Time Graph #define TW 101 // number of plot points along the time axis #define BL 375 // Bounce Graph Left: left-most pixel col of plottable area #define BT 293 // Bounce Graph Top: top-most pixel column of plottable area #define BE 161 // Horiz & Vert extents of plottable area of Bounce Graph #define CL 575 // C-left: x-coord of the SET 'c' value selector menu #define IS 20 // vertical item spacing in the menus #define BW 97 // button width + inter-button space [pixels] // FOR TAB 1 [BIFMAP1] #define T1H 50 // left margin (in pixels) #define T1V 80 // top margin (in pixels) #define T1X 510 // X-dimension of display window (in pixels) #define T1Y 195 // Y-dimension of display window (in pixels) #define T1S 160 // number of pixels per unit real #define T1cMAX 481 // = T1S * 3 + 1 [dimensions of the plotting area] #define T1rMAX 161 // = T1S + 1, [dimensions of the plotting area] #define T1W 240 // = T1V + T1S [origin on the vertical scale] #define T1Q 275 // T1V+T1Y [vertical coord of graph's horizontal axis] // FOR TAB 2 [BIFMAP2] #define T2N 10000 // number of iterations of x = x * x + c #define T2H 250 // X-dimension of display window (in pixels) #define T2V 400 // Y-dimension of display window (in pixels) #define T2Xb 540 // set horizontal 'real' origin (pixel bias) #define T2Yb 260 // set vertical 'real' origin (pixel bias) #define T2XB 340 // T2Xb-200 [dist from window left to start of graph area] #define T2TM 70 // (T2Xb-T2V)/2 [dist from top of window to top of graph] // FOR TAB3 MANDELBROT SET #define T3H 260 // x position of the mandelbrot set within the window #define T3V 70 // y position of the mandelbrot set within the window #define T3X 225 // x pixel co-ordinates of real point 0,0 #define T3Y 190 // y pixel co-ordinates of real point 0,0 #define T3XE 350 // x-dimension of the display area (pixels) #define T3YE 380 // y-dimension of the display area (pixels) // FOR TAB4 HENON'S STRANGE ATTRACTOR #define T4H 280 // x position of the Henon attractor within the window #define T4V 70 // y position of the Henon attractor within the window #define T4X 300 // horizontal extent [in pixels] of the attractor graph #define T4Y 300 // vertical extent [in pixels] of the attractor graph // LISSAJOUS FIGURES #define T5LH 80 // horizontal positioning offset of graph within window #define T5LV 90 // vertical positioning offset of graph within window #define T5LR 80 // radius of the trace vector #define T5LB 90 // bias to centre of graph T5LR + 10 #define T5LW 180 // width of graphics window [twice T5LB] #define T5LX 86 // bias for the x-axis annotation 'X' [T5LB - 4] #define T5LY 95 // bias for the y-axis annotation 'Y' [T5LB + 5] // TRACK RECORDER'S GLOBAL VARIABLES #define T5TH 400 // horizontal placing of track recorder within the window #define T5TV 90 // vertical placing of track recorder within the window #define T5TX 180 // Horizontal extent of trace area #define T5TY 180 // Vertical extent of trace area #define T5Tx 90 // horizontal off-set of trace area [half T5TX] #define T5Ty 90 // vertical off-set of trace area [half T5TY] Display *XD; // pointer to an X11 display device Window XW; // reference for the window being created GC XG; // X11 graphics context int // GENERAL INTEGERS mX, // x-coordinate of mouse click mY, // y-coordinate of mouse click TB = 0, // tab number [re buttons across the top of the window area] PT, // contains selected inter-plot sleep time in microseconds IF = 1, // InitFlag [need it Initialised when RUN selected] FP = 0, // 0 = first pass not yet done; 1 = first pass done // CONTROL PANEL INTEGERS EN = 0, // 0: x = cx(1 - x), 1: c = x * x + c RS = 1, // 0: RUN, 1: STOP, 2: CLEAR PM, // 0: Line Graph, 1: Spot Values SM, // 0: Single-Shot, 1: Continuous IR = 0, // 0: Plot out or range of graph area, 1: within graph area // TIME GRAPH INTEGER VARIABLES T0Td, // time plot to be drawn [horizontal axis of the time graph] T0Tw, // time plot to be wiped on the time axis T0Tp, // local vertical co-ordinate of previous plot from top of window T0Tn, // local vertical co-ordinate of current plot from top of window T0TX, // vertical coordinate of Time Graph's horizontal time-axis T0PC, // Time Graph's plot counter T0WC, // Time Graph's wipe counter T0[TW], // Time Graph re-exposure buffer T0V[BE], // vertical plot accumulator for 'end of spot values' trace // BOUNCE GRAPH INTEGER VARIABLES T0Bp, // old plot [in pixels] on Bounce Graph T0Bw, // new plot [in pixels] on Bounce Graph T0BX, // relative x-coordinate of bounce graph origin T0BY, // relative y-coordinate of bounce graph origin T0Bmax, // max excursions of x & y in Bounce Graph T0Bmin, // min excursions of x & y in Bounce Graph // TAB1 INTEGERS T1x, // x-coordinate of the resultant plot (in pixels) T1f, // BifMap1's 'trace finished' flag T1A[T1cMAX][T1rMAX], // pixel buffer for BifMap1 graph // TAB2 INTEGERS T2x, // set to start map from the right-hand edge T2f, // 1:indicates when the map has been completed T2A[T2H][T2V], // pixel buffer for BifMa2 graph // TAB3 MANDELGRAPH T3f, // indicates when the Mandelbrot set is finished T3v, // current line number of graph from top of raster /* Create colours for the display. The first colour represents convergence of the Mandelbrot series after only one iteration. Successive colours represent convergence after 2, 3, 4... iterationas and so on up to 20. */ T3C[] = { 0xF9F9F9,0x000080,0x008800,0x900000,0x009800, 0xA000A0,0xA8A800,0xB0B0B0,0x1010B8,0x10C010, 0xC81010,0x10D0D0,0xD810D8,0xE0E010,0xE8E8E8, 0x4040F0,0x40F840,0xFC4040,0xFFFF40,0xFFFFFF }, T3A[T3XE][T3YE], // create a colour buffer array for the display area // TAB4 HENON'S STRANGE ATTRACTOR T4A[T4X][T4Y], // create acolour array for the display area // LISSAJOUS FIGURES T5S[] = { // Sine Table: integers = sin(a)*128 for every half degree 0, 87, 175, 262, 349, 436, 523, 610, 698, 785, 872, 958,1045,1132, 1219,1305,1392,1478,1564,1650,1736,1822,1908,1994,2079,2164,2250,2334, 2419,2504,2588,2672,2756,2840,2924,3007,3090,3173,3256,3338,3420,3502, 3584,3665,3746,3827,3907,3987,4067,4147,4226,4305,4384,4462,4540,4617, 4695,4772,4848,4924,5000,5075,5150,5225,5299,5373,5446,5519,5592,5664, 5736,5807,5878,5948,6018,6088,6157,6225,6293,6361,6428,6494,6561,6626, 6691,6756,6820,6884,6947,7009,7071,7133,7193,7254,7314,7373,7431,7490, 7547,7604,7660,7716,7771,7826,7880,7934,7986,8039,8090,8141,8192,8241, 8290,8339,8387,8434,8480,8526,8572,8616,8660,8704,8746,8788,8829,8870, 8910,8949,8988,9026,9063,9100,9135,9171,9205,9239,9272,9304,9336,9367, 9397,9426,9455,9483,9511,9537,9563,9588,9613,9636,9659,9681,9703,9724, 9744,9763,9781,9799,9816,9833,9848,9863,9877,9890,9903,9914,9925,9936, 9945,9954,9962,9969,9976,9981,9986,9990,9994,9997,9998,10000,10000 }, T5a[] = {0,0,0,0}, // main phase angles T5d[] = {1,1,1,1}, // main phase angle incrementers T5Q[] = {0,0,0,0}, // quadrant counters T5s[] = {1,1,1,1}, // indicates the output signs T5f[] = {0,0,0,0}, // flags to trigger start of wipers T5m = 1, // x-axis frequency multiplier [can be 1 or 2] T5q = 4, // Number of quadrants to paint on each pass T5w = 1, // trigger flag to wipe the 'scope trace' // TRACK RECORDER'S GLOBAL VARIABLES T5TT, // trace colour switch T5TF, // trigger to flip the track trace colour T5TC, // Track Recorder trace colour T5TD[T5TX][T5TY]; // buffer to preserve the trace so far struct // PARAMETERS USED IN THE COMPUTATION OF THE X & Y COORDINATES T5Z { // FOR THE TRACK RECORDER TRACE IN THE FUNCTION: T5Tplot(). double a, // main phase angle A, // quadrant main angle q, // phase angle Q; // quadrant phase angle int P, // phase cycle counter f, // 'first main cycle complete' flag c; // 'current main cycle completed' flag } T5T[2]; // 2-D array: index 0 for the x-coord, index 1 for the y-coord double // FOR THE TIME GRAPH T0nX = 0.01, // new value of the iterated variable T0pX = 0.01, // last time's value of the iterated variable T0cv = 3.68, // the formula scaling constant T0Xmax = 1, // upper real range limit of graph area T0Xmin = 0, // lower real range limit of graph area T0TGK, // Time Graph's vertical real-to-pixel conversion factor // FOR HENON'S STRANGE ATTRACTOR T4x, // real x-coordinate of the Henon 'chaotic body' T4y, // real y-coordinate of the Henon 'chaotic body' T4z; // swap variable for the above char *MSG[] = { // MESSAGES PERTAINING TO THE TIME GRAPH'S SINGLE SCAN MODE "Stopped because X has gone beyond range of graph.", "Stopped because the single scan has been completed." }, HL[60]; // for storing the hyperlink access command line for each tab void showLink(char *S) { XSetForeground(XD,XG,YELLOW); // draw the HELP web link for Tab0 /* n = number of characters in host name. If host name is changed you must alter the number of characters 'n' in it. */ int n = 26; XDrawString(XD,XW,XG,LM,459,"https://robmorton.website/",n); XDrawString(XD,XW,XG,LM + n * 6,459,S,strlen(S)); } // CLEAR TAB AREA: the whole of the window below the row of tab buttons void clearTab() { XSetForeground(XD,XG,BLACK); XFillRectangle(XD,XW,XG,0,38,640,442); } // TAB0------------------------------------------------------------------------ // Show and clear the messages beneath the Tab 0 control panel. void T0showMsg(char *p, int flag) { if(flag == 0) { XSetForeground(XD,XG,BLACK); XFillRectangle(XD,XW,XG,LM,425,305,20); } else { XSetForeground(XD,XG,GREEN); XDrawString(XD,XW,XG,LM,437,p,strlen(p)); } } /* DRAW THE AXES OF THE TIME GRAPH Called from only one place in T0reEx() during exposure. */ void T0showTGaxes() { /* CLEAR THE EXTENDED TIME GRAPH AREA but not if already running. This can happen when returning from a minimized state or from another desktop. */ if(IF > 0) { XSetForeground(XD,XG,BLACK); XFillRectangle(XD,XW,XG,TL - 26,TT - 5,TH + 35,TV + 11); } const int X = TL - 1; // X coordinate of the Time Graph's vertical axis int Y, H; if(EN == 0) H = TV; // Y coordinate for Equation x = cx(1 -x) else H = TV / 2; // Y coordinate for Equation x = x**2 + c Y = TT + H; T0TX = Y; // current y-coordinate of Time Graph axis XSetForeground(XD,XG,AXIS); // set axis colour XDrawLine(XD,XW,XG,X,TT,X,TT + TV); // draw the vertical axis XDrawLine(XD,XW,XG,TL,Y,X + TH,Y); // draw the horizontal axis /* PUT THE ANNOTATION NUMBERS AND GRATICULE MARKS ON THE VERTICAL SCALE According to which equation is being used: for each required annotation mark on the axis, draw a short horizontal graticule line, then display the annotation number and put the number on the scale at the side of the graticule line and increment Y to the next annotation mark. */ const char *F0[] = {"1.0","0.8","0.6","0.4","0.2","0.0"}, *F1[] = {" +2"," +1"," 0"," -1"," -2"}, **F[] = {F0,F1}, **p = F[EN]; // pointer to access the annotation numbers const int X0 = X - 25, // x-coordinate of the start of annotations X1 = X - 5; // x-coordinate of the start of graticule mark int y = TT, // initialize to top of vertical axis I, // number of annotations on the Time Graph's Y-axis dy; // vertical spacing between the annotations // For x = x * x + c the above two values are different if(EN == 0) { I = 6; dy = 32; } else { I = 5; dy = 40; } // Draw the annotations and graticule marks for the vertical scale. for(int i = 0; i < I; i++, y += dy) { XDrawString(XD,XW,XG,X0,y + 5,p[i],strlen(p[i])); XDrawLine(XD,XW,XG,X1,y,X,y); } // Draw the "Y" and "T" labels. if(EN == 0) Y = TT + 165; else Y = TT + 85; XDrawString(XD,XW,XG,X - 1,TT - 4,"X",1); XDrawString(XD,XW,XG,X + TH + 4,Y,"T",1); if(IF > 0) { T0Tn = T0TX; // begin time plotting on the Time Graph's axis int TGk; if(EN == 0) // if equation x = cx(1 - x) is selected TGk = 160; // set 160 pixels per unit real 1.0 else // if equation x = x * x + c is selected TGk = 40; // set 40 pixels per unit real 1.0 T0TGK = (double)TGk; // convert to double precision multiplication factor /* Set the initial horizontal positions of the plot 'T0Td' and the wipe 'T0Tw' on the time axis. */ T0Td = 1; // starting point of the painter T0Tw = 61; // the wiper starts 60 pixels ahead of the painter T0PC = 0; // index of starting plot in buffer array T0[] T0WC = 12; // index of starting wipe in buffer array T0[] } } /* Clear the program's window re-exposure buffers. Needed when the RUN button is hit. Called from only 2 places in T0menu(), namely Case 2.2 and Case 5. */ void T0clrBuf() { for(int i = 0; i < 101; i++) // clear the horizontal plots buffer T0[i] = 0; for(int i = 0; i < 160; i++) // clear the 'final plot' accumulator T0V[i] = 0; // for continuous spot values mode } /* REDISPLAYS THE WHOLE TIME GRAPH EACH ITERATION. Because the window event handling may operate on a different thread from the main() execution loop, this is the only really safe way to handle re-exposure after the program's window has been minimised or the user has gone to and returned from a differ- ent desktop. Called from 1 place each in T0plotTG() below and T0reEx(). */ void T0showBuf() { int a = T0[0], // full window coordinate of the start point of the plot b, // full window coordinate of the end point of the plot t = TL + 1, // left edge of Time Graph i; // index of the end point of the [next] plot for(i = 1; i < 100; i++) { // one start-point + 100 end-points b = T0[i]; // get this time's end-point vertical plot if(a > 0 && b > 0) { XSetForeground(XD,XG,GREEN); // set trace colour if(PM == 0) // if Plot Mode is set to 'Line Graph' XDrawLine(XD,XW,XG,t,a,t+4,b); else // if Plot Mode is set to 'Spot Value' XDrawLine(XD,XW,XG,t,b,t+4,b); } else { // else, [either a or b is zero], so: /* WIPE THE OLD TIME GRAPH TRACE 60 PIXELS AHEAD OFTHE NEW TRACE Get the y-coordinate of the time axis and fill the appropriate area of the time graph with black. Then set colour to white and re-draw redraw the bombed bits of the axis. It clears a 7-pixel wide area 60 pixels ahead of the current new plot. */ XSetForeground(XD,XG,BLACK); XFillRectangle(XD,XW,XG,t,TT,7,TV); XSetForeground(XD,XG,AXIS); XDrawLine(XD,XW,XG,TL,T0TX,TL + TH,T0TX); } a = b; // make next time's old plot this time's new plot t += 5; // advance to the next time plot } } /* DISPLAY THE PLOT ACCUMULATOR BUFFER Called from one place each in T0reEx() and T0plotTG(). */ void T0showVer() { if(PM == 0) return; // bail out if not in 'Spot Value' mode int t = TL + TH - 9; // horizontal coord of Time Graph's final plot XSetForeground(XD,XG,GREEN); // green trace for(int i = 0; i < TV; i++) // for each vertical position in Time graph if(T0V[i] > 0) { // if a plot is present int x = TT + i; // set vertical coordinate of plot XDrawLine(XD,XW,XG,t,x,t+4,x); // and draw short horizontal line } } // DISPLAY THE BOUNCE GRAPH AXES. Called from only one place in T0reEx() void T0showBGaxes() { /* CLEAR THE BOUNCE GRAPH AREA but not if already running. This can happen when returning from a minimized state or from another desktop. */ if(IF > 0) { // if the initialisation flag is set XSetForeground(XD,XG,BLACK); XFillRectangle(XD,XW,XG,BL - 4,BT - 12,BE + 14,BE + 18); } int X = BL - 1, // horizontal coordinate of bounce graph origin Y = BT + BE, // vertical coordinate of bounce graph origin H; if(EN == 0) // if equation being iterated is x = cx(1 -x) H = 0; // set bounce graph origin to bottom right else // else equation x = x*x + c is being iterated H = 80; // so set bounce graph origin to centre /* DRAW AND LABEL THE X & Y AXES AND DRAW THE DIAGONAL BOUNCE LINE Note that the x-axis is drawn 1 pixel to the left of the plotting area and the y-axis is drawn 1 pixel below the plotting area. */ XSetForeground(XD,XG,AXIS); // set axis colour XDrawLine(XD,XW,XG,X,Y - H,X + BE,Y - H); // draw horizontal axis XDrawLine(XD,XW,XG,X + H,BT,X + H,BT + BE); // draw vertical axis XDrawLine(XD,XW,XG,X,Y,X + BE,Y - BE); // draw the diagonal XDrawString(XD,XW,XG,X + BE + 5,Y - H + 5,"X",1); XDrawString(XD,XW,XG,X + H - 2,BT - 3,"Y",1); /* According to whether formula X = X**2 + C or X = CX(1 - X) is selected: set the co-ordinates of the graph's origin, the scaling factor (number of pixels = 1.0), max and min excursions of x or y and the x-coordinate of parabola axis. */ int BGK, // Bounce Graph's real-to-pixel conversion factor PxA; // x-coordinate of the parabola's axis T0BX = H; if(EN == 0) { T0BY = 160; BGK = 160; T0Bmax = 160; T0Bmin = 0; PxA = 80; } else { T0BY = 80; BGK = 40; T0Bmax = +80; T0Bmin = -80; PxA = 0; } /* DRAW THE PARABOLA: set the horizontal and vertical real variables then, for each small step along the horizontal axis: if equation switch EN = 1, use the square law equation else, if equation switch EN = 0, use standard logistics equation and, provided y is within range of the raph area, do a plot in pixel units. Set the upper and lower real range limits of the graph area, the real T0nX-increment for drawing the parabola and the initial value of T0nX according to whether formula X = X**2 + C or X = CX(1 - X) is selected. */ double dP, p, q; // real T0nX-increment for drawing the parabola if(EN == 0) { // if equation X = CX(1 - X) is selected T0Xmin = 0; T0Xmax = 1; dP = .001; if(IF > 0) T0nX = 0.01; } else { // else equation X = X**2 + C is selected T0Xmin = -2; T0Xmax = +2; dP = .005; if(IF > 0) T0nX = 0; } IR = 1; // assume T0nX is in range to start with int x, y; for(p = T0Xmin; p <= T0Xmax; p += dP) { // does 1000 iterations if(EN == 0) q = T0cv * p * (1 - p); // X = CX(1 - X) else q = p * p + T0cv; // X = X**2 + C // Provided they are within range of the plotting area if(q >= T0Xmin && q <= T0Xmax) { x = BL + T0BX + (int)(BGK * p); // convert real coords to pixel co-ords y = BT + T0BY - (int)(BGK * q); // add window X and Y displacements XDrawPoint(XD,XW,XG,x,y); // then draw the parabola point } } /* Don't re-Initialise the plots if already running. This can happen when returning from a minimized state or from another desktop. */ if(IF > 0) { // if not yet initialised if(EN == 0) T0Bp = 0; else // else [if already running] T0Bp = -80; T0Bw = (int)(T0TGK * T0nX); IF = 0; // all is now initialised, so clear the initialisation flag } } // PLOT THE BOUNCE GRAPH. Called from only one place in T0iter() void T0plotBG() { const int LineColour[] = { DARKG, GREEN }; for(int i = 0; i < 2; i++) { // For both the wipe line and the new line int H = BL + T0BX, // x window co-ordinate of graph origin V = BT + T0BY, // y window co-ordinate of graph origin v = V - T0Bmax, // y co-ordinate of top of graph's plotting area p = H + T0Bp, // begin-point of the horzontal bounce line s = H + T0Bw, // end-point of the horzontal bounce line q = V - T0Bp, // begin-point of the vertical bounce line r = V - T0Bw; // end-point of the vertical bounce line /* Set colour to draw [bright green] or wipe [dark green] as required, then draw or wipe the vertical and horizontal lines respectively. */ XSetForeground(XD, XG, LineColour[i]); XDrawLine(XD,XW,XG,p,q,p,r); XDrawLine(XD,XW,XG,p,r,s,r); /* Re-draw the end-point of each line that falls on the parabola in order to maintain the parabola's visibility. */ XSetForeground(XD,XG,AXIS); XDrawLine(XD,XW,XG,p,q,p,q); XDrawLine(XD,XW,XG,p,r,p,r); /* Redraw the X & Y axes. Put the 'take-off' and the 'landing' foreground dots back on the diagonal. */ XDrawLine(XD,XW,XG,H + T0Bmin,V + 1,H + T0Bmax,V + 1); XDrawLine(XD,XW,XG,H - 1,V - T0Bmin,H - 1,V - T0Bmax); XDrawLine(XD,XW,XG,p,q,p,q); XDrawLine(XD,XW,XG,s,r,s,r); // save the old plot and compute the new plot if(i == 0) { T0Bp = T0Bw; T0Bw = (int)(T0TGK * T0nX); } } // having wiped, loop back to do the new line } /* RE-EXPOSE TAB0'S STATIC CONTENT AND BUFFERED GRAPHICS Called once each from ReExpose() and from case 2.2 and case 5 in T0menu(). */ void T0reEx() { // initialise/reinitialise Tab 0 if(IF > 0) { // Don't re-initialize if already running. if(EN == 0) { // This can happen when returning from a T0nX = 0.01; // minimized state or from another desktop. T0pX = 0; } else { T0nX = 0; T0pX = 0; } } T0showTGaxes(); // re-display the Time Graph's axes T0showBGaxes(); // re-display the Bounce Graph's axes T0showBuf(); // re-display the Time Graph buffer T0showVer(); // re-display the vertical plot accumulator buffer } /* TAB0'S MENU AND BUTTONS HANDLER. Called from 1 place each in T0buts(), T0show(), T0plotTG(), T0iter() and from 3 places in T0click(). */ void T0menu(int m, // number of the menu we are dealing with int u, // number of the item selected within that menu int d) { // default item of given menu to be applied T0showMsg(NULL,0); // kill any previously displayed message. /* Declare pointers M0, M1, M2, M3, M4, M5, M6 to 7 separate arrays of strings. Set up an array M of pointers to these pointers to string arrays. Set the pointer 'p' to point to array of strings pertaining to the menu 'm' that we are dealing with this pass. */ const char *M0[] = {"2 secs","1.5 sec","1 sec","500 ms","250 ms","100 ms"}, *M1[] = {"x = cx(1 - x)","x = x * x + c"}, *M2[] = {"RUN","STOP","CLEAR"}, *M3[] = {"Line Graph","Spot Value"}, *M4[] = {"Single Scan","Continuous"}, *M5[] = { "+1.99","+2.00","+2.10","+2.50","+2.70","+2.90", "+3.00","+3.20","+3.40","+3.50","+3.55","+3.60", "+3.65","+3.68","+3.77","+3.90","+4.00","+4.01" }, *M6[] = { "+0.55","+0.50","+0.15","+0.05","+0.00","-0.05", "-0.50","-0.95","-1.00","-1.10","-1.30","-1.35", "-1.40","-1.50","-1.70","-1.75","-2.00","-2.01" }, **M[] = {M0,M1,M2,M3,M4,M5,M6}, **p = M[m]; /* A[] and B[] contain the x and y coordinates of the Plot Time, Equation Number, Run State, Plot Mode and Scan Mode selectors respectively.D[] con- tains the number of items in each menu. */ const int G[6] = {5,0,1,0,1,13}, // default item selected for each menu A[] = {35,140,140,245,245,CL,CL}, B[] = {300,300,360,300,380,104,104}, D[] = {6,2,3,2,2,18,18}, T[] = {2000000,1500000,1000000,500000,250000,100000 }; // period mu-secs const double CV[18][2] = { // SAMPLE C-VALUES TO TRY FOR EACH EQUATION {1.99, 0.55}, {2.00, 0.50}, // and beyond, x skates off to infinity {2.10, 0.15}, {2.50, 0.05}, // x climbs to a steady value {2.70, 0.00}, {2.90,-0.05}, // x stays at zero {3.00,-0.50}, {3.20,-0.95}, // x climbs to a steady negative value {3.40,-1.00}, // x oscillates with period 2 {3.50,-1.10}, // x oscillates with period 4 {3.55,-1.30}, {3.60,-1.35}, // x oscillates with period 8 {3.65,-1.40}, {3.68,-1.50}, // bounded chaotic oscillation {3.77,-1.70}, // a good demonstration of bounded chaos {3.90,-1.75}, // periodic oasis within chaos - period 3 {4.00,-2.00}, // x rapidly goes wild into infinite chaos < -2 {4.01,-2.01} }; static int U[6]; // Actual Item: preserveS all the current menu settings int X = A[m], // x-coordinate of selected menu's annotation strings Y = B[m], // y-coordinate of the top annotation string N = D[m]; // number of items in the menu being dealt with /* If d is zero, illuminate the default item 'u' for the given menu instead of the presented itemu. This is used when initializing the control panel. However, if the inaugural pass has already been done, use the item number 'u' preserved from the inaugural pass. A default value of 2 only comes from within the mouse-click event handler MEH() regarding a change in the select- ed equation, which requires a swap over to the set of C_values that pertain to new equation. */ switch(d) { // THE 'DEFAULT' SETTING HANDLER case 1: // if the default item for menu 'm' was requested u = G[m]; // use the default item if(FP == 1) // but if first exposure has already been done u = U[m]; // use last time's [the actual] item for this menu break; case 2: // If a new equation has just been selected u = U[m]; // use last time's [actual] C menu 'm' } /* The logic of cases 2 and 5 in the following switch is quite critical. These cases cater for when the program's window returns from a minimised state or upon the user's return after visiting another desktop. */ switch(m) { // SWITCH ACCORDING TO WHICH MENU WE ARE DEALING WITH case 0: PT = T[u]; break; // set the inter-plot time in microseconds case 1: EN = u; break; // set the number of the selected equation case 2: switch(u) { // SWITCH ON REQUESTED RUN MENU ITEM case 0: // if menu item zero 'RUN' has been selected IF = 1; // set the initialisation flag if(d == 0 // if a default menu item has not been asked for || FP == 1) // or we are doing the inaugural pass IF = 0; // then unset the initialisation flag break; case 2: // if menu item 2 'CLEAR' has been selected IF = 1; // set the initialisation flag T0clrBuf(); // clear the graph plots buffer T0reEx(); // repaint the static content of Tab 0 u = 1; // set selected menu item number to 'STOP' } RS = u; // make the 'run state' this menu's item number break; case 3: PM = u; break; // set the selected plot mode case 4: SM = u; break; // set the selected scan mode case 5: // C-VALUE MENU T0cv = CV[u][EN]; if(d == 0 // if a default menu item has not been asked for || FP == 0) { // or we are doing the inaugural pass IF = 1; // set the initialisation flag T0clrBuf(); // clear the graph plots buffer T0reEx(); // re-expose Tab 0 } p = M[m + EN]; // pointer to string of selected T0cv } // end of switch(m) U[m] = u; // remember selected item of this menu for subsequent pass int R = 90; // set the width of the menu background shading if(m > 4) // because the C_values menu is only half the width R = 45; /* For each item in menu 'm': if menu item 'i' is the newly selected one, set the colour to white; otherwise, set the colour to grey. Then draw the menu item at the first or next lower position in the menu list. Could usefully add a means of not having to redisplay items that haven't changed. This would save lots of pointless calls to X11 functions. */ for(int i = 0; i < N; i++, Y += IS) { XSetForeground(XD,XG,DARK); XFillRectangle(XD,XW,XG,X - 5,Y,R,15); if(i == u) XSetForeground(XD,XG,WHITE); else XSetForeground(XD,XG,GREY); XDrawString(XD,XW,XG,X,Y + 12,p[i],strlen(p[i])); } } /* Re-display the selector menus whenever the mouse is clicked over one of a menu's items. 'm' is the menu's index number and 'u' is the number of the item currently selected within that menu. Returns the number of the menu in which a new item has been selected. IMPORTANT: the y-coordinate of the mouse rectangle must be 12 pixels higher than that of the string to be clicked. Called from only one place on T0click() */ int T0buts() { #define Y0 105 // top of top [first] c-vales box #define Y1 300 // top 'y' of the Plot Time, EN & Plot Mode menus #define Y2 320 // top of first item in Time, Equation & Plot Mode menus #define Y3 340 // bottom of 1st item in Time, Equation & Plot Mode menus #define Y4 360 // top of first item in Run State menu #define Y5 380 // bottom of first item in Run State menu #define Y6 400 // botton of Run State & Scan Mode menus #define Y7 485 // bottom of bottom c-values box #define Y8 415 // bottom of bottom 'run menu' box const int BD[] = {5,0,1,0,1,13,13}; // default menu settings int m, // index number of the particular selector menu u = 0; // number of selected item within this menu // If presented mouse coordinates are inside the Plot Time menu... if(mX > 35 && mX < 100 && mY >= Y1 && mY <= Y7) { m = 0; // menu number zero [periods] u = 0; // item number zero [100 milliseconds] int Y = Y2; while(mY > Y) { // Find which menu item 'u' the mouse pointer is over. Y += IS; u++; } if(u > 5) // must not stray beyond end of the menu items array. u = 5; } // If presented mouse coordinates are inside the Equations column... else if(mX > 140 && mX < 240) { if(mY >= Y1 && mY <= Y3) { // if it is in the Equation menu... m = 1; // Equations menu u = 0; // Equation x = xc(1 - x) if(mY > Y2) u = 1; // Equation x = x * x + c EN = u; // set the selected equation } else if(mY >= Y4 && mY <= Y8) { // if it's in Run State menu... m = 2; // the 'run' menu if(mY < Y5) u = 0; // RUN else if(mY < Y6) u = 1; // STOP else u = 2; // CLEAR } else return -1; // safety net } // If presented mouse coordinates are inside the Plot or Scan Mode menus: else if(mX > 245 && mX < 335) { if(mY >= Y1 && mY <= Y3 ) { // If inside Plot Mode box m = 3; // Plot Mode u = 0; // Line Graph if(mY > Y2) u = 1; // Spot Value PM = u; // Set the selected Plot Mode } else if(mY >= Y5 && mY <= Y7) { // If inside Scan Mode box m = 4; // Scan Mode u = 0; // Single Scan if(mY > Y6) u = 1; // Continuous Mode SM = u; // Set the selected Scan Mode } else return -1; // safety net } // If presented mouse coordinates are within the C-value selector: else if(mX > CL && mX < 605 && mY >= Y0 && mY <= Y7) { m = 5; // SET C menu u = 0; // 1.90 // Find which menu item 'u' the mouse pointer is over. int Y = Y0 + IS; while(mY > Y) { Y += IS; u++; } if(u > 18) // must not stray beyond end of menu item array. u = 18; } else return -1; // safety net T0menu(m,u,0); // re-display modified menu selection return m; } /* DISPLAY THE STATIC CONTENT OF THE DifEqns TAB. Called only from 1 place in showTab(). */ void T0show() { if(TB != 0) return; char *S; XSetForeground(XD,XG,YELLOW); S = "PLOT EVERY:"; XDrawString(XD,XW,XG, 35,290,S,strlen(S)); S = "EQUATION:"; XDrawString(XD,XW,XG,140,290,S,strlen(S)); S = "PLOT MODE:"; XDrawString(XD,XW,XG,245,290,S,strlen(S)); S = "SCAN MODE:"; XDrawString(XD,XW,XG,245,370,S,strlen(S)); S = "SET 'c'"; XDrawString(XD,XW,XG, CL, 95,S,strlen(S)); for(int i = 0; i < 6; i++) // Initialise the menus T0menu(i,0,1); showLink("chaos/difeqn.html"); XSetForeground(XD,XG,YELLOW); // draw the HELP web link for Tab0 } // DifEqn's TAB BUTTON WAS CLICKED. Called from only 1 place in MEH(). void T0click() { int a = T0buts(); if(a == 1) { // if the selected equation has changed T0menu(5,0,2); // set 'c' to its default value T0menu(2,2,0); // flip the 'c' menus then hit the CLEAR button } if(a == 3 || a == 4) // if plot mode or scan mode changed T0menu(2,2,0); // hit the CLEAR button } /* PLOT ONE ITERATION OF THE SELECTED FORMULA ON THE TIME GRAPH. Called from only one place in T0iter() */ void T0plotTG() { T0Tp = T0Tn; // copy last time's new plot to this time's old plot /* Compute this time's new vertical plot coordinate. This is the vertical coordinate of the Time Graph's current horizontal axis T0TX [note that T0TX is different according to which of the two equations is being plotted] minus the [possibly bipolar] number of pixels corresponding to the 'real' vertical value of the plot. However, since the bipolar number of pixels can never go above the top of the graph area, T0Tn will always be the positive number of pixels that the plot is below the top of the program window. */ T0Tn = T0TX - (int)(T0TGK * T0nX); /* The buffer array T0[] maintains the plots that must be re-painted on the screen by the ReExpose() function after the program's window is re-displayed after having been minimised. Each plot is the vertical drop [in pixels] from the very top of the program's window area. */ T0[T0PC] = T0Tn; // save new vertical plot in buffer array if(SM == 0) { // if in single-shot scanning mode int x = TL + T0Td; // horizontal co-ordinate of new plot XSetForeground(XD,XG,GREEN); // set trace to green if(PM == 0) // if Plot Mode is set to 'Line Graph' XDrawLine(XD,XW,XG,x,T0Tp,x+4,T0Tn); else // else it's set to 'Spot Value' XDrawLine(XD,XW,XG,x,T0Tn,x+4,T0Tn); } else { // Else, if in continuous scanning mode: T0[T0WC] = 0; // indicate that this plot should be wiped T0showBuf(); // display the whole graph to date from the buffer } if((T0Tw += 5) > TH) { // when the wiper reaches end of the time axis T0Tw = 1; // loop back to start of time axis } /* If the trace has reached the end of the time axis: if doing spot plots, cancel the time axis advance; else, if doing a continuous graph, then if the program is in single-scan mode, halt at the end of the first Time Graph scan and signal to stop the program on return. */ if((T0Td += 5) > TH) { // if painter has reached end of plotting area if(PM == 1) { // if doing spot values T0Td -= 5; // back up the time axis by 5 pixels, and T0PC -= 1; // back up one position in the image buffer T0WC = 0; // don't let the wiper re-start T0V[T0Tn - TT] = 1; // put the plot in the 'vertical accumulator' T0showVer(); // re-display the accumulated vertical plots } else { // else doing line graph T0Td = 1; // so fly back to the start of the time axis and T0PC = 0; // also fly back to the beginning of the buffer } if(SM == 0) { // if in singe-shot mode T0menu(2,1,0); // hit the STOP button T0showMsg(MSG[1],1); } } /* The following pertains to the buffer array T0[], which contains the plots that must be re-painted on the screen after the program's window is re-displayed after having been minimised. */ if(++T0PC > 100) // If next plot would be beyond the end of buffer array T0PC = 0; // set plot-index back to the beginning of the buffer. if(PM == 0) // provided we're in Line Graph mode if(++T0WC > 100) // If next wipe would be beyond the end of buffer array T0WC = 0; // set wipe-index back to the beginning of the buffer. } /* DO ONE ITERATION OF THE SELECTED EQUATION: if the equation switch flag set to 1, use the square law equation, otherwise use standard logistics equation. If, as a result, T0nX is now outside the graph area, set to 'out of range' and stop the iteration process. */ void T0iter() { T0pX = T0nX; // save last time's value of X if(EN == 0) T0nX = T0cv * T0nX * (1 - T0nX); else T0nX = T0nX * T0nX + T0cv; if(T0nX < T0Xmin || T0nX > T0Xmax) { IR = 0; // out of range T0menu(2,1,0); // hit the STOP button T0showMsg(MSG[0],1); // show 'out of range' message } else { T0plotTG(); // show new plot on Time Graph T0plotBG(); // show new plot on Bounce Graph } } // TAB1------------------------------------------------------------------------ /* DISPLAY THE STATIC CONTENT OF THE BifMap1 TAB. Called only from 1 place each in showTab(), T1reEx() and T1click(). */ void T1show() { if(TB != 1) return; // bail out if called other than from Tab 1 int j, k; // utility loop variables XSetForeground(XD,XG,WHITE); // set colour for scale lines XDrawLine(XD,XW,XG,T1H,T1V,T1H,T1W); // draw the vertical scale line int di = T1S / 5; // increment between scale marks on vertical scale j = T1H - 5; // start of scale marks for vertical scale /* At each marked position on vertical scale, draw the vertical scale's scale marks.*/ for(int i = 0; i <= T1S; i += di) { int x = T1V + i; XDrawLine(XD,XW,XG,j,x,T1H,x); // draw the vertical scale marks } int T1N = T1S * 3; // length of the horizontal scale j = T1W + 5; // bottom of scale marks for horizontal scale di = T1S / 2; // increment between scale marks XDrawLine(XD,XW,XG,T1H,T1W,T1H+T1N,T1W); // draw the vertical scale line for(int i = 0; i <= T1N; i += di) { // At each scale mark position int x = T1H + i; // horiz position of scale mark XDrawLine(XD,XW,XG,x,T1W,x,j); // draw a scale mark. } int h = T1H - 18, // horizontal co-ordinate of vertical scale figures v = T1V + 4; // start position of vertical co-ordinates XDrawString(XD,XW,XG,h + 4,v,"1",1); // number the vertical axis XDrawString(XD,XW,XG,h,v + (int)(T1S * .2),".8",2); XDrawString(XD,XW,XG,h,v + (int)(T1S * .4),".8",2); XDrawString(XD,XW,XG,h,v + (int)(T1S * .6),".8",2); XDrawString(XD,XW,XG,h,v + (int)(T1S * .8),".8",2); XDrawString(XD,XW,XG,h + 3,v + T1S,"0",1); h = T1H - 2; // start position of horizontal scale's figures v = T1V + T1S + 18; // vert co-ord of base of figures for horiz scale XDrawString(XD,XW,XG,h,v,"1",1); // number the horizontal axis XDrawString(XD,XW,XG,h + T1S,v,"2",1); XDrawString(XD,XW,XG,h + (int)(T1S*2),v,"3",1); XDrawString(XD,XW,XG,h + (int)(T1S*3),v,"4",1); XSetForeground(XD,XG,WHITE); // set colour for lettering XDrawString(XD,XW,XG,T1H - 2,T1V - 4,"x",1); // label vertical axis XDrawString(XD,XW,XG,T1H + 485,T1W + 2,"c",1); // label horizontal axis XDrawString(XD,XW,XG,T1H + 195,T1W + 30,"BIFURCATION MAP",15); XDrawString(XD,XW,XG,T1H + 195,T1W + 45,"for x = xc(1-x)",15); v = 300; int d = 18; XDrawString(XD,XW,XG,T1H + 65,v += d, "Each column of pixels in the above graph shows the envelope",59); XDrawString(XD,XW,XG,T1H + 65,v += d, "within which 'x' is free to wander for one specific value of",60); XDrawString(XD,XW,XG,T1H + 65,v += d, "the constant 'c'. Thus the graph as a whole shows the envelopes",63); XDrawString(XD,XW,XG,T1H + 65,v += d, "of freedom for 'x' for the full range of 'c' from 1 to 4.",57); XSetForeground(XD,XG,GREEN); XDrawString(XD,XW,XG,T1H+55,T1W + 165, "Click anywhere on the graph area to start or restart the trace.",63); showLink("chaos/bifmap.html"); T1x = 0; // start map from right-hand edge PT = 60000; // BifMap1 inter-plot time frame (microseconds) T1f = 0; // clear the 'finished' flag } // BifMap1's TAB BUTTON WAS CLICKED. Called from only one place in MEH(). void T1click() { // clearTab(); T1show(); /* 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 XDrawLine() unnecessarily. */ for(int j = 0; j < T1cMAX; j++) for(int k = 0; k < T1rMAX; k++) T1A[j][k] = BLACK; RS = 0; // set the main() program loop running } void T1iter() { // ITERATE BIFURCATION MAP 1. Called only from Iterate(). if(T1f) return; int C = GREEN; // set trace colour if(T1x < T1cMAX) { // if not yet reached end of c axis... // the 'c' value (real) corresponding to the current x-pixel (int) double T1c = (double)(T1x) / T1S + 1, T1r = .01; // Reset 'r' for a new iteration run for(int i = 0; i < 10000; i++){ // for N (further) iterations if(i > 100) // When 'r' has had time to settle, C = DARKG; // change colour. T1r = T1c * T1r * (1 - T1r); // advance the iteration process if(T1r >= 1 || T1r <= 0) // bail out if has 'r' gone off screen break; int y = (int)(T1r * T1S); // y-plot for current value of 'r' int K = T1A[T1x][y]; // current colour of corresponding pixel // if the pixel needs to be drawn if(K == BLACK || C == DARKG && K == GREEN) { T1A[T1x][y] = C; // set pixel element to required trace colour int h = T1H + T1x, // horizontal window co-ordinate of the new plot v = T1W - y; // vertical window co-ordinate of the new plot XSetForeground(XD,XG,C); // for final stretch of current line XDrawPoint(XD,XW,XG,h,v); // draw the dot on the screen } } T1x++; // advance to the right by one pixel } else T1f = 1; // else the attractor is now complete } /* RE-EXPOSE BifMap1 FROM THE BUFFER ARRAY. Called from only one place in ReExpose(). */ void T1reEx() { T1show(); // display the tab's static content for(int i = 0; i < T1cMAX; i++) for(int j = 0; j < T1rMAX; j++) { int C = T1A[i][j]; // get colour of current pixel if(C > 0) { // provided it's not BLACK XSetForeground(XD,XG,C); // set pixel colour XDrawPoint(XD,XW,XG,T1H+i,T1W-j); // draw the dot on the screen } } RS = 0; // set the main() program loop running } // TAB2------------------------------------------------------------------------ /* SHOW THE CONTENT OF THE BifMap2 TAB AND SET IT UP READY TO RUN Called from one place each in showTab(), T2click() and T2reEx(). */ void T2show() { if(TB != 2) return; // bail out if called from anywhere but Tab 2 int i, j, k, x, y; // set colour for scale lines and draw the horizontal and vertical axes XSetForeground(XD,XG,0xFFFFFF); XDrawLine(XD,XW,XG,T2Xb,T2Yb-200,T2Xb,T2Yb+200); XDrawLine(XD,XW,XG,T2XB,T2Yb,T2Xb+50,T2Yb); j = T2Yb - 5; // draw horizontal scale marks k = T2Yb + 5; int P, Q = T2Xb + 51; for(i = T2XB; i < Q; i += 50) XDrawLine(XD,XW,XG,i,j,i,k); j = T2Xb - 5; // draw vertical scale marks k = T2Xb + 5; P = T2Yb - 200, Q = T2Yb + 201; for(i = P; i < Q; i += 50) XDrawLine(XD,XW,XG,j,i,k,i); x = T2Xb - 32; y = T2Yb + 4; XDrawString(XD,XW,XG,x,y -200,"+2.0",4); // number the vertical axis XDrawString(XD,XW,XG,x,y -150,"+1.5",4); XDrawString(XD,XW,XG,x,y -100,"+1.0",4); XDrawString(XD,XW,XG,x,y - 50,"+0.5",4); XDrawString(XD,XW,XG,x,y + 50,"-0.5",4); XDrawString(XD,XW,XG,x,y +100,"-1.0",4); XDrawString(XD,XW,XG,x,y +150,"-1.5",4); XDrawString(XD,XW,XG,x,y +200,"-2.0",4); x = T2Xb - 14; y = T2Yb - 8; XDrawString(XD,XW,XG,x+50,y,"+0.5",4); // number the horizontal scale XDrawString(XD,XW,XG,x-50,y,"-0.5",4); XDrawString(XD,XW,XG,x-100,y,"-1.0",4); XDrawString(XD,XW,XG,x-150,y,"-1.5",4); XDrawString(XD,XW,XG,x-200,y,"-2.0",4); XDrawString(XD,XW,XG,T2Xb+10,T2Yb - 197,"x",1); // label the vertical axis XDrawString(XD,XW,XG,T2Xb-210,T2Yb+3,"c",1); // label the horizontal XDrawString(XD,XW,XG,LM,80,"BIFURCATION MAP",15); XDrawString(XD,XW,XG,LM,95,"for x = x * x + c",17); int v = 115, d = 18; XDrawString(XD,XW,XG,LM,v += d,"Each column of pixels in the",28); XDrawString(XD,XW,XG,LM,v += d,"above graph shows the envelope",30); XDrawString(XD,XW,XG,LM,v += d,"within which 'x' is free to",27); XDrawString(XD,XW,XG,LM,v += d,"wander for one specific value",29); XDrawString(XD,XW,XG,LM,v += d,"of the constant 'c'. Thus the",29); XDrawString(XD,XW,XG,LM,v += d,"graph as a whole shows the",26); XDrawString(XD,XW,XG,LM,v += d,"envelopes of freedom for 'x'",28); XDrawString(XD,XW,XG,LM,v += d,"for the full range of 'c'",25); XDrawString(XD,XW,XG,LM,v += d,"from +0.5 to -2.",16); XSetForeground(XD,XG,GREEN); XDrawString(XD,XW,XG,LM,320,"Click anywhere on the graph area",32); XDrawString(XD,XW,XG,LM,335,"to start or restart the trace.",30); showLink("chaos/bifmap.html"); T2x = T2H - 1; // start map from the right-hand edge PT = 60000; // BifMap2 inter-plot time frame (microseconds) T2f = 0; // bifurcation map not yet completely displayed RS = 0; // set Tab 2 program running } // BifMap2's TAB BUTTON WAS CLICKED. Called only from one place in MEH(). void T2click() { clearTab(); // clear the program window below the Tab Buttons T2show(); // display Tab 2's static content /* 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 XDrawLine() unnecessarily. */ for(int j = 0; j < T2H; j++) for(int k = 0; k < T2V; k++) T2A[j][k] = BLACK; RS = 0; // set the main() program loop running } // ITERATE BIFURCATION MAP 2. Called from only one place in Iterate(). void T2iter() { if(T2f) return; // don't re-display if the graph image is complete int C = GREEN; // set current trace colour if(T2x > 0) { // if not yet reached left side of window // the c value (real) corresponding to the current x-pixel (int) double T2c = (double)(T2x - 200) / 100, T2r = 0; // Reset 'r' for a new iteration run for(int i = 0; i < T2N; i++) { // for 'N' (further) iterations if(i > 100) // when 'r' has had time to settle C = DARKG; // change colour T2r = T2r * T2r + T2c; // advance the iteration process if(T2r > 2 || T2r < -1.99) // If 'r' has gone off screen, break; // bail out. int y = T2Yb - (int)(T2r * 100); // y-plot for current value of 'r' int K = T2A[T2x][y-T2TM]; // current colour of corresponding pixel /* If the pixel needs to be drawn, set the pixel element to appropriate trace colour. */ if(K == BLACK || C == DARKG && K == GREEN) { T2A[T2x][y-T2TM] = C; XSetForeground(XD,XG,C); // for final stretch of the current line XDrawPoint(XD,XW,XG,T2XB+T2x,y); // draw the dot on the screen } } T2x--; // advance to the left by one pixel } else T2f = 1; // else the graph is now complete } /* RE-EXPOSE BifMap2 FROM ITS BUFFER ARRAY Called only from one place in ReExpose(). */ void T2reEx() { T2show(); // display Tab2's static content for(int i = 0; i < T2H; i++) for(int j = 0; j < T2V; j++) { int C = T2A[i][j]; // get colour for current pixel if(C > 0) { // provided it isn't BLACK XSetForeground(XD,XG,C); // set the pixel colour XDrawPoint(XD,XW,XG,T2XB+i,T2TM+j); // draw the dot on the screen } } RS = 0; // set the main() program loop running } // TAB3------------------------------------------------------------------------ /* THE MANDELBROT SET: Display the tab's visible content and set the Mandelbrot Set generator to the run-state. Called from one place each in showTab() and T3click(). */ void T3show() { for(int j = 0; j < T3YE; j++) { // for each of the 180 lines in picture int I = 0, // starting dot of next single-colour stretch of line i = 0; // to find next pixel in current line with a different colour int C = T3A[I][j]; // make colour of first dot the reference colour while(++i < T3XE){ // while not yet reached end of current line // if this dot is a different colour from the previous dot if(C != T3A[i][j]) { // set colour then draw a line from start dot to previous dot XSetForeground(XD,XG,C); XDrawLine(XD,XW,XG,I+T3H,j+T3V,i-1+T3H,j+T3V); C = T3A[I = i][j]; // make reference colour that of this dot } } // set colour for final stretch of current line then draw it XSetForeground(XD,XG,C); XDrawLine(XD,XW,XG,I+T3H,j+T3V,i-1+T3H,j+T3V); } XSetForeground(XD,XG,WHITE); XDrawString(XD,XW,XG,LM,80,"THE MANDELBROT SET",18); int v = 110, d = 18; XDrawString(XD,XW,XG,LM,v += d,"To generate the Mandelbrot Set,",31); XDrawString(XD,XW,XG,LM,v += d,"the following 3 equations are",29); XDrawString(XD,XW,XG,LM,v += d,"iterated until the values of",28); XDrawString(XD,XW,XG,LM,v += d,"the real component 'x' and the",30); XDrawString(XD,XW,XG,LM,v += d,"imaginary component 'y' of the",30); XDrawString(XD,XW,XG,LM,v += d,"complex variable converge [stop",31); XDrawString(XD,XW,XG,LM,v += d,"changing significantly]. The",28); XDrawString(XD,XW,XG,LM,v += d,"colour of each pixel represents",31); XDrawString(XD,XW,XG,LM,v += d,"how many iterations it took for",31); XDrawString(XD,XW,XG,LM,v += d,"'x' and 'y' to converge. The",28); XDrawString(XD,XW,XG,LM,v += d,"brighter areas show where",25); XDrawString(XD,XW,XG,LM,v += d,"convergence took longer.",24); XSetForeground(XD,XG,GREEN); XDrawString(XD,XW,XG,LM,380,"Click anywhere on the graph area",32); XDrawString(XD,XW,XG,LM,398,"to start or restart the trace.",30); showLink("chaos/mandel.html"); T3v = 0; // current line number of graph from top of raster PT = 100000; // Mandelbrot inter-plot time frame (microseconds) T3f = 0; // Mandelbrot Set not yet completely displayed RS = 0; // set Mandelbrot Set generator running } // MANDELBROT'S TAB BUTTON WAS CLICKED. Called from only one place in MEH(). void T3click() { clearTab(); for(int i = 0; i < T3XE; i++) // clear the colour array to BLACK for(int j = 0; j < T3YE; j++) T3A[i][j] = BLACK; T3show(); // display the Mandelbrot set Tab's static content RS = 0; // activate the main() run loop } // ITERATE THE MANDELBROT SET. Called only from one place in Iterate() void T3iter() { if(T3f > 0) return; // bail out if the Mandelbrot Set graph is complete if(T3v < T3YE) { // if last line not yet painted int I = 0, // start pixel for current colour i = 0, // current pixel of current colour C = T3A[I][T3v]; // make colour of first dot the reference colour while(++i < T3XE) { // for each pixel on the current line... double x = .01, // reset x & y values to start another convergence test y = .01, z, /* Convert the current pixel co-ordinates to real co-ordinates on the complex plane. */ cx = (double)(i-T3X)/100, cy = (double)(T3Y-T3v)/100; for(int k = 0; k < 128; k++) { // if not yet completed 128 iterations z = x * y; // Compute and add in the next term of x = x * x - y * y + cx; // the complex series. y = z + z + cy; /* If the value of the complex quantity has gone beyond the bounds of the display area. */ if(x < -2.6 || x > 1.6 ||y < -2.75 || y > 2.75) { if(k > 19) k = 19; // max number of iterations for divergence T3A[i][T3v] = T3C[k]; // store the pixel colour break; } } /* If the colour has changed, set it according to the number of itera- tions then draw the plot and set current colour as reference colour. */ if(C != T3A[i][T3v]) { XSetForeground(XD,XG,C); XDrawLine(XD,XW,XG,I+T3H,T3v+T3V,i-1+T3H,T3v+T3V); C = T3A[I = i][T3v]; } } // set the colour for the final stretch of the current line then draw it XSetForeground(XD,XG,C); XDrawLine(XD,XW,XG,I+T3H,T3v+T3V,i-1+T3H,T3v+T3V); T3v++; // advance downwards to next line } else T3f = 1; // else the Mandelbrot Set graph has been completed } // TAB4------------------------------------------------------------------------ /* HENON'S STRANGE ATTRACTOR: show the static content of Tab 4. Called Called from one place each in showTab() and T4click(). */ void T4show() { for(int i = 0; i < T4X; i++) // create the x-axis graticule T4A[i][150] = GREY; for(int j = 0; j < T4Y; j++) // create the y-axis graticule T4A[150][j] = GREY; for(int j = 0; j < T4Y; j++) { // for each of the lines of the graph int I = 0, // starting dot of next single-C stretch of line i = 0, // to find next pixel in current line with different C C = T4A[I][j]; // make C of first dot the reference C /* While not yet reached end of current line: if this dot is a different colour from the previous dot, set the colour to that of the previous dot, draw a line from the start dot to the previous dot then make the reference colour that of this dot. */ while(++i < T4X) { if(C != T4A[i][j]){ XSetForeground(XD,XG,C); XDrawLine(XD,XW,XG,T4H+I,T4V+j,T4H+i-1,T4V+j); C = T4A[I = i][j]; } } // set colour for final stretch of current line, then draw it XSetForeground(XD,XG,C); XDrawLine(XD,XW,XG,T4H+I,T4V+j,T4H+i-1,T4V+j); } XSetForeground(XD,XG,GREEN); XDrawString(XD,XW,XG,332,415,"Click anywhere on the graph area",32); XDrawString(XD,XW,XG,332,430," to start or restart the trace. ",32); XSetForeground(XD,XG,WHITE); XDrawString(XD,XW,XG,T4H+302,T4V+155,"X",1); XDrawString(XD,XW,XG,T4H+148,T4V - 3,"Y",1); int v = 80, d = 20; XDrawString(XD,XW,XG,LM+10,v,"HENON'S STRANGE ATTRACTOR",25); v += 10; XDrawString(XD,XW,XG,LM,v += d,"Henon's Strange Attractor re-",29); XDrawString(XD,XW,XG,LM,v += d,"represents the behaviour of a",29); XDrawString(XD,XW,XG,LM,v += d,"complex dynamical system that",29); XDrawString(XD,XW,XG,LM,v += d,"is described in terms of three",30); XDrawString(XD,XW,XG,LM,v += d,"interdependent variables x, y",29); XDrawString(XD,XW,XG,LM,v += d,"and z. The 2 variables x & y",28); XDrawString(XD,XW,XG,LM,v += d,"form a dissipative function:",28); v += d; XDrawString(XD,XW,XG,LM+10,v += d," x = 1.4 * x * x + y - 1",24); XDrawString(XD,XW,XG,LM+10,v += d," y = 0.3 * x",12); v += d; XDrawString(XD,XW,XG,LM,v += d,"The system is driven (pumped)",29); XDrawString(XD,XW,XG,LM,v += d,"by a periodic variable z. The",29); XDrawString(XD,XW,XG,LM,v += d,"axis of z is perpendicular to",29); XDrawString(XD,XW,XG,LM,v += d,"to the screen.",14); showLink("chaos/henon.html"); T4x = 0, // real x-coordinate of 'chaotic body' T4y = 0; // real y-coordinate of 'chaotic body' PT = 150000; // inter-plot time frame (microseconds) RS = 0; // set Henon Attractor generator running } /* RE-DRAW THE HENON TAB UPON RETURNING FROM A MINIMISED STATE OR FROM ANOTHER DESKTOP. Called only from one place in ReExpose(). */ void T4reEx() { for(int i = 0; i < 180; i++) // Re-draw the current state of for(int j = 0; j < 180; j++) { // the Henon trace after return- XSetForeground(XD,XG,T4A[i][j]); // ing from a minimised window. XDrawPoint(XD,XW,XG,T4H+i,T4V+j); } } // HENON'S TAB BUTTON WAS CLICKED. Called from only one place in MEH() void T4click() { clearTab(); for(int i = 0; i < T4X; i++) // clear Henon colour array to BLACK for(int j = 0; j < T4Y; j++) T4A[i][j] = BLACK; T4show(); // display the tab's static content RS = 0; // set it running } // ITERATE HENON'S STRANGE ATTRACTOR. Called from only one place in Iterate() void T4iter() { T4z = T4x; // 'z' inherits last time's value of 'x' T4x = 1.4 * T4x * T4x + 0.3 * T4y - 1; // advance 'x' T4y = T4z; // advance 'y' /* The 'real' origin is at pixel coordinates 150,150. 'x' scale = 'y' scale = 100 pixels per unit 'real'. */ int i = 150 + (int)(T4x * 100), // Compute the pixel co-ordinates cor- j = 150 - (int)(T4y * 100); // responding to new values of 'x' & 'y' T4A[i][j] = GREEN; // put the plot on the 'canvas' XSetForeground(XD,XG,GREEN); // set colour for the plot XDrawPoint(XD,XW,XG,T4H+i,T4V+j); // draw the plot in the window } // TAB5------------------------------------------------------------------------ /* RESET THE TRACK RECORDER'S VARIABLES TO THEIR INITIAL STATES Called from one place each in T5click() and T5iter(). */ void T5Treset() { for(int i = 0; i < 2; i++) { T5T[i].a = 1.570796325; // initialise main angle to 90 degrees [pi/2] T5T[i].A = 0; // quadrant main angle T5T[i].q = 0; // phase off-set angle T5T[i].Q = 0; // quadrant phase angle T5T[i].P = 0; // phase cycle counter T5T[i].f = 0; // 'first main cycle complete' flag T5T[i].c = 0; // 'current main cycle completed' flag } } /* LISSAJOUS: DISPLAY THE GRAPH AXES AND ANNOTATIONS Called from 1 place each in showTab(), T5click() and T5iter(). */ void T5Lshow() { XSetForeground(XD,XG,BLACK); // clear the plotting area XFillRectangle(XD,XW,XG,T5LH,T5LV,T5LH+200,T5LV+200); // show the graph's title and frequency annotation in WHITE XSetForeground(XD,XG,WHITE); XDrawString(XD,XW,XG,T5LH+T5LB-55,T5LV-15, "LISSAJOUS FIGURES",17); if(T5m == 0) XDrawString(XD,XW,XG,T5LH+T5LB-73,T5LV+192, "Y-FREQ ALMOST = X-FREQ",22); else XDrawString(XD,XW,XG,T5LH+T5LB-83,T5LV+192, "Y-FREQ ALMOST TWICE X-FREQ",26); XDrawString(XD,XW,XG,T5TH+25,T5TV-15, "TRACK RECORDER TEST",19); // display the x and y axes XDrawLine(XD,XW,XG,T5LH+T5LX-T5LR,T5LV+T5LY,T5LH+T5LX+T5LR,T5LV+T5LY); XDrawLine(XD,XW,XG,T5LH+T5LX,T5LV+T5LY-T5LR,T5LH+T5LX,T5LV+T5LY+T5LR); XDrawString(XD,XW,XG,T5LH+T5LX+T5LR+3,T5LV+T5LY+4,"X",1); // mark X-axis XDrawString(XD,XW,XG,T5LH+T5LX-3,T5LV+T5LY-T5LR-4,"Y",1); // mark Y-axis int v = T1W + 55, d = 18; XDrawString(XD,XW,XG,T1H+55,v += d, "The track recorder test is an application of Lissajous Figures",62); XDrawString(XD,XW,XG,T1H+55,v += d, "that I used in 1968 to test the linearity of the servo-driven",61); XDrawString(XD,XW,XG,T1H+55,v += d, "pen of a flight simulator's track recorder. Any irregularities",62); XDrawString(XD,XW,XG,T1H+55,v += d, "in the shape of the moving-phase ellipse are easily spotted by",62); XDrawString(XD,XW,XG,T1H+55,v += d, "the experienced eye.",20); v += 12; XSetForeground(XD,XG,GREEN); XDrawString(XD,XW,XG,T1H+55,v += d, "Click anywhere on the graph area to start or restart the trace.",63); showLink("chaos/lissajou.html"); PT = 120000; // inter-plot time frame (microseconds) RS = 0; // set Lissajous Figues generator running } /* RE-EXPOSE THE TRACK RECORDER TRACE Called from only one place in ReExpose(). */ void T5reEx() { for(int i = 0; i < 180; i++) // Re-draw the current state for(int j = 0; j < 180; j++) { // of the Track Recorder trace XSetForeground(XD,XG,T5TD[i][j]); // after returning from a XDrawPoint(XD,XW,XG,T5TH+i,T5TV+j); // minimised window. } } /* THE MOUSE WAS CLICKED WITHIN THE TRACK RECORDER'S TAB Called from 2 places in MEH(). */ void T5click() { clearTab(); // clear the whole area of the tab below the tab buttons if(RS > 0) { // if the Track Recorder isn't running for(int i = 0 ; i < 180 ; i++) // clear the Track Recorder's buffer for(int j = 0 ; j < 180 ; j++) T5TD[i][j] = BLACK; } T5TT = 1; // set trace colour switch T5TF = 1; // set trigger to flip the track trace colour T5TC = WHITE; // initial trace colour for track recorder T5Treset(); // reset Track Recorder to starting conditions RS = 0; // set it running if it is not yet running T5Lshow(); // display the static content of the Tab } /* LISSAJOUS: FINDS VALUE OF CO-ORDINATE CORRESPONDING TO INCREMENTED ANGLE The index 'i' specifies which coordinate is being dealt with: i = 0 for the the x-coordinate of the paint trace i = 1 for the the y-coordinate of the paint trace i = 2 for the the x-coordinate of the wiper trace i = 3 for the the y-coordinate of the wiper trace Called from 4 places in T5iter(). */ int T5Lplot(int p, int m, int b, int i) { int // copy the indexed variables to local variables a = T5a[i], d = T5d[i], Q = T5Q[i], s = T5s[i], f = T5f[i], x = T5S[a] >> 7; // get x or y co-ord corresponding to the given angle if(s) // if the output should be positive x = b + x; // add it to the co-ordinate origin bias else // else the output should be negative x = b - x; // so take it from the co-ordinate origin bias a += d << m; // Advance main angle by appropriate amount (can be > 1). if(a < 0) { // If, as a result, we're now at or below bottom of table a = p - a; // wrap back up table; add this quadrant's phase increment d = 1; // and set main angle to move up the table Q++; // increment quadrant counter if(s == 0) // reverse sign for the next 2 quadrants s = 1; else s = 0; } else if(a > 180) { // if, as a result, we're now at or above top of table a = 360 - a - p; // wrap back down table; subtract this quadrant's Q++; // phase increment & increment the quadrant counter d = -1; // then set to move angle DOWN the table } if(f == 0 && Q > 6) // if wiper start flag not set and 3 quadrants + 80 f = 1; // degrees has been completed, set wiper start flag // re-pack the local variables into the appropriate indexed storage T5a[i] = a; T5d[i] = d; T5Q[i] = Q; T5s[i] = s; T5f[i] = f; return(x); // return new 'x' or 'y' co-ordinate } /* FINDS VALUE OF CO-ORDINATE CORRESPONDING TO INCREMENTED ANGLE Called from 2 places in T5iter(). */ int T5Tplot(double p, int i) { int x = 85 + (int)(80 * sin(T5T[i].a + T5T[i].q)); /* Increment the main phase angle 'a' by the angular velocity increment 'delta-a' [0.00872664626] then, if as a result, it now exceeds 2pi: */ if((T5T[i].a += 0.00872664626) > 6.2831873) { T5T[i].a = 0; // reset the main phase angle to 'zero' T5T[i].c = 1; // and set the 'cycle completed' flag } // if painter has completed the first 6 radians if((T5T[i].A += 0.00872664626) > 6.256) { T5T[i].A = 0; T5T[i].f = 1; // signal the wiper to start } // when the incremented phase angle has surpassed 2pi, reset it to zero. if((T5T[i].q += p) > 6.2831853) T5T[i].q = 0; // if just completed another two quadrants if((T5T[i].Q += p) > 1.570796325) { T5T[i].P++; // increment the phase cycle counter T5T[i].Q = 0; // reset the quadrant phase differential } return(x); // return the new x or y co-ordinate } /* GENERATES LISSAJOUS FIGURES [Jules Antoine Lissajous 1822-1880, a French physicist]. This function is called from only one place in Iterate(). */ void T5iter() { int x, y; // temporary co-ordinate variables if(T5Q[0] > 2160) // while 2160 quadrants not yet completed T5w = 1; // trigger a scope trace wipe if(T5w) { // if it's time to wipe the trace if (T5m == 0) // if just finished a double ellipse T5m = 1; // start to do a single ellipse else // otherwise T5m = 0; // start a new double ellipse for(int i = 0; i < 4; i++) { T5a[i] = 0; // main phase angle T5d[i] = 1; // main phase angle incrementer T5Q[i] = 0; // clear the quadrant counter T5s[i] = 1; // indicates the output sign T5f[i] = 0; // wiper start flag } T5Lshow(); // re-display of the annotated axes T5q = 4; // number of quadrants to update T5w = 0; // cancel the 'wipe' request } while(T5Q[0] < T5q) { // while current main cycle not yet finished x = T5Lplot(1,0,T5LX,0); // get the new 'x' plot y = T5Lplot(0,T5m,T5LY,2); // get the new 'y' plot XSetForeground(XD,XG,GREEN); // set trace colour XDrawPoint(XD,XW,XG,T5LH+x,T5LV+y); // draw the next dot if(T5f[0]) { // If painter's completed at least 1 cycle x = T5Lplot(1,0,T5LX,1); // get the new 'x' plot to be wiped y = T5Lplot(0,T5m,T5LY,3); // get the new 'y' plot to be wiped /* Provided the plot is not located on one of the axes, wipe the trace; otherwise, repaint it in the axis colour. This makes sure the axes are preserved in the right colour. */ if(x != T5LX && y != T5LY) XSetForeground(XD,XG,BLACK); else XSetForeground(XD,XG,WHITE); XDrawPoint(XD,XW,XG,T5LH+x,T5LV+y); } } T5q = T5Q[0] + 4; // reset 'T5q' to 4 quadrants ahead // ITERATE THE TRACK RECORDER if(T5T[1].P > 1) { // if just finished a complete cycle T5Treset(); // reset all phase variables if(T5TT) { // if just finished painting T5TC = DARK; // set the wiping colour T5TT = 0; // and reset the painting flag } else { // else, if just finished wiping T5TC = WHITE; // so set the painting colour T5TT = 1; // and set flag = 'painting' } } x = T5Tplot(0,0); // advance the 'x' coordinate y = T5Tplot(0.001090830783,1); // advance the 'y' coordinate XSetForeground(XD,XG,T5TC); // set the plot colour XDrawPoint(XD,XW,XG,T5TH+x,T5TV+y); // draw the plot on screen T5TD[x][y] = T5TC; // put advanced dot in raster buffer } // COMMON FUNCTIONS------------------------------------------------------------ /* DISPLAY ALL THE TITLES, BUTTONS AND DEFAULT IN-FILL FOR CURRENT TAB which is specified by the numeric value of the global variable 'TB': Called from only 1 place each in main() and MEH(). */ void showTab() { XSetForeground(XD,XG,BLACK); XFillRectangle(XD,XW,XG,24,55,600,455); // clear the Tab area const char // TAB BUTTON LABELS *BA[8] = { "DifEqns","BifMap1","BifMap2","Mandelbrot","Henon","Lissajous" }; const int NC[8] = {7,7,7,10,5,9}, // number of characters in each annotation NB[8] = {20,20,20,11,25,14}; // x-bias for each annotation XSetForeground(XD,XG,DARK); // shade button background int h = LM; // horizontal left edge of first button for(int i = 0; i < 6; i++) { // draw each button XFillRectangle(XD,XW,XG,h,17,BW-12,21); h += BW; } h = LM; // horizontal left edge of first button for(int i = 0; i < 6; i++) { // annotate each button if(TB == i) // if annotating the selected button XSetForeground(XD,XG,WHITE); // annotated it in WHITE else // if it's any of the other buttons XSetForeground(XD,XG,GREY); // annotate it in GREY XDrawString(XD,XW,XG,h + *(NB + i),32,*(BA + i),*(NC + i)); h += BW; } clearTab(); // clear window area below tab buttons switch(TB) { // SWITCH ON TAB NUMBER case 0: T0show(); break; // paint the DifEqns tab case 1: T1show(); break; // paint the BifMap1 tab case 2: T2show(); break; // paint the BifMap2 tab case 3: T3show(); break; // paint the Mandelbrot tab case 4: T4show(); break; // paint the Hénon tab case 5: T5Lshow(); // paint the Lissajous tab } } /* DRAWS THE CURRENT STATE OF THE TRACE IN THE CURRENTLY SELECTED TAB AFTER RETURNING FROM ANOTHER TAB OR FROM A MINIMISED WINDOW. Called from one place each in MEH() and main(). */ void ReExpose() { switch(TB) { case 0: T0reEx(); break; // Re-expose the difference equation iterator case 1: T1reEx(); break; // Re-expose the BifMap1 graph case 2: T2reEx(); break; // Re-expose the BifMap2 graph case 4: T4reEx(); break; // Re-expose Henon's Strange Attractor case 5: T5reEx(); break; // Re-expose Lissajous & Track Recorder } } /* MOUSE EVENT HANDLER. Handles clicks of the left mouse button [Button 1]. The 'x' and 'y' coordinates of the click point within the application window are already in the global variables 'mX' and 'mY'. Called only from one place in main()'s event loop. */ void MEH() { int i; // means 'not clicked on any button' if(mY > 16 && mY < 38) { // if click on one of the Tab buttons for(i = 0; i < 6; i++) { // for each button int y = i * BW; // button offset in pixels if(mX > 35 + y && mX < 117 + y) // if click was within the button break; // break out of the for() loop } if(i < 6) { // provided 'i' is between '0' and '4' TB = i; // set Tab Number to index number of 'break' showTab(); // display the Tab's content switch(TB) { case 0: ReExpose(); break; // in case of running content case 5: T5click(); // in case track recorder running } } } // else, if the mouse was clicked within the hyperlink area: else if(mX > 34 && mY > 448 && mX < 299 && mY < 461) switch(TB) { // here we deal with the HELP hyperlinks case 0: sprintf(HL+41,"difeqn.html &"); system(HL); break; case 1: sprintf(HL+41,"bifmap.html &"); system(HL); break; case 2: sprintf(HL+41,"bifmap.html &"); system(HL); break; case 3: sprintf(HL+41,"mandel.html &"); system(HL); break; case 4: sprintf(HL+41,"henon.html &"); system(HL); break; case 5: sprintf(HL+41,"lissajou.html &"); system(HL); } /* Otherwise the click was somewhere else in the current tab's area, which is a signal that current Tab's iteration program should be started or re-started. */ else switch(TB) { case 0: T0click(); break; // DifEqns Tab's button click handler case 1: T1click(); break; // BifMap1 Tab's button click handler case 2: T2click(); break; // BifMap2 Tab's button click handler case 3: T3click(); break; // Mandelbrot Tab's button was clicked case 4: T4click(); break; // Hénon Tab's button was clicked case 5: T5click(); // Lissajous Tab's button was clicked } } /* DO ONE ITERATION OF THE SELECTED TAB'S PROGRAM: Called from only one place in main() [see below]. */ void Iterate() { switch(TB) { case 0: T0iter(); break; // iterate the difference equation case 1: T1iter(); break; // iterate Bifurcation Map 1 case 2: T2iter(); break; // iterate Bifurcation Map 2 case 3: T3iter(); break; // iterate the Mandelbrot Set case 4: T4iter(); break; // iterate the Henon Strange Attractor case 5: T5iter(); // iterate Lissajous Figures & Track Recorder } } // MAIN------------------------------------------------------------------------ int main(void) { XEvent e; // reference to an event from the created window int s; // number of the current default screen /* Check that you can actually open the X-display by getting a non-NULL reference to a display. Else send an error message to the terminal. */ XD = XOpenDisplay(NULL); if(XD == NULL) { fprintf(stderr,"Cannot open display\n"); exit(1); } s = DefaultScreen(XD); // get the number of the default screen XG = DefaultGC(XD,s); // set the graphics context to use XW = XCreateSimpleWindow( // create an application window XD, // within the default display RootWindow(XD,s), // within the root window [desktop] 10, 10, // position of top right corner 640, 480, // width and height of the window 1, // border width in pixels BlackPixel(XD,s), // colour of window's border BlackPixel(XD,s) // colour of window's background ); // set the program's window title XStoreName(XD,XW, "Chaos Theory Demonstrator by Robert John Morton YE572246C" ); /* Make input possible from the window controls and mouse then display the new window on the screen. Not all these bit masks are needed so comment out those that are not needed. */ XSelectInput( XD,XW, ExposureMask | // KeyPressMask | // KeyReleaseMask | // PointerMotionMask | ButtonPressMask // | // ButtonReleaseMask | // StructureNotifyMask ); XMapWindow(XD,XW); // make window viewable on desktop when required XFlush(XD); // flush the window's output buffer /* Define a unique indentifier WM_DELETE_WINDOW (referred to as an atom) that will invoke the X11 protocol that closes windows upon the display 'XD' we have just created. [At least I think this is what it means] */ Atom WM_DELETE_WINDOW = XInternAtom(XD,"WM_DELETE_WINDOW",False); /* Make a 'delete window' event from this particular program visible within this program as a ClientMessage event. */ XSetWMProtocols(XD,XW,&WM_DELETE_WINDOW,1); // form the static content of the hyperlink access command sprintf(HL,"xdg-open " "https://robmorton.website/" "chaos/"); // 41 chars /* The event-handling [or 'run'] loop [a permanent loop] broken only by the 'break' function in response to an external event. */ while(1) { if(XPending(XD) > 0) { // Provided there are events on the event queue, XNextEvent(XD,&e); // go fetch the first one from the queue. if(e.type == Expose) { // If it is the 'window ready' event, showTab(); // display current tab's content in window ReExpose(); // expose current dynamic state of program FP = 1; // first pass of exposure now completed } else if(e.type == ButtonPress) { // if a mouse button was clicked if(e.xbutton.button == Button1) { // provided it's the left button mX = e.xbutton.x; // set the window's x-coord of the click mY = e.xbutton.y; // set the window's y-coord of the click MEH(); // pass through the mouse event handler } // then return and continue this loop } } /* Else if a box control has been clicked, break out of the permanent while() loop, close the window and exit. Should also check here for other client message types; however, as the only protocol registered above is WM_DELETE_WINDOW, it is safe for this program. */ else if (e.type == ClientMessage) break; else // else no external event was captured so run the loop if(RS == 0) { // provided the program is in the 'run' state Iterate(); // update to the next plot on the relevant graph usleep(PT); // pause the thread for 'plot time' microseconds } } // end of outer permanent while() loop XCloseDisplay(XD); // clear this window from the X11 display 'XD' return 0; // return that everything went OK } // end of main() /* To do List: 1) Create a small text entry field to enable the user to enter very precise arbitrary values for 'c'. */