The simphys package

Explanation of the simphys package
Jan Tobochnik and Harvey Gould

Test Program Simulation interface ControlFrame ControlMenu
Table Plot WorldGraphics

Introduction to the simphys package

The simphys package is designed to implement some basic tasks that are useful in many simulations of physical systems. Objects can be created from the classes in this package without knowing the details of how the classes are written. However, we also list the classes below for interested user who may wish to modify or extend them. Any class that uses the simphys package must include the statement import simphys.*; at the beginning of the file containing the class. In the following we list the classes and describe what they do and how they can be used.

ControlFrame
This class places any number of Buttons and TextFields on a Frame, and gives the user a simple method for input and output of data through Textfields and the control of the program through Buttons. The user only needs to specify the names for the Buttons, the names for Labels that appear under the TextFields, and optionally, initial strings to appear in the Textfields. An example appears below:
String Blist[] = {"start", "stop", "reset"};
String Llist[] = {"Temperature", "Lattice Size"};
double Tlist[] = {2.2,64};
ControlFrame cf = new ControlFrame(S, Llist, Tlist, Blist);
This example creates three buttons and two TextFields with Labels below them. When a button is pressed or a TextField changed, another object needs to know. To accomplish this communication, the object S must implement the interface Simulation. Why do we use an interface? Why not just write a class with the necessary methods? The answer is that the interface lets ControlFrame have an instantiation of any class as an argument as long as that class implements the Simulation interface. Any class that implements Simulation must include the methods shown in the following example.
public class Example implements Simulation
   {

   public void buttonAction(int k)
   {
      // respond to Buttons numbered from 0 to number of Buttons - 1
   }

   public void textAction(int k, double x)
   {
      // respond to  TextFields numbered from 0 to number of TextFields - 1
   }

   public void menuAction(int k)
   {
      // respond to Menus that perform actions numbered from 0 to number of Menu items - 1
   }

   public void dialogAction(int k, double x)
   {
      // respond to Menus that call up dialog boxes numbered from 0 to number of Menu items - 1
   }

}
This interface is used with ControlMenu also. In method textAction and dialogAction, the double x is the value entered into a TextField by the user.

ControlMenu
This class has the same functionality as ControlFrame. Two Menus are created, one called "Actions" and one called "Parameters" which play the same role as the Buttons and TextFields, respectively. The constructor for ControlMenu works in the same way as the constructor for ControlFrame.

Table
This class displays a table of formated numbers separated by tabs in a Frame. The constructor arguments are the TableName and a String array of table headings for the columns. The class contains methods for setting formats for the entries to the table, adding entries to a row (one at a time or up to the first 5 entries at once), and displaying a row. The Table can be copied from the Frame and pasted directly into a spreadsheet or plotting program. Below we show an example.

   "String h[] = {"Column A","Column B","Column C"};
   Table tb = new Table("Example Table",h);
   String pattern[] = {"#.0000", "#.00","#.000"};
   tb.setFormat(pattern,10); // width of each column is 10 characters
   tb.setEntry(0,3.173);     // set zeroth element of row
   tb.setEntry(1,999.9173);  // set first element of row
   tb.setEntry(2,3891.73);   // set second element of row
   tb.displayEntries();  // display first row
   tb.setEntries(3317.3,99.173,817.3);  // set all three elements of row at once
   tb.displayEntries();  // display second row
The result of the above code is to produce a table that looks like the following:
  Column A  Column B  Column C
    3.1730    999.91  3891.730
 3317.3000     99.17   817.300
Plot
This class is used to draw simple plots in separate Frames. The constructor arguments set up the title for the plot, labels for the horizontal and vertical axes, the type of plot, the minimum and maximum values for x and y, the symbol size, and symbol type. The data must be encoded in two double arrays that are passed to the method plotPoints. Below we show the constructor and public methods used in this class.
public Plot(String title, String xLabel, String yLabel, int nAxes,
       double xmin,double xmax, double ymin, double ymax, int symbolSize, String symbolType)

