/** * Time Graph Class for Rob's * Difference Equation Iteration Demonstrator Applet * @author Robert J Morton UK-YE572246C * @version 27 November 1997 modified 07 August 2009, 18 April 2012 */ /* Plots values of a real variable x which is passed to it against time. It plots directly onto the screen and an off-screen saver image. */ import java.awt.*; // for graphics operations (GUI) import javax.swing.*; // library for constructing Swing GUI class timegr extends JPanel{ private static final int yMAX = 160, // height of the wipe rectange tMAX = 500, // extent of time axis HB = 25, VB = 15; // window co-ordinates of top left corner of graph private int t, // current plot position on time axis u, // current wipe position on time axis y1, // local co-ordinate of previous plot y2, // local co-ordinate of current plot Y; // local co-ordinates of graph's real origin private String M = "Stopped because the single scan has been completed."; private static final Color axis = Color.gray, // colour of the graph's axes trace = new Color(0, 128, 64); // colour of time line trace private boolean PlotMode = false, // true when plotting points only: not joined lines ScanMode = true, // true when scanning continuously: not single shot EqnFlag = false, // true when using equation X = X**2 + C ready = false; // inhibits the initial paint after construction private double k; // vertical real-to-pixel conversion factor private difeqnap ap; // instance reference of the main applet private setbutt sb; // instance reference of the setbutt class private formula f; // instance reference of formula iteration class private Graphics gi; // graphics context reference for off-screen image private Color bg; // background colour private Font font; // font for graph axis annotations private FontMetrics fm; // dimensions of lettering etc for chosen font private Image TG = null; // instance reference to time-graph image timegr(difeqnap ap, Color bg, formula f, Graphics g, Font font, Image TG) { this.ap = ap; // instance reference of the main applet this.f = f; // instance reference of the formula iteration class this.bg = bg; // background colour for the time graph this.TG = TG; // copy instance reference to time-graph image gi = g; // graphics reference of the bounce-graph image this.font = font; // copy font reference to local variable // get dimensions etc for this font's lettering fm = getFontMetrics(font); } void setSB(setbutt b) {sb = b;} //instance reference to the setbutt class /* The following 4 methods enable external classes to set which equation to use, select 'line graph' or 'spot value' plotting, select 'single-scan' or 'continuous' and inform that the applet's run loop is initialised. */ void setEF(boolean b) {EqnFlag = b;} void setPM(boolean b) {PlotMode =b;} void setSM(boolean b) {ScanMode = b;} void setReady(boolean b) {ready = b;} void reset() { // INITIALISE/RESET THE TIME-GRAPH IMAGE t = 1; // horizontal position of plot on time axis u = 61; // horizontal position of wipe on time axis /* Set the background colour for time-graph image and clear the whole image to the background colour. */ gi.setColor(bg); gi.fillRect(0, 0, 545, 180); int K = 160; // pixels per unit real for x = cx(1-x) int x = 218, y = 352; // axis positions for x=cx(1-x) if(EqnFlag) { // if using equation x=x*2+c K = 40; // set number of pixels per unit real for x=x**2+c x = 298; y = 272; // set axis positions for x=x*2+c graph } k = (double)K; // convert to a double multiplication factor // Set the colour in which to draw the graph's axes and draw the y-axis gi.setColor(axis); gi.drawLine(HB, VB, HB, VB + yMAX); // Relative position of the origin of the vertical axis. Y = 160; if(EqnFlag) Y = 80; gi.drawLine(HB, VB + Y, HB + tMAX, VB + Y); // draw the time-axis String s=""; // for numbers of the graph's vertical scale x = HB - 6; // x co-ordinate of the vertical scale numbers int n; // actual value of the scale number gi.setFont(font); // set font for axis annotations if(EqnFlag) { // If we're using the equation: x = x**2 + c n = 2; // start at the top of the scale /* Draw successive scale marks and their correspond- ing annotation figures every 40 pixels descending. */ for(y = 0 ; y < 161 ; y += 40, n--) { int v = VB + y; // vertical pixel coordinate for next scale mark gi.drawLine(x, v, HB, v); // draw the short scale mark line s = String.valueOf(n); // draw the figures onto the scale gi.drawString(s, x - fm.stringWidth(s), v + 3); } } else { // Else we're using the equation: x = cx(1 - x) n = 10; // start at the top of the scale /* Draw successive scale marks and their correspond- ing annotation figures every 32 pixels descending. */ for(y = 0 ; y < 161 ; y += 32) { int v = VB + y; // vertical pixel coordinate for next scale mark gi.drawLine(x, v, HB, v); // draw the short scale mark line /* If we're at the top or the bottom of the scale, use the literal number, otherwise calculate the number. */ if(n == 10) s = "1"; else if(n == 0) s = "0"; else s = "." + String.valueOf(n); // Draw the figures onto the scale and decrement to the next mark gi.drawString(s, x - fm.stringWidth(s), v + 3); n -= 2; } } gi.setColor(Color.black); // lettering is in black gi.drawString("x", 23, 11); // draw the 'x' label // Set the vertical coordinate of the 't' label then draw the 't' label int z = 178; if(EqnFlag) z = 98; gi.drawString("t", HB + tMAX + 6, z); y2 = Y; // starting point for plotting the graph ready = true; // true = the applet run() loop has been initialised repaint(); // paint the graph axes onto the panel } // (re)draw on screen from time graph image public void paint(Graphics g) { g.drawImage(TG, 0, 0, null); } // (re)draw on screen from time graph image public void update(Graphics g) { g.drawImage(TG, 0, 0, null); } // FIND POSITION OF NEXT PLOT POINT AND DRAW IT ON THE OFF-SCREEN IMAGE void Plot() { double x = f.getX(); // get new value of x from iteration formula y1 = y2; // this time's old plot is last time's new plot y2 = Y - (int)(k * x); // co-ords of this time's new plot Line(gi); // put new plot on off-screen image /* If in continuous scan mode, wipe oldest plot on off- screen image then wrap the time axis if necessary. */ if(ScanMode) Wipe(gi); if((u += 5) > tMAX) u = 1; /* If the trace has now reached the end of the time-axis, we have to do some housekeeping depending on what kind of trace we are doing. */ if((t += 5) > tMAX) { if(PlotMode) // If doing spot plots, t -= 5; // cancel the time axis advance. else { // Else we're doing a line graph, /* so: if in continuous mode, fly back to the start of the time axis; else we're in single-shot mode so halt at the end of the first time-graph scan, display the message and exit. */ if(ScanMode) t = 1; else { sb.Stop(); ap.showStatus(M); return; } } } repaint(); return; //not yet at end of scan } // DRAW A LINE BETWEEN THE OLD AND NEW PLOTS private void Line(Graphics g) { int x = HB + t, // x co-ordinate of old plot y = VB + y2; // y co-ordinate of new plot g.setColor(trace); // set the color for the graph trace /* If doing spot plots, draw the new plot, else if doing continuous line, draw line between old and new plots. */ if(PlotMode) g.drawLine(x, y, x + 5, y); else g.drawLine(x, VB + y1, x + 5, y); } // WIPE THE OLD TRACE 60 PIXELS AHEAD OF THE NEW TRACE private void Wipe(Graphics g) { int y = VB + Y; // y co-ordinate of the time axis /* Set the background colour for the time graph and wipe the next bit of the old trace. */ g.setColor(bg); g.fillRect(HB + u, VB, 7, yMAX); /* Set colour in which to re-draw the bombed bit of axis and replace the wiped bit of the time axis. */ g.setColor(axis); g.drawLine(HB, y, HB + tMAX, y); } }