/** Lissajous Figure Track Recorder Test by Robert John Morton * Completed: Fri 10 Oct 1997 * Converted to JFrame application program: Wed 15 Feb 2017 */ /* A program to test the linearity of the servos of a large area track recorder by tracing out a variant of Lissajous figues. */ import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; class SinCoord { // CLASS TO CREATE AND MANIPULATE x-y CO-ORDINATE OBJECTS double a; // main phase angle double A; // quadrant main angle double q; // phase angle double Q; // quadrant phase angle int pc; // phase cycle counter boolean f; // 'first main cycle complete' flag boolean c; // 'current main cycle completed' flag // FINDS VALUE OF CO-ORDINATE CORRESPONDING TO INCREMENTED ANGLE public int Advance(double d, double p, int m, int R, int B) { int x = B + (int)(R * Math.sin(a * m + q)); // on completion of each cycle if((a += d) > 6.2831873) { a = 0; // reset the angle to 'zero' c = true; // set the 'cycle completed' flag } // if painter completed first 6 radians if ((A += d) > 6.256) { A = 0; f = true; // signal the wiper to start } // when phase angle = 2Pi if ((q += p) > 6.2831853) q = 0; // reset it to zero // if just completed another two quadrants if ((Q += p) > 1.570796325) { pc++; // increment phase cycle counter Q = 0; // reset the quadrant phase differential } return(x); // return new x or y co-ordinate } // RESET THE VALUES IN A CO-ORDIATE OBJECT public void Reset() { pc = 0; // phase cycle counter a = 0; // main angle A = 0; // quadrant main angle q = 0; // phase angle Q = 0; // quadrant phase angle f = false; // 'first main cycle complete' flag c = false; // 'current main cycle completed' flag } } public class trackrec extends JPanel implements Runnable { private int XE, // Horizontal extent of window and JFrame. YE, // Vertical extent of window and JFrame. XT = 180, // Horizontal extent of trace area YT = 180, // Vertical extent of trace area Xoff, // Horizontal off-set of trace area Yoff, // Vertical off-set of trace area m = 1; // y-frequency multiplier private static double d = .00872664626, // delta-a primary angular velocity p = .001090830783; // phase differential incrementer private long tf = 15, // inter plot time frame t; // time a new plot is due to begin private boolean Trace = true, // trace colour switch flip = true; // trigger to flip track trace colour private Color colour, // track trace colour Dots[][] = new Color[180][180]; // to preserve the trace so far private Thread T; // declare a thread reference variable private SinCoord T1, // reference to trackrec X1 = new SinCoord(), // create a new x co-ordinate object Y1 = new SinCoord(); // create a new y co-ordinate object private BufferedImage I; // off-screen image on which to plot the graph private Graphics2D gi; // graphics reference for off-screen image public trackrec(int XE, int YE, int TH) { this.XE = XE; // horizontal [X] dimension of the JPanel this.YE = YE; // vertical [Y] dimension of the JPanel Xoff=(XE-XT) >> 1; // horizontal off-set of trace area Yoff=((YE-YT) >> 1)-TH; // vertical off-set of trace area setBounds(0,0,XE,YE); // the JPanel fills the whole of the JFrame area /* Disable any automatic arranging of objects on this panel. The application programmer in this case needs free reign to plot the graphical representation of Lissajou's Figures. */ setLayout(null); /* Create a Buffered Image "I" on which to plot Lissajou's Figures off- screen and get its graphics context "gi" for realising the plots. */ I = new BufferedImage(XT,YT,BufferedImage.TYPE_INT_RGB); gi = I.createGraphics(); T = new Thread(this); // create a new program thread T.start(); // start run() method with the new thread } public void paint(Graphics g) { g.drawImage(I,Xoff,Yoff, null); // display the buffered image } // PLOT/RE-PLOT THE PICTURE SO FAR private void initialPlot() { // for each of the 180 lines in the trace for(int x = 0; x < XT; x++) { int Y = 0; // starting dot of next single-colour stretch of line y = 0; /* to find the next pixel in the current line with a different colour */ Color c = Dots[x][Y]; // make colour of first dot the reference colour while(++y < YT) { // while not yet reached end of current line if(c != Dots[x][y]){ // If dot different colour from prev dot gi.setColor(c); // Set colour to colour of previous dot. gi.drawLine(x,Y,x,y-1); // Draw line from start dot to prev dot c = Dots[x][Y = y]; // make reference colour that of this dot } } gi.setColor(c); // set colour for final stretch of current line gi.drawLine(x,Y,x,y-1); // draw last stretch of current line } } private void currentPlot() { // update the plots if (flip) { // if just completed a cycle if (Trace) { // if just finished wiping colour = Color.black; // prepare to paint the trace Trace = false; // and set flag = 'painting' } else { // else colour = Color.white; // prepare to wipe the trace Trace = true; // and set flag = 'wiping' } X1.Reset(); Y1.Reset(); // reset SinCoord objects X1.a = 1.570796325; // initialise main angle Y1.a = 1.570796325; // to 90 degrees (pi/2) flip = false; // cancel 'time to flip' flag } gi.setColor(colour); // paint/wipe in approp colour int x = X1.Advance(d,p,1,80,85); // advance to the next x-position int y = Y1.Advance(d,0,m,80,85); // advance to the next y-position gi.drawLine(x,y,x,y); // Draw the next bit of line Dots[x][y] = colour; // put the dot in the raster buffer } // run the Track Recorder painting thread public void run() { // set raster to background colour for(int x = 0 ; x < 180 ; x++) for(int y = 0 ; y < 180 ; y++) Dots[x][y] = Color.lightGray; // Note the system time at which the first plotting cycle must end. t = System.currentTimeMillis() + tf; initialPlot(); while(T != null) { // permanent loop broken only by an external event if (X1.pc > 1) // if just completed a phase quadrant flip = true; // trigger a trace colour flip currentPlot(); repaint(); // request a rationalised call to paint() // Get the amount of 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 /* Catch any possible interrupts from GUI or browser. If one is caught, trigger a reset to restart the program. */ catch(InterruptedException e){flip = true;} // Set time at which the next plottinf cycle must end t = System.currentTimeMillis() + tf; } } }