home *** CD-ROM | disk | FTP | other *** search
/ Java Programmer's Toolkit / Java Programmer's Toolkit.iso / applets / plot2d / graph2d.jav < prev    next >
Encoding:
Text File  |  1995-12-15  |  17.9 KB  |  603 lines

  1. import java.awt.*;
  2. import java.applet.*;
  3. import java.util.*;
  4. import java.lang.*;
  5. import java.io.StreamTokenizer;
  6. import java.io.InputStream;
  7. import java.io.IOException;
  8. import java.net.URL;
  9.  
  10. /*************************************************************************
  11. **
  12. **    Class  Graph2D
  13. **                                              Version 1.0   October 1995
  14. **
  15. **************************************************************************
  16. **    Copyright (C) 1995 Leigh Brookshaw
  17. **
  18. **    This program is free software; you can redistribute it and/or modify
  19. **    it under the terms of the GNU General Public License as published by
  20. **    the Free Software Foundation; either version 2 of the License, or
  21. **    (at your option) any later version.
  22. **
  23. **    This program is distributed in the hope that it will be useful,
  24. **    but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. **    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  26. **    GNU General Public License for more details.
  27. **
  28. **    You should have received a copy of the GNU General Public License
  29. **    along with this program; if not, write to the Free Software
  30. **    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  31. **************************************************************************
  32. **
  33. **    This class is the main interface for plotting 2D data sets.
  34. **
  35. *************************************************************************/
  36.  
  37.  
  38.  
  39. /*
  40. ** class Graph2D extends Canvas 
  41. **
  42. ** The main entry point and interface for the 2D graphing package.
  43. ** This class keeps track of the DataSets and the Axes.
  44. ** It has the main drawing engine that positions axis etc.
  45. */
  46.  
  47. public class Graph2D extends Canvas {
  48.  
  49. /*********************
  50. **
  51. ** Protected Variables
  52. **
  53. *********************/
  54.  
  55. /*
  56. **  The axis we are controlling
  57. */
  58.     protected Vector axis          = new Vector(4);
  59. /*
  60. **  The datasets we are controlling
  61. */
  62.     protected Vector dataset       = new Vector(10);
  63. /*
  64. **  The markers that have been loaded
  65. */
  66.     protected Markers marker;
  67.  
  68. /*********************
  69. **
  70. ** Public Variables
  71. **
  72. *********************/
  73.  
  74. /*
  75. **  The border around the entire plot. Allows slopover from axis labels
  76. **  legends etc.
  77. */
  78.     public int     border       = 40;
  79. /*
  80. **  Do you want a frame around the data window. Axis will overlay this frame
  81. **  Not forgetting the frame color
  82. */
  83.     public boolean frame        = true;
  84.     public Color   framecolor;
  85. /*
  86. **  Do you want a grid over the data window.
  87. */
  88.     public boolean grid         = true; 
  89.     public Color   gridcolor    = Color.pink;
  90. /*
  91. **  The rectangle where the data will be plotted
  92. */
  93.     public Rectangle datarect   = new Rectangle();
  94. /*
  95. **  Blank the screen when the update method is called? Normally
  96. **  only modified for special effects
  97. */
  98.     public boolean clearAll     = true;
  99. /*
  100. **  Paint it All ie axis, data etc. Normally
  101. **  only modified for special effects
  102. */
  103.     public boolean paintAll     = true;
  104.  
  105. /******************
  106. **
  107. **  Public Methods
  108. **
  109. *******************/
  110.  
  111.  
  112. /*
  113. **  loadFile( URL file)  
  114. **
  115. **  Load a DataSet from a File. The dataset is assumed to consist
  116. **  (at this stage) 2 ASCII columns of numbers x, y. As always blank lines
  117. **  are ignored and a # at the beginning of a line signifies a comment.
  118. **
  119. **  NOTE: the streamtokenizer was used in the first version of this method
  120. **        it was more trouble than it was worth for numbers and scientific
  121. **        notation.
  122. **
  123. **  URL file:    The URL of the file.
  124. */
  125.     public DataSet loadFile( URL file) {
  126.            byte b[] = new byte[50];
  127.            int nbytes = 0;
  128.            int max  = 100;
  129.            int inc  = 100;
  130.            int n    = 0;
  131.            double data[] = new double[max];
  132.            InputStream is = null;
  133.            boolean comment    = false;
  134.            int c;
  135.  
  136.            try {
  137.                  is = file.openStream();
  138.  
  139.                  while( (c=is.read()) > -1 ) {
  140.                      
  141.                      switch (c) {
  142.                      
  143.                          case '#':
  144.                                     comment = true;
  145.                                     break;
  146.                          case '\r': case '\n':
  147.                                     comment = false;
  148.                          case ' ': case '\t':
  149.                                 if( nbytes > 0 ) {
  150.                                    String s = new String(b,0,0,nbytes);
  151.                                    data[n] = Double.valueOf(s).doubleValue();
  152.                                    n++;
  153.                                    if( n >= max ) {
  154.                                        max += inc;
  155.                                        double d[] = new double[max];
  156.                                        System.arraycopy(data, 0, d, 0, n);
  157.                                        data = d;
  158.                                    }
  159.  
  160.                                    nbytes = 0;
  161.                                 }
  162.                                 break;
  163.                          default:
  164.                                    if( !comment ) {
  165.                                         b[nbytes] = (byte)c;
  166.                                         nbytes++;
  167.                                    }
  168.                                    break;
  169.                       }
  170.  
  171.                   }
  172.  
  173.                 if (is != null) is.close();
  174.         } catch(Exception e) {
  175.           System.out.println("Failed to load Data set from file ");
  176.           e.printStackTrace();
  177.           if (is != null) try { is.close(); } catch (Exception ev) { }
  178.           return null;
  179.         }
  180.  
  181.         return loadDataSet(data,n/2);
  182.         
  183.     }
  184. /*
  185. **  loadDataSet( double data[], int n )  
  186. **
  187. **  Load a DataSet from an Array. The data is assumed to be stored
  188. **  in the form  x,y,x,y,x,y.... A local copy of the data is made.
  189. **
  190. **  If everything goes well it returns 'true'
  191. **
  192. **  double data[]: The data to plot stored in the form x,y,x,y...
  193. **  int   n:      The number of data points. This means that the
  194. **                minimum length of the data array is 2*n.
  195. */
  196.  
  197.     public DataSet loadDataSet( double data[], int n ) {
  198.        DataSet d;
  199.        try { 
  200.              d =  new DataSet(data, n);
  201.              dataset.addElement( d );
  202.              d.g2d = this;
  203.             }
  204.        catch (Exception e) { 
  205.          System.out.println("Failed to load Data set ");
  206.          e.printStackTrace();
  207.          return null;
  208.        }
  209.        return d;
  210.     }
  211.  
  212. /*
  213. ** deleteDataSet( DataSet d ) 
  214. **        Delete all pointers to the local copy for the data set.
  215. */
  216.  
  217.     public void deleteDataSet( DataSet d ) {
  218.        if(d != null) {
  219.                       if(d.xaxis != null) d.xaxis.detachDataSet(d);
  220.                       if(d.yaxis != null) d.yaxis.detachDataSet(d);
  221.                       dataset.removeElement(d);
  222.        }
  223.     }
  224. /*
  225. ** createAxis( int position )
  226. **            create an axis
  227. */
  228.     public Axis createAxis( int position ) {
  229.        Axis a;
  230.  
  231.        try { 
  232.              a =  new Axis(position);
  233.              axis.addElement( a );
  234.              a.g2d = this;
  235.             }
  236.        catch (Exception e) { 
  237.          System.out.println("Failed to create Axis");
  238.          e.printStackTrace();
  239.          return null;
  240.        }
  241.  
  242.        return a;
  243.  
  244.     }
  245. /*
  246. ** deleteAxis( Axis a )
  247. **            delete an axis
  248. */
  249.     public void deleteAxis( Axis a ) {
  250.        
  251.        if(a != null) {
  252.                    a.detachAllDataSets();
  253.                    axis.removeElement(a);
  254.        }
  255.     }
  256. /*
  257. ** Vector getMarkerVector(int m)
  258. **         Given the marker index (m > 0 ) return the vector containing the
  259. **         points to stroke the marker
  260. */
  261.     public Vector getMarkerVector(int m) {
  262.  
  263.         if( marker == null) return null;
  264.  
  265.  
  266.         if( marker.number > 0 && m <  marker.number ) {
  267.  
  268.               return marker.vert[m-1];
  269.         }
  270.  
  271.         return null;
  272.     }
  273. /*
  274. ** boolean loadMarkerFile(URL name)
  275. **        Load the marker file given the files URL. Markers are NOT
  276. ** predefined but must be loaded from a Marker definition file
  277. */
  278.     public boolean loadMarkerFile(URL name) {
  279.  
  280.         marker = new Markers(name);
  281.  
  282.         if(marker.number <= 0) return false;
  283.  
  284.         return true;
  285.  
  286.     }
  287. /*
  288. ** paint(Graphics g)
  289. **                  Paint it ALL.
  290. */
  291.     public void paint(Graphics g) {
  292.         Graphics lg  = g.create();
  293.         Rectangle r = bounds();
  294.         DataSet d;
  295.         Enumeration e;
  296.  
  297.         r.x      += border;
  298.         r.y      += border;
  299.         r.width  -= 2*border;
  300.         r.height -= 2*border;
  301.  
  302.         if( dataset.isEmpty() ) return;
  303.  
  304.         if( !axis.isEmpty() ) r = drawAxis(lg, r);
  305.         else                  drawFrame(lg,r.x,r.y,r.width,r.height);
  306.  
  307.         for (e = dataset.elements() ; e.hasMoreElements() ;) {
  308.           d = (DataSet)e.nextElement();
  309.           if( d.legend_text != null) d.draw_legend(lg);
  310.         }
  311.  
  312.         lg.clipRect(r.x, r.y, r.width, r.height);
  313.  
  314.         datarect.x      = r.x;
  315.         datarect.y      = r.y;
  316.         datarect.width  = r.width;
  317.         datarect.height = r.height;
  318.  
  319.         for (e = dataset.elements() ; e.hasMoreElements() ;) {
  320.           ((DataSet)e.nextElement()).draw_data(lg);
  321.         }
  322.  
  323.  
  324.         lg.dispose();
  325.  
  326.     }
  327. /*
  328. ** update(Graphics g)
  329. **                   This method is called through the repaint() method.
  330. ** All it does is blank the canvas before calling paint.
  331. */
  332.     public void update(Graphics g) {
  333.           Rectangle r = bounds();
  334.           Color c = g.getColor();
  335.  
  336.           if( clearAll ) {
  337.              g.setColor(getBackground());
  338.              g.fillRect(r.x,r.y,r.width,r.height);
  339.              g.setColor(c);
  340.           }
  341.           if( paintAll ) paint(g);
  342.     }
  343. /******************
  344. **
  345. ** Protected Methods
  346. **
  347. *******************/
  348. /*
  349. **  Rectangle drawAxis(Graphics g, Rectangle r)
  350. **
  351. **  Draw the Axis. As each axis is drawn and aligned less of the canvas
  352. **  is avaliable to plot the data. The returned Rectangle is the canvas
  353. **  area that the data is plotted in.
  354. */
  355.         protected Rectangle drawAxis(Graphics g, Rectangle r) {
  356.             Axis a;
  357.             Enumeration e;
  358.             int waxis;
  359.             int x      = r.x;
  360.             int y      = r.y;
  361.             int width  = r.width;
  362.             int height = r.height;
  363.  
  364. // Calculate the available area for the data
  365.             for ( e = axis.elements() ; e.hasMoreElements() ;) {
  366.                a = ((Axis)e.nextElement());
  367.                waxis = a.getAxisWidth(g);
  368.  
  369.                switch (a.getAxisPos()) {
  370.                case Axis.LEFT:
  371.                           x += waxis;
  372.                           width -= waxis;
  373.                           break;
  374.                case Axis.RIGHT:
  375.                           width -= waxis;
  376.                           break;
  377.                case Axis.TOP:
  378.                           y += waxis;
  379.                           height -= waxis;
  380.                           break;
  381.                case Axis.BOTTOM:
  382.                           height -= waxis;
  383.                           break;
  384.                }
  385.             }
  386. // Draw a frame around the data area (If requested)
  387.             drawFrame(g,x,y,width,height);
  388.  
  389. // Now draw the axis in the order specified aligning them with the final
  390. // data area.
  391.             for ( e = axis.elements() ; e.hasMoreElements() ;) {
  392.                a = ((Axis)e.nextElement());
  393.  
  394.                switch (a.getAxisPos()) {
  395.                case Axis.LEFT:
  396.                           r.x += a.width;
  397.                           r.width -= a.width;
  398.                           a.positionAxis(r.x,r.x,y,y+height);
  399.                           if(r.x == x && grid) {
  400.                              a.gridcolor = gridcolor;
  401.                              a.gridlength = width;
  402.                              a.drawAxis(g);
  403.                              a.gridlength = 0;
  404.                           } else {
  405.                              a.drawAxis(g);
  406.                           }
  407.                           break;
  408.                case Axis.RIGHT:
  409.                           r.width -= a.width;
  410.                           a.positionAxis(r.x+r.width,r.x+r.width,y,y+height);
  411.                           if(r.x+r.width == x+width && grid) {
  412.                              a.gridcolor = gridcolor;
  413.                              a.gridlength = width;
  414.                              a.drawAxis(g);
  415.                              a.gridlength = 0;
  416.                           } else {
  417.                              a.drawAxis(g);
  418.                           }
  419.                            break;
  420.                case Axis.TOP:
  421.                           r.y += a.width;
  422.                           r.height -= a.width;
  423.                           a.positionAxis(x,x+width,r.y,r.y);
  424.                           if(r.y == y && grid) {
  425.                              a.gridcolor = gridcolor;
  426.                              a.gridlength = height;
  427.                              a.drawAxis(g);
  428.                              a.gridlength = 0;
  429.                           } else {
  430.                              a.drawAxis(g);
  431.                           }
  432.                           break;
  433.                case Axis.BOTTOM:
  434.                           r.height -= a.width;
  435.                           a.positionAxis(x,x+width,r.y+r.height,r.y+r.height);
  436.                           if(r.y +r.height == y+height && grid) {
  437.                              a.gridcolor = gridcolor;
  438.                              a.gridlength = height;
  439.                              a.drawAxis(g);
  440.                              a.gridlength = 0;
  441.                           } else {
  442.                              a.drawAxis(g);
  443.                           }
  444.  
  445.                           break;
  446.                }
  447.             }
  448.  
  449.            return r;
  450.       }
  451. /*
  452. ** drawFrame(Graphics g, int x, int y, int width, int height)
  453. **    If requested draw a frame around the data area.
  454. */
  455.       protected void drawFrame(Graphics g, int x, int y, int width, int height) {
  456.         Color c = g.getColor();
  457.  
  458.         if(!frame) return;
  459.  
  460.         if( framecolor != null ) g.setColor(framecolor);
  461.  
  462.         g.drawRect(x,y,width,height);
  463.  
  464.         g.setColor(c);
  465.  
  466.  
  467.      }
  468.  
  469.  
  470.  
  471. }
  472.  
  473. /*
  474. ** class FileFormatException
  475. ** This should be thrown if the fileloader encounters a format error
  476. ** not being thrown at the moment. Is being thown by the getMarker method.
  477. */
  478. class FileFormatException extends Exception {
  479.     public FileFormatException(String s) {
  480.         super(s);
  481.     }
  482. }
  483.  
  484. /*
  485. ** class MVertex
  486. **       This is a structure to contian the Marker points with the 
  487. ** drawing command
  488. */
  489.  
  490. class MVertex extends Object {
  491.       boolean draw;
  492.       int     x;
  493.       int     y;
  494. }
  495. /*
  496. ** class Markers
  497. **              This class is used to load and store the marker definitions.
  498. ** The marker definitions are not compiled in but are loaded from a file.
  499. */
  500.  
  501. class Markers extends Object {
  502.  
  503. /*
  504. **    Number of markers loaded
  505. */
  506.       public int number = 0;
  507. /*
  508. **    An array of vectors. Each element in the array contains the vertex
  509. **    vectors for a marker. Marker 1 is at element vert[0].
  510. */
  511.       public Vector vert[] = new Vector[10];
  512.  
  513. /*
  514. **    Markers(URL file)
  515. **         The marker contructor. Loads the markers from the givern URL
  516. */
  517.       public Markers(URL file) {
  518.  
  519.           try {
  520.                 getMarkers(file);
  521.           } catch ( Exception e ) {
  522.             System.out.println("Error loading marker file!");
  523.           }
  524.  
  525.       }
  526. /*
  527. **   getMarkers(URL file)
  528. **        This method read the marker file and loads the marker definitions
  529. **   into the vert array. The format of the file is simple
  530. **   The keword "start" starts a new marker definition.
  531. **   The keword "end"   ends a marker definition.
  532. **   m x y means move to position x,y
  533. **   l x y means line to x,y from previous position.
  534. */
  535.       private void getMarkers(URL file)
  536.                     throws IOException, FileFormatException {
  537.          InputStream is;
  538.          StreamTokenizer st;
  539.          MVertex v;
  540.  
  541.          try {
  542.             is = file.openStream();
  543.          } catch (Exception e) {
  544.             return;
  545.          }
  546.  
  547.          st = new StreamTokenizer(is);
  548.          st.eolIsSignificant(true);
  549.          st.commentChar('#');
  550.  
  551. scan:
  552.         while (true) {
  553.             switch (st.nextToken()) {
  554.               default:
  555.                 break scan;
  556.               case StreamTokenizer.TT_EOL:
  557.                 break;
  558.               case StreamTokenizer.TT_WORD:
  559.  
  560.                    if ("start".equals(st.sval)) {
  561.                         vert[number] = new Vector();
  562.                    } else
  563.                    if ("end".equals(st.sval)) {
  564.                         number++;
  565.                    } else
  566.                    if ("m".equals(st.sval)) {
  567.                         v = new MVertex();
  568.                         v.draw = false;
  569.                         if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
  570.                            v.x = (int)st.nval;
  571.                            if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
  572.                                v.y = (int)st.nval;
  573.                                vert[number].addElement(v);
  574.                            }
  575.                         }
  576.                    } else
  577.                    if ("l".equals(st.sval)) {
  578.                         v = new MVertex();
  579.                         v.draw = true;
  580.                         if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
  581.                            v.x = (int)st.nval;
  582.                            if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
  583.                                v.y = (int)st.nval;
  584.                                vert[number].addElement(v);
  585.                            }
  586.                         }
  587.                     }
  588.                  break;
  589.              }
  590.  
  591.  
  592.         }
  593.         try {
  594.             if (is != null)
  595.                 is.close();
  596.         } catch(Exception e) {
  597.         }
  598. }
  599.  
  600. }
  601.  
  602.  
  603.