public Plot(int nAxes,double xmin,double xmax, double ymin, double ymax)

/* nAxes = 1, one bottom horizontal axis, ymin should = 0
   nAxes = 2, one left vertical axis, one bottom horizontal axis, xmin and ymin should = 0
   nAxes = 3, one center vertical axis, one bottom horizontal axis, xmin should = -xmax, ymin should = 0
   nAxes = 4, crossed axes, xmin should = -xmax and ymin should = -ymax
   nAxes = 5, crossed axes, only x > 0, xmin should = 0 and ymin should = -ymax
   nAxes = anything else, no axes drawn.
   symbolType = "fillsquare" or "opensquare" or "opencircle" or "fillcircle"
   symbolSize = 1 for point (default), otherwise gives size in pixels
*/

public void plotPoints(double x[], double y[]);
WorldGraphics
This class creates a Frame and defines many of the methods in the Graphics class in terms of world coordinates where the vertical coordinate increases up the screen. It also uses double coordinates instead of integers, and works with an offscreen Image buffer. The constructor is shown below. The first four arguments define the minimum and maximum coordinates in the horizontal and vertical directions. The argument size defines the horizontal size of the frame in pixels, and the last two arguments define where the upper left hand corner of the frame is on the screen in pixels with yLoc = 0 at the top of the screen.
public WorldGraphics(String title,
       double xmin,double xmax, double ymin, double ymax, int size, int xLoc, int yLoc)

The offscreen buffer can be used to do animation and speed up graphics. To do this call the method setToScreen(false), which will cause all subsequent drawing methods to draw only to the offscreen buffer and not to the screen. Then to draw the buffer to the screen, call drawBuffer(). Animation can be achieved for an image moving from one place to another by clearing the old location of the image using clearRect(x,y,dx,dy), drawing the image at the new location, and then calling drawBuffer().

A test program showing how these classes are instantiated and used is shown below. Try running this program and testing the Buttons, TextFields, and menu items. Find the code in the program for the things you see on the screen.

import simphys.*;

   public class Test implements Simulation
   {
      ControlFrame cf;
      ControlMenu cm ;
      WorldGraphics wg;

   public static void main(String args[])
   {
      Test u = new  Test();
   }


   public Test()
   {
      String h[] = {"zero","first","second"};
      Table tb = new Table("test",h);
      String pattern[] = {"#.0000", "#.00","#.000"};
      tb.setFormat(pattern,10);
      tb.setEntry(0,3.173);
      tb.setEntry(1,999.9173);
      tb.setEntry(2,3891.73);
      tb.displayEntries();
      tb.setEntries(3317.3,99.173,817.3);
      tb.displayEntries();
      String lString[] = {"zero", "one", "two","three", "four", "five","six", "seven", "eight"};
      String bString[] = {"A", "B", "C","D", "E", "F","G", "H", "I"};
      double initialP[] = { 1,2,3,4,5,6,7,8,9};
      ControlFrame cf = new  ControlFrame(this,lString,initialP,bString);
      ControlMenu cm = new  ControlMenu(this,lString,initialP,bString);
      WorldGraphics wg = new WorldGraphics("Visualization",0,10,0,10, 500, 200,200);
      wg.setMenuBar(cm.sBar);
   }

   public void buttonAction(int k)
   {
      double x[] = {0,1,2,3,4};
      double y[] = {0,1,2,3,4};
      switch(k)
      {
         case 0: Plot p =  new Plot("y vs x","xxxx","yyyy",1,-5,5,0,5);p.plotPoints(x,y);break;
         case 1: Plot p1 = new Plot("y vs x","xxxx","yyyy",2,-5,5,0,5);p1.plotPoints(x,y);break;
         case 2: Plot p2 = new Plot("y vs x","xxxx","yyyy",3,-5,5,0,5);p2.plotPoints(x,y);break;
         case 3: Plot p3 = new Plot("y vs x","xxxx","yyyy",4,-5,5,0,5);p3.plotPoints(x,y);break;
      }
   }

   public void textAction(int k, double x)
   {
      System.out.println(x);
      cf.setText(0,x);
      cf.setText(1,(int) x);
      cf.setText(2,"hello " + String.valueOf(k));
   }

   public void menuAction(int k)
   {
      switch(k)
      {
         case 0: wg.drawRect(5,5,4,4.0); break;
         case 1: wg.drawString("good job", 7.0,2.0);break;
         case 2: wg.drawLine(0,0,10,10);break;
         case 3: wg.drawRect(200,200,100,100);break;
         case 4: wg.drawCircle(5,5,3);break;
         case 5: wg.clearRect(0,0,10,10);break;
         case 6: wg.setColor("yellow");break;
         case 7: wg.setColor("green");break;
         case 8: wg.setColor("random");break;
      }
      cf.texts[1].setText(String.valueOf(k));  // shows which menu selected in a TextField
   }

   public void dialogAction(int k, double x)
   {
      System.out.println(x);
   }

}
Class and interface Listings
package simphys;

public interface Simulation
{  // used with ControlFrame or ControlMenu
   public void buttonAction(int k);
   public void textAction(int k, double x);
   public void menuAction(int k);
   public void dialogAction(int k, double x);
}

package simphys;

   import java.awt.*;
   import java.awt.event.*;

   public class ControlFrame extends Frame
   {
      public TextField texts[];
      Label labels[];
      Button buttons[];
      Simulation S;
      String lString[], bString[];
      double x[];

      public ControlFrame(Simulation S, String lString[], String bString[])
      {
         this.S = S;
         this.lString = lString;
         this.bString = bString;
         int nT = lString.length;
         x = new double[nT];
         for(int i = 0; i < nT;i++)
            x[i] = 0.0;
         setUpFrame();
      }

      public ControlFrame(Simulation S, String lString[], double x[], String bString[])
      {
         this.S = S;
         this.lString = lString;
         this.bString = bString;
         this.x = x;
         setUpFrame();
      }

      public void setUpFrame()
      {
         int ix = 20, iy = 20,bxT = 100,by = 20, ysep = 80, lsep = 20;
         int fSize = 0, bx;
         int nT = lString.length;      // number of textfields
         int nB = bString.length;      // number of buttons
         for (int i = 0; i < nB; i++)
            fSize += 20 + max(bString[i].length()*10,bxT);
         for (int i = 0; i < nT; i++)
            fSize += 20 + max(lString[i].length()*10,bxT);
         if(fSize < 500)
            setSize(fSize, ysep);
         else
         {
            setSize(500, (1 + fSize/500)*ysep);
            fSize = 500;
         }
         setTitle("Controls");
         this.S = S;
         setLayout(null);
         texts = new TextField[nT];    // new array of textfields
         labels = new Label[nT];       // new array of labels
         buttons = new Button[nB];     // new array of buttons

         for(int i = 0; i < nB;i++)
         {
            bx = max(bString[i].length()*10,bxT);   // button size
            if(ix + bx + 20 > fSize)
            {
               ix = 20;
               iy += ysep;
            }
            buttons[i] = new Button(bString[i]);     // new button
            buttons[i].addActionListener(new B(i));  // add listener
            buttons[i].setSize(bx,by);
            buttons[i].setLocation(ix,iy);
            add(buttons[i]);                          // add button
            ix += bx + 20;
         }
         for(int i = 0; i < nT;i++)
         {
            bx = max(lString[i].length()*10,bxT); // label size
            if(ix + bx + 20 > fSize)
               {
                  ix = 20;
                  iy += ysep;
               }
               labels[i]= new Label(lString[i]);  // new label for textfield
               labels[i].setSize(bx,by);
               labels[i].setLocation(ix,iy+lsep);
               add(labels[i]);    // add label
               if( (int) x[i] == x[i])
                  texts[i]= new TextField(String.valueOf( (int) x[i]));  // new textfield
               else
                   texts[i]= new TextField(String.valueOf(x[i]));  // new textfield
               texts[i].addActionListener( new T(i));  // add listener
               texts[i].setSize(bxT,by);
               texts[i].setLocation(ix,iy);
               add(texts[i]);                     // add textfield
               ix +=  bx + 20;
            }
            setVisible(true);
         }

         private int max(int a, int b)
         {
            if ( a > b)
               return a;
            else
               return b;
         }

         public void setText(int k, String s)
         {
            texts[k].setText(s);
         }

         public void setText(int k, int x)
         {
            texts[k].setText(String.valueOf(x));
         }

         public void setText(int k, double x)
         {
            texts[k].setText(String.valueOf(x));
         }

     //  Inner classes

         class B implements ActionListener
         {
            int k;    // kth button

            public B(int k)
            {
               this.k = k;
            }

            public void actionPerformed(ActionEvent e)
            {
               S.buttonAction(k);
            }
         }

         class T implements ActionListener
         {
            int k;   // kth TextField

            public T(int k)
            {
               this.k = k;
            }

            public void actionPerformed(ActionEvent e)
            {
               Double R = new Double(texts[k].getText().trim());
               S.textAction(k,R.doubleValue());
            }
         }

      }
package simphys;
   import java.awt.*;
   import java.awt.event.*;

   public class ControlMenu
   {
      public MenuBar sBar;          // declare menubar
      Menu bMenu,tMenu;             // declare two menus
      MenuItem bItems[];            // declare array of menu items
      MenuItem tItems[];            // declare array of menu items
      Simulation S;
      String lString[], bString[];
      double x[];

      public ControlMenu(Simulation S, String lString[], String bString[])
      {
         this.S = S;
         this.lString = lString;
         this.bString = bString;
         int nT = lString.length;
         x = new double[nT];
         for(int i = 0; i < nT;i++)
            x[i] = 0.0;
         setUpMenu();
      }

      public ControlMenu(Simulation S, String lString[], double x[], String bString[])
      {
         this.S = S;
         this.lString = lString;
         this.bString = bString;
         this.x = x;
         setUpMenu();
      }

      public void setUpMenu()
      {
         sBar = new MenuBar();                    // assign menubar
         bMenu = new Menu("Actions");             // assign menu
         tMenu = new Menu("Parameters");          // assign menu
         sBar.add(bMenu);                   // add menu to menubar
         sBar.add(tMenu);               // add menu to menubar
         int nB = bString.length;
         bItems = new MenuItem[nB];    // assign array of menu items
         int nT = lString.length;
         tItems = new MenuItem[nT];    // assign array of menu items

         for(int i = 0; i < nT;i++)
         {
            tItems[i] = new MenuItem(lString[i]);  // assign menu item to array element
            tItems[i].addActionListener( new T(i));
            tMenu.add(tItems[i]); }        // add menu item to menu
            for(int i = 0; i < nB;i++)
            {
               bItems[i] = new MenuItem(bString[i]);  // assign menu item to array element
               bItems[i].addActionListener( new B(i));
               bMenu.add(bItems[i]); // add menu item to menu
            }                
         }

         class B implements ActionListener
         {
            int k;

            public B(int k)
            {
               this.k = k;
            }

            public void actionPerformed(ActionEvent e)
            {
               S.menuAction(k);
            }
         }

         class T implements ActionListener
         {
            int k;

            public T(int k)
            {
               this.k = k;
            }

            public void actionPerformed(ActionEvent e)
            {
               InputDialog d = new InputDialog(S,k,lString[k],x);
            }
         }
      }

      package simphys;
      import java.awt.*;
      import java.awt.event.*;

      class InputDialog extends Frame // Used with ControlMenu
      {    

         TextField newValueText;
         double x[];

         public InputDialog(Simulation S,int k, String lString, double x[])
         {
            this.x = x;
            setTitle("Dialog Box");
            setSize(250,250);
            setLocation(50,50);
            setLayout(null);
            Label label = new Label("Enter new  " + lString);
            label.setSize(150,20);
            label.setLocation(50,20);
            add(label);
            Button b1 = new Button("Okay");
            b1.addActionListener( new OkayClass(k,S));
            Button b2 = new Button("Cancel");
            b2.addActionListener( new CancelClass());
            b1.setLocation(30,80);
            b2.setLocation(150,80);
            b1.setSize(70,20);
            b2.setSize(70,20);
            add(b1);
            add(b2);
            newValueText = new TextField(String.valueOf(x[k]));
            newValueText.setSize(80,20);
            newValueText.setLocation(50,50);
            add(newValueText);
            setVisible(true);
         }

         class OkayClass implements ActionListener
         {

            int k;
            Simulation S;

            public OkayClass(int k, Simulation S)
            {
               this.k = k;
               this.S = S;
            }

            public void actionPerformed(ActionEvent e)
            {
               String s = newValueText.getText();
               Double R = new Double(s.trim());
               S.dialogAction(k,R.doubleValue());
               x[k] = R.doubleValue();
               setVisible(false);
               dispose();
            }
         }

         class CancelClass implements ActionListener
         {

            public void actionPerformed(ActionEvent e)
            {
               setVisible(false);
               dispose();
            }
         }
      }
package simphys;
   import java.awt.*;
   import java.text.*;

   public class Table extends Frame
   {

      protected double entry[];
      int n;
      public TextArea t;
      DecimalFormat number[];
      int FieldSize = 10;

      public Table(String TableName, String h[],int FieldSize)
      {  // set up Table with default formats
         this.FieldSize = FieldSize;
         n = h.length;
         entry = new double[n];
         number = new DecimalFormat[n];
         setTitle(TableName);
         setSize(90*n,500);
         t = new TextArea();
         add(t);
         t.setEditable(true);
         setVisible(true);
         setHeadings(h);
         String pattern[] = new String[n];
         for(int i = 0; i < n ; i++)
            pattern[i] = "#.000";
         setFormat(pattern,FieldSize);
      }

      public Table(String TableName, String h[])
      {  // set up Table with default formats
         this(TableName,h,10);
      }

      public void setHeadings(String h[])
      {
         int len;
         setFont(new Font("Monospaced",Font.BOLD,10));
         for(int i = 0; i < n ; i++)
         {
            len = h[i].length();
            if(len < FieldSize)
               for(int j = 0; j < FieldSize-len;j++) h[i] = " " + h[i];
            t.append(h[i] + "\t");
         }
           t.append("\n" + "\n");
      }

      public void println(String S)
      {
         t.append(S + "\n");
      }

      public void setFormat(String pattern[],int FieldSize)
      {
         this.FieldSize = FieldSize;
         for(int i = 0; i < n ; i++)
            number[i] = new DecimalFormat(pattern[i]);
      }

      public void displayEntries()
      {
         int len,Eloc;
         String s;
         setFont(new Font("Monospaced",Font.PLAIN,10));
         for(int i = 0; i < n ; i++)
         {
            if(Math.abs(entry[i]) < 1.0E7)
            {
               s = number[i].format(entry[i]);
               len = s.length();
            }
            else
            {
               s = String.valueOf(entry[i]);
               len = s.length();
               Eloc = len;
               for(int j = 0; j < len;j++)
                  if ( s.substring(j,j+1).equalsIgnoreCase("E"))Eloc = j;
               s = s.substring(0,4) + s.substring(Eloc,len);
               len = s.length();
            }
            if(len < FieldSize)
               for(int j = 0; j < FieldSize-len;j++) s = " " + s;
            t.append(s + "\t");
         }
         t.append("\n");
      }

      public void setEntry(int i, double x)
      {
         entry[i] = x;
      }

      public void setEntries(double x0, double x1)
      {
         entry[0] = x0;
         entry[1] = x1;
      }

      public void setEntries(double x0, double x1, double x2)
      {
         setEntries(x0,x1);
         entry[2] = x2;
      }

      public void setEntries(double x0, double x1, double x2, double x3)
      {
         setEntries(x0,x1,x2);
         entry[3] = x3;
      }

      public void setEntries(double x0, double x1, double x2, double x3, double x4)
      {
         setEntries(x0,x1,x2,x3);
         entry[4] = x4;
      }
   }
package simphys;
   import java.awt.*;

   public class Plot extends Frame
   {
      double scalex,scaley,xmin,ymin,xmax,ymax;
      int nAxes;
      String xLabel,yLabel;
      int mg = 30;
      int xSize,ySize;
      int symbolSize;
      String symbolType;
      Image buffer;
      Graphics b;

      public Plot(String title, String xLabel, String yLabel, int nAxes, double xmin,double xmax, double ymin, double ymax, int symbolSize, String symbolType)
      {
         this.nAxes = nAxes;
         this.xLabel = xLabel;
         this.yLabel = yLabel;
         this.xmin = xmin;
         this.ymin = ymin;
         this.xmax = xmax;
         this.ymax = ymax;
         this.symbolSize = symbolSize;
         setTitle(title);
         setSize(400,400);
         xSize = size().width;
         ySize = size().height;
         setLocation(150,150);
         setVisible(true);
         setScale();
         buffer = createImage(getSize().width,getSize().height);
         b = buffer.getGraphics();
         setAxes();
      }

      public Plot(String title, String xLabel, String yLabel, int nAxes, double xmin,double xmax, double ymin, double ymax)
      {
         this(title,xLabel,yLabel,nAxes,xmin,xmax,ymin,ymax,1,"dot");
      }
      public Plot(int nAxes, double xmin,double xmax, double ymin, double ymax)
      {
         this("Plot","x","y",nAxes,xmin,xmax,ymin,ymax,1,"dot");
       }

      public void paint(Graphics g)
      {
         g.drawImage(buffer,0,0,this);   // draw image onto screen
      }

      private void setScale()
      {
         if(xmax > xmin)
            scalex = (size().width-2*mg)/(xmax-xmin);
         else
         {
            System.out.println("Error  xmax not greater than xmin");
            return;
         }
         if(ymax > ymin)
            scaley = (size().height-2*mg)/(ymax-ymin);
         else
         {
            System.out.println("Error  ymax not greater than ymin");
            return;
         }
      }

      public void setAxes()
      {
         int mg = 30;
         int ix,iy;
         if(xSize != size().width || ySize != size().height)
         {
            xSize = size().width;
            ySize = size().height;
            setScale();
         }
         b.clearRect(0,0,getSize().width,getSize().height);
         switch (nAxes)
         {   // draw axes and axis labels
            case 1: b.drawLine(mg,size().height-mg,size().width-mg,size().height-mg);
                    b.drawString(xLabel,size().width/2,size().height-10);
                    break;
            case 2: b.drawLine(mg,size().height-mg,mg,mg);
                    b.drawLine(mg,size().height-mg,size().width-mg,size().height-mg);
                    b.drawString(xLabel,size().width-2*mg,size().height-10);
                    b.drawString(yLabel,10,20);
                    break;
            case 3: b.drawLine(mg,size().height-mg,size().width-mg,size().height-mg);
                    b.drawLine(size().width/2,size().height-mg,size().width/2,mg);
                    b.drawString(xLabel,size().width-2*mg,size().height-10);
                    b.drawString(yLabel,size().width/2,20);
                    break;
            case 4: b.drawLine(mg,size().height/2,size().width-mg,size().height/2);
                    b.drawLine(size().width/2,size().height-mg,size().width/2,mg);
                    b.drawString(xLabel,size().width-2*mg,size().height/2+20);
                    b.drawString(yLabel,size().width/2,20);
                    break;
            case 5: b.drawLine(mg,size().height/2,size().width-mg,size().height/2);
                    b.drawLine(mg,mg,mg,size().height-mg);
                    b.drawString(xLabel,size().width-2*mg,size().height/2+20);
                    b.drawString(yLabel,mg,20);
                    break;
            }
            repaint();
         }

         public void clearPoints()
         {
            repaint();
         }

         public void plotPoints(double x[],double y[])
         {
            int ix,iy;
            for(int i = 0; i < x.length; i++)
            {  // plot points
               ix = (int)((x[i]-xmin)*scalex) +mg;
               iy = size().height - (int)((y[i]-ymin)*scaley) - mg;
               if(symbolSize > 1)
               {
                  if(symbolType == "fillsquare")
                     b.fillRect(ix-symbolSize/2,iy-symbolSize/2,symbolSize,symbolSize);
                  else if(symbolType == "opensquare")
                     b.drawRect(ix-symbolSize/2,iy-symbolSize/2,symbolSize,symbolSize);
                  else if(symbolType == "opencircle")
                     b.drawOval(ix-symbolSize/2,iy-symbolSize/2,symbolSize,symbolSize);
                  else
                     b.fillOval(ix-symbolSize/2,iy-symbolSize/2,symbolSize,symbolSize);
               }
               else
                  b.drawLine(ix,iy,ix,iy);
               }
               repaint();
            }
         }
package simphys;

   /* Create Frame for drawing on using world coordinates. y increases as you move up,
       x increases as you move right. Unless otherwise specified methods use world
       coordinates. */

   import java.awt.*;

   public class WorldGraphics extends Frame
   {
      double scalex,scaley,xmin,ymin,xmax,ymax;
      Image buffer;
      Graphics b;
      Color c = Color.black;
      boolean toScreen = true; // draw to screen as well as buffer

      public WorldGraphics(String title, double xmin,double xmax, double ymin, double ymax, int size, int xLoc, int yLoc)
      {
         this.xmin = xmin;
         this.ymin = ymin;
         this.xmax = xmax;
         this.ymax = ymax;
         double f = (ymax-ymin)/(xmax-xmin);
         setTitle(title);
         setSize(size,(int)(size*f));
         setScale();
         setLocation(xLoc,yLoc);
         setVisible(true);
         buffer = createImage(getSize().width,getSize().height);
         b = buffer.getGraphics();
      }

      public WorldGraphics(String title, double xmin,double xmax, double ymin, double ymax)
      {
         // old version
         this.xmin = xmin;
         this.ymin = ymin;
         this.xmax = xmax;
         this.ymax = ymax;
         double f = (ymax-ymin)/(xmax-xmin);
         setTitle(title);
         setSize(360,(int)(360*f));
         setScale();
         setLocation(200,100);
         setVisible(true);
         buffer = createImage(getSize().width,getSize().height);
         b = buffer.getGraphics();
      }

      public void setScale()
      {
         if(xmax > xmin)
         {
            scalex = (getSize().width)/(xmax-xmin);
         }
         else
         {
            System.out.println("Error xmax not greater than xmin");
            return;
         }
         if(ymax > ymin)
         {
            scaley = (getSize().height)/(ymax-ymin);
         }
         else
         {
            System.out.println("Error  ymax not greater than ymin");
            return;
         }
      }

      private int scrX(double x)
      {
         return (int)((x-xmin)*scalex);
      }

      private int scrY(double y)
      {
         return getSize().height - (int)((y-ymin)*scaley);
      }

      private int scrDX(double dx) {
         return (int)(dx*scalex);
      }

      private int scrDY(double dy)
      {
         return (int)(dy*scaley);
      }

       public void setToScreen(boolean toScreen)
      {
         this.toScreen = toScreen;
      }

      public void drawLine(double x1, double y1, double x2, double y2)
      {
         b.drawLine(scrX(x1),scrY(y1),scrX(x2),scrY(y2));
         if (toScreen) repaint();
      }

      public void fillRect(double x, double y, double dx, double dy)
      {
         b.fillRect(scrX(x),scrY(y)-scrDY(dy),scrDX(dx),scrDY(dy));
         if (toScreen) repaint();
      }

      public void drawRect(double x, double y, double dx, double dy)
      {
         b.drawRect(scrX(x),scrY(y)-scrDY(dy),scrDX(dx),scrDY(dy));
         if (toScreen) repaint();
      }

      public void drawRect(int x, int y, int dx, int dy)
      {  // use screen coordinates
         b.drawRect(x,y,dx,dy);
         if (toScreen) repaint();
      }

      public void fillRect(int x, int y, int dx, int dy)
      {  // use screen coordinates
         b.fillRect(x,y,dx,dy);
         if (toScreen) repaint();
      }

      public void fillOval(double x, double y, double dx, double dy)
      {
         b.fillOval(scrX(x),scrY(y)-scrDY(dy),scrDX(dx),scrDY(dy));
         if (toScreen) repaint();
      }

      public void drawOval(double x, double y, double dx, double dy)
      {
         b.drawOval(scrX(x),scrY(y)-scrDY(dy),scrDX(dx),scrDY(dy));
         if (toScreen) repaint();
      }

      public void drawCircle(double x, double y, double r)
      {
         b.drawOval(scrX(x)-scrDX(r),scrY(y)-scrDY(r),scrDX(2*r),scrDY(2*r));
         if (toScreen) repaint();
      }

      public void fillCircle(double x, double y, double r)
      {
         b.fillOval(scrX(x)-scrDX(r),scrY(y)-scrDY(r),scrDX(2*r),scrDY(2*r));
         if (toScreen) repaint();
      }

      public void clearRect(double x, double y, double dx, double dy)
      {
         b.clearRect(scrX(x),scrY(y)-scrDY(dy),scrDX(dx),scrDY(dy));
         if (toScreen) repaint();
      }

      public void clearBuffer()
      {
         b.clearRect(scrX(xmin),scrY(ymin)-scrDY(ymax-ymin),scrDX(xmax-xmin),scrDY(ymax-ymin));
      }

      public void clearLine(double x1, double y1, double x2, double y2)
      {  // erase line
         Color cSave = c;
         b.setColor(getBackground());
         drawLine(x1,y1,x2,y2);
         b.setColor(cSave);
      }

      public void drawString(String s, double x, double y)
      {
         b.drawString(s, scrX(x),scrY(y));
         if (toScreen) repaint();
      }

      public void drawString(String s, int x, int y)
      {  // use screen coordinates
         b.drawString(s,x,y);
         if (toScreen) repaint();
      }

      public void setColor(String s)
      {
         if(s == "red") c = Color.red;
         else if(s == "blue") c = Color.blue;
         else if(s == "black") c = Color.black;
         else if(s == "green") c = Color.green;
         else if(s == "yellow") c = Color.yellow;
         else if(s == "white") c = Color.white;
         else if(s == "magenta") c = Color.magenta;
         else if(s == "cyan") c = Color.cyan;
         else if(s == "orange") c = Color.orange;
         else if(s == "pink") c = Color.pink;
         else if(s == "random") c = new Color((int) (Math.random()*255),
            (int) (Math.random()*255), (int) (Math.random()*255));
         b.setColor(c);
      }

      public void drawBuffer()
      {
         Graphics g = getGraphics();
         g.drawImage(buffer,0,0,this);   // draw image onto screen, useful for animation
      }

      public void paint(Graphics g)
      {
         g.drawImage(buffer,0,0,this);   // draw image onto screen
      }
   }

Updated 3 June 2000.