Rob's Lissajous Figures Applet http://home.clara.net/robmorton/projects/client/lissajou.htm

Below is the Java source code for the Lissajous figures generator. Beware! It is only the second applet which Rob wrote while learning Java. Nevertheless, it works.

It was developed on Sun Microsystems' Java Developers' Kit 1.0.1 running under IBM's OS/2 Warp 4. It ran perfectly on the OS/2 desktop. However, when run in Microsoft Internet Explorer 3.0, the Y-axis line seems to get bombed for no reason which Rob could see at the time.

alt="Your browser understands the <APPLET> tag but isn't running the applet, for some reason." Your browser is completely ignoring the <APPLET> tag!

The source file Lissajou.java.

Full Source Listing

/** Lissajous Figures demonstrator by Robert J Morton */

/*  This version uses a sine table for reason of speed so that
    the applet does not take up too much of the host machine's
    processing time. Finished 01 October 1997 */

import java.awt.*;
import java.lang.System;

class TabCoord
{
   int a = 0;                                //main phase angle
   int d = 1;                                //main phase angle incrementer
   int Q = 0;                                //quadrant counter
   boolean s = true;                         //indicates the output sign
   boolean f = false;                        //flag to trigger start of wiper


   static int[] S = {                        //Sine Table giving sin(a) 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 };


   public int Advance(int p, int m, int b)   //FINDS VALUE OF CO-ORDINATE CORRESPONDING TO INCREMENTED ANGLE
   {
      int x = S[a] >> 7;                     //get x or y co-ord corresponding to given angle
      if (s)                                 //if the output should be positive
         x = b + x;                          //add it to the co-ordinate origin bias
      else
         x = b - x;                          //take it from the co-ordinate origin bias
      a += d << m;                           //advance the main angle by the appropriate amount (can be > 1)
      if (a < 0) {                           //if as a result we are now at or 'below' the bottom of the table
         a = p - a;                          //wrap back up the table and add this quadrant's phase increment
         d = 1;                              //set main angle to move up the table
         Q++;                                //quadrant counter
         s = !s;                             //reverse sign for the next 2 quadrants
      }                                      //alternatively...
      else if (a > 180) {                    //if as a result we are now at or 'above' the top of the table
         a = 360 - a - p;                    //wrap back down the table and subtract this quadrant's phase increment
         Q++;                                //quadrant counter
         d = -1;                             //set to move angle down the table
      }
      if (!f)                                //if wiper start flag not set
         if (Q > 6)                          //if 3 quadrants + 80 degrees completed
            f = true;                        //set the wiper start flag
      return(x);                             //return new x or y co-ordinate
   }

   public void Reset()                       //RESET THE VALUES IN A CO-ORDIATE OBJECT
   {
      a = 0;                                 //main phase angle
      d = 1;                                 //main phase angle incrementer
      Q = 0;                                 //clear the quadrant counter
      s = true;                              //indicates the output sign
      f = false;                             //wiper start flag
   }
}


public class Lissajou extends java.applet.Applet
                      implements Runnable
{
   int R = 80;                               //radius of the trace vector
   int B = R + 10;                           //bias of centre of graph (both x and y the same)
   int W = B + B;                            //width of graphics window
   int Bx = B - 4;                           //bias for the x-axis annotation 'X'
   int dBy;                                  //half the height of the letter 'X'
   int By = B + 5;                           //bias for the y-axis annotation 'Y'
   int Z = 192;                              //vertical position of annotations
   int q = 4;                                //number of quadrants to paint on each call to update()

   long TF = 200;                            //total Time Frame for an update cycle
   long T;                                   //time at which the next new cycle is due to begin
   int m = 1;                                //x-axis frequency multiplier
   boolean wipe = true;                      //trigger flag to wipe scope trace
   Thread ScopeTrace;                        //declare a thread reference variable
   Font font;                                //and a font reference for annotations
   FontMetrics fm;                           //dimensions of letters etc of chosen font
   TabCoord X1;                              //x co-ordinate of the painter trace
   TabCoord Y1;                              //y co-ordinate of the painter trace
   TabCoord X2;                              //x co-ordinate of the wiper trace
   TabCoord Y2;                              //y co-ordinate of the wiper trace
   String S1 = "Y-FREQ ALMOST = X-FREQ";     //single ellipse's annotation string
   int B1;                                   //centrallising bias for S1
   String S2 = "Y-FREQ ALMOST 2 * X-FREQ";   //double ellipse's annotation string
   int B2;                                   //centrallising bias for S2
   String s;                                 //current annotation string
   int b;                                    //centrallising bias for current annotation string
   Color tr;                                 //trace colour
   Color ax;                                 //axes colour

   public void init()
   {
      X1 = new TabCoord();                   //create co-ordinate objects for painter trace
      Y1 = new TabCoord();
      X2 = new TabCoord();                   //create co-ordinate objects for wiper trace
      Y2 = new TabCoord();
      tr = new Color( 0, 255, 128);          //create special bright green for trace
      ax = new Color(64, 192,   0);          //create special browny-green for axes
      font = new Font("Dialog", Font.PLAIN, 12);
      fm = getFontMetrics(font);             //get the metrics (pixel dimensions of height, leading etc.) for this font
      dBy = fm.getAscent() >> 1;             //half the height of the 'X'
      B1 = B - (fm.stringWidth(S1) >> 1);    //width of first trace's annotation
      B2 = B - (fm.stringWidth(S2) >> 1);    //width of second trace's annotation
      T = System.currentTimeMillis() + TF;   //set end time for current update time frame
   }

   public void paint(Graphics g)             //SET UP THE INITIAL 'SCOPE SCREEN'
   {
      g.setFont(font);                       //set up the annotation font
      g.setColor(Color.black);               //set appropriate wiping colour
      g.fillRect(0,0,180,200);               //clear the applet window
      g.setColor(Color.lightGray);           //set colour to paint new trace
      g.drawString(s, b, Z);                 //display the graph's annotation
      g.drawString("X",Bx + R + 3,By + dBy); //put the 'X' at the end of the X-axis
      g.drawString("Y", Bx - 3, By - R - 4); //put the 'Y' at the top of the Y-axis
      g.setColor(ax);                        //set colour to paint new trace
      g.drawLine(Bx - R, By, Bx + R, By);    //display the x axis and label it
      g.drawLine(Bx, By - R, Bx, By + R);    //display the y axis and label it
   }

   public void update(Graphics g)            //UPDATE THE 'SCOPE TRACE'
   {
      int x, y;                              //temporary co-ordinate variables
      if (wipe) {                            //if it's time to wipe trace
         if (m == 0) {                       //if just finished a double
            m = 1; s = S2; b = B2;           //start to do a single ellipse
         } else {                            //else
            m = 0; s = S1; b = B1;           //start a new double
         }
         X1.Reset(); Y1.Reset();             //reset Coord objects for the painter trace
         X2.Reset(); Y2.Reset();             //reset Coord objects for the wiper trace
         paint(g);                           //re-display the blank window with axes and annotations
         q = 4;                              //number of quadrants to update
         wipe = false;                       //cancel the 'wipe' request
      }
      while(X1.Q < q) {                      //while current main cycle not yet finished
         x = X1.Advance(1, 0, Bx);           //get the new x plot
         y = Y1.Advance(0, m, By);           //get the new y plot
         g.setColor(tr);                     //paint the trace in black
         g.drawLine(x, y, x, y);             //Draw the next bit of line
         if (X1.f) {                         //If painter has completed at least one cycle
            x = X2.Advance(1, 0, Bx);        //get the new x plot to be wiped
            y = Y2.Advance(0, m, By);        //get the new y plot to be wiped
            if(x != Bx && y != By)           //Provided the plot is not on one of the axes
               g.setColor(Color.black);      //wipe the trace
            else g.setColor(ax);             //else repaint it in the axis colour
            g.drawLine(x, y, x, y);          //and thereby preserve the axial pixels
         }
     }
     q = X1.Q + 4;                           //reset 'q' to 4 quadrants ahead
   }

   public void run()                         //RUN THE ScopeTrace THREAD
   {
      while(true) {                          //permanent loop broken by external event
         if (X1.Q > 1080)                    //while 1080 quadrants not yet done
            wipe = true;                     //trigger a scope screen wipe
         repaint();                          //sets up a call to update()
         long s = T                          //get time remaining in this cycle's time frame
                - System.currentTimeMillis();
         if (s < 5) s = 5;                   //in case host PC is too slow for ideal trace speed
         try {
            Thread.currentThread().sleep(s); //sleep for remaining time
         } catch (InterruptedException e) {  //allow browser events to break the thread
            wipe = true;                     //triggers a 'reset' if applet is restarted
         }                                   //happens if you return to applet's HTML page
         T = System.currentTimeMillis()      //set finish time of next time frame
           + TF;
      }
   }

   public void start() {                     //Start program thread by
      ScopeTrace = new Thread(this);         //creating the thread object
      ScopeTrace.start();                    //and starting it running
   }                                         //[returns a call to run()]

   public void stop() {                      //Stop program thread
      ScopeTrace.stop();
   }
}

This page's parent within this Web Site. About this Web Site. Its home page. Email its Author.