root/trunk/matml/src/sphlow/PlotBox.java

Revision 148, 82.9 kB (checked in by powell, 7 years ago)

New Sphlow Java applet.

  • Property svn:keywords set to Author Date Id Revision
Line 
1/* A labeled box for signal plots.
2
3@Author: Edward A. Lee and Christopher Hylands
4
5@Contributors:  William Wu
6
7@Version: @(#)PlotBox.java      1.81    01/22/98
8
9@Copyright (c) 1997-1998 The Regents of the University of California.
10All rights reserved.
11
12Permission is hereby granted, without written agreement and without
13license or royalty fees, to use, copy, modify, and distribute this
14software and its documentation for any purpose, provided that the
15above copyright notice and the following two paragraphs appear in all
16copies of this software.
17
18IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
19FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
20ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
21THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
22SUCH DAMAGE.
23
24THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
25INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
27PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
28CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
29ENHANCEMENTS, OR MODIFICATIONS.
30
31                                                PT_COPYRIGHT_VERSION_2
32                                                COPYRIGHTENDKEY
33*/
34//package ptplot;
35
36import java.awt.*;
37import java.io.*;
38import java.net.*;
39import java.util.*;
40import java.lang.*;
41
42//////////////////////////////////////////////////////////////////////////
43//// PlotBox
44/**
45 * Construct a labeled box within which to place a data plot.  A title,
46 * X and Y axis labels, tick marks, and a legend are all supported.
47 * Zooming in and out is supported.  To zoom in, drag the mouse
48 * downwards to draw a box.  To zoom out, drag the mouse upward.
49 * Zooming out stops automatically at the point where the data fills
50 * the drawing rectangle.
51 *
52 * The box can be configured either through a file with commands or
53 * through direct invocation of the public methods of the class.
54 * If a file is used, the file can be given as a URL through the
55 * <code>setDataurl</code> method. The file contains any number
56 * commands, one per line.  Unrecognized commands and commands with
57 * syntax errors are ignored.  Comments are denoted by a line starting
58 * with a pound sign "#".  The recognized commands include:
59 * <pre>
60 * TitleText: <i>string</i>
61 * XLabel: <i>string</i>
62 * YLabel: <i>string</i>
63 * </pre>
64 * These commands provide a title and labels for the X (horizontal) and Y
65 * (vertical) axes.
66 * A <i>string</i> is simply a sequence of characters, possibly
67 * including spaces.  There is no need here to surround them with
68 * quotation marks, and in fact, if you do, the quotation marks will
69 * be included in the labels.
70 * <p>
71 * The ranges of the X and Y axes can be optionally given by commands like:
72 * <pre>
73 * XRange: <i>min</i>, <i>max</i>
74 * YRange: <i>min</i>, <i>max</i>
75 * </pre>
76 * The arguments <i>min</i> and <i>max</i> are numbers, possibly
77 * including a sign and a decimal point. If they are not specified,
78 * then the ranges are computed automatically from the data and padded
79 * slightly so that datapoints are not plotted on the axes.
80 * <p>
81 * The tick marks for the axes are usually computed automatically from
82 * the ranges.  Every attempt is made to choose reasonable positions
83 * for the tick marks regardless of the data ranges (powers of
84 * ten multiplied by 1, 2, or 5 are used).  However, they can also be
85 * specified explicitly using commands like:
86 * <pre>
87 * XTicks: <i>label position, label position, ...</i>
88 * YTicks: <i>label position, label position, ...</i>
89 * </pre>
90 * A <i>label</i> is a string that must be surrounded by quotation
91 * marks if it contains any spaces.  A <i>position</i> is a number
92 * giving the location of the tick mark along the axis.  For example,
93 * a horizontal axis for a frequency domain plot might have tick marks
94 * as follows:
95 * <pre>
96 * XTicks: -PI -3.14159, -PI/2 -1.570795, 0 0, PI/2 1.570795, PI 3.14159
97 * </pre>
98 * Tick marks could also denote years, months, days of the week, etc.
99 * <p>
100 * The X and Y axes can use a logarithmic scale with the following commands:
101 * <pre>
102 * XLog: on
103 * YLog: on
104 * </pre>
105 * The grid labels represent powers of 10.  Note that if a logarithmic
106 * scale is used, then the values must be positive.  Non-positive values
107 * will be silently dropped.
108 * <p>
109 * By default, tick marks are connected by a light grey background grid.
110 * This grid can be turned off with the following command:
111 * <pre>
112 * Grid: off
113 * </pre>
114 * It can be turned back on with
115 * <pre>
116 * Grid: on
117 * </pre>
118 * Also, by default, the first ten data sets are shown each in a unique color.
119 * The use of color can be turned off with the command:
120 * <pre>
121 * Color: off
122 * </pre>
123 * It can be turned back on with
124 * <pre>
125 * Color: on
126 * </pre>
127 * All of the above commands can also be invoked directly by calling the
128 * the corresponding public methods from some Java procedure.
129 *
130 * @author Edward A. Lee, Christopher Hylands
131 * @version @(#)PlotBox.java    1.81   01/22/98
132 */
133public class PlotBox extends Panel {
134
135    //////////////////////////////////////////////////////////////////////////
136    ////                         public methods                           ////
137   
138    /**
139     * Handle button presses to fill the plot.  This rescales so that
140     * the data that is currently plotted just fits.
141     * @deprecated As of JDK1.1 in java.awt.component
142     * but we need to compile under 1.0.2 for netscape3.x compatibility.
143     */
144    public boolean action (Event evt, Object arg) {
145        if (evt.target == _fillButton) {
146            fillPlot(_graphics);
147            return true;
148        } else {
149            return super.action (evt, arg); // action() is deprecated in 1.1
150            // but we need to compile under
151            // jdk1.0.2 for netscape3.x
152        }
153    }
154
155    /**
156     * Create the peer of the plot box.
157     */
158    public void addNotify() {
159        // This method is called after our Panel is first created
160        // but before it is actually displayed.
161        super.addNotify();
162        _measureFonts();
163    }
164
165    /**
166     * Add a legend (displayed at the upper right) for the specified
167     * data set with the specified string.  Short strings generally
168     * fit better than long strings.  You must call <code>init()</code>
169     * before calling this method.
170     */
171    public void addLegend(int dataset, String legend) {
172        if (_debug > 8) System.out.println("PlotBox addLegend: " +
173                dataset + " " + legend);
174        _legendStrings.addElement(legend);
175        _legendDatasets.addElement(new Integer(dataset));
176    }
177   
178    /**
179     * Specify a tick mark for the X axis.  The label given is placed
180     * on the axis at the position given by <i>position</i>. If this
181     * is called once or more, automatic generation of tick marks is
182     * disabled.  The tick mark will appear only if it is within the X
183     * range.
184     */
185    public void addXTick (String label, double position) {
186        if (_xticks == null) {
187            _xticks = new Vector();
188            _xticklabels = new Vector();
189        }
190        _xticks.addElement(new Double(position));
191        _xticklabels.addElement(label);
192    }
193   
194    /**
195     * Specify a tick mark for the Y axis.  The label given is placed
196     * on the axis at the position given by <i>position</i>. If this
197     * is called once or more, automatic generation of tick marks is
198     * disabled.  The tick mark will appear only if it is within the Y
199     * range.
200     */
201    public void addYTick (String label, double position) {
202        if (_yticks == null) {
203            _yticks = new Vector();
204            _yticklabels = new Vector();
205        }
206        _yticks.addElement(new Double(position));
207        _yticklabels.addElement(label);
208    }
209
210  // Adam's little pseudo-drawplot.
211    public synchronized void drawPlot(boolean clearfirst) {
212      drawPlot(_graphics, clearfirst); }
213   
214    /**
215     * Draw the axes using the current range, label, and title information.
216     * If the argument is true, clear the display before redrawing.
217     */
218    public synchronized void drawPlot(Graphics graphics, boolean clearfirst) {
219        if (_debug > 7)
220            System.out.println("PlotBox: drawPlot"+graphics+" "+clearfirst);
221        if (graphics == null) {
222            System.out.println("Attempt to draw axes without "+
223                    "a Graphics object.");
224            return;
225        }
226        //        super.paint(graphics);
227        // Give other threads a chance, so that hopefully things are
228        // up to date.
229        //        Thread.yield();
230           
231        // Find the width and height of the total drawing area, and clear it.
232        Rectangle drawRect = bounds(); // FIXME: bounds() is deprecated
233        // in JDK1.1, but we need to compile under 1.0.2 for
234        // netscape3.x compatibility.
235
236        graphics.setPaintMode();
237        if (clearfirst) {
238            // Clear all the way from the top so that we erase the title.
239            // If we don't do this, then zooming in with the pxgraph
240            // application ends up blurring the title.
241            graphics.clearRect(0,0,drawRect.width, drawRect.height);
242        }
243
244        if (_debug > 8) {
245            System.out.println("PlotBox: drawPlot drawRect ="+
246                    drawRect.width+" "+drawRect.height+" "+
247                    drawRect.x+" "+drawRect.y);
248            graphics.drawRect(0,0,drawRect.width, drawRect.height);
249        }
250
251        // If an error message has been set, display it and return.
252        if (_errorMsg != null) {
253            int fheight = _labelFontMetrics.getHeight() + 2;
254            int msgy = fheight;
255            graphics.setColor(Color.black);
256            for(int i=0; i<_errorMsg.length;i++) {
257                graphics.drawString(_errorMsg[i],10, msgy);
258                msgy += fheight;
259            }
260            return;
261        }
262
263        // Make sure we have an x and y range
264        if (!_xRangeGiven) {
265            if (_xBottom > _xTop) {
266                // have nothing to go on.
267                _setXRange(0,0);
268            } else {
269                _setXRange(_xBottom, _xTop);
270            }
271        }
272        if (!_yRangeGiven) {
273            if (_yBottom > _yTop) {
274                // have nothing to go on.
275                _setYRange(0,0);
276            } else {
277                _setYRange(_yBottom, _yTop);
278            }
279        }
280         
281        // Vertical space for title, if appropriate.
282        // NOTE: We assume a one-line title.
283        int titley = 0;
284        int titlefontheight = _titleFontMetrics.getHeight();
285        if (_title != null || _yExp != 0) {
286            titley = titlefontheight + _topPadding;
287        }
288       
289        // Number of vertical tick marks depends on the height of the font
290        // for labeling ticks and the height of the window.
291        graphics.setFont(_labelfont);
292        int labelheight = _labelFontMetrics.getHeight();
293        int halflabelheight = labelheight/2;
294
295        // Draw scaling annotation for x axis.
296        // NOTE: 5 pixel padding on bottom.
297        int ySPos = drawRect.height - 5;
298        int xSPos = drawRect.width - _rightPadding;
299        if (_xlog)
300            _xExp = (int)Math.floor(_xtickMin);
301        if (_xExp != 0 && _xticks == null) {
302            String superscript = Integer.toString(_xExp);
303            xSPos -= _superscriptFontMetrics.stringWidth(superscript);
304            graphics.setFont(_superscriptfont);
305            if (!_xlog) {
306                graphics.drawString(superscript,xSPos,ySPos - halflabelheight);
307                xSPos -= _labelFontMetrics.stringWidth("x10");
308                graphics.setFont(_labelfont);
309                graphics.drawString("x10", xSPos, ySPos);
310            }
311            // NOTE: 5 pixel padding on bottom
312            _bottomPadding = (3 * labelheight)/2 + 5;
313        }
314       
315        // NOTE: 5 pixel padding on the bottom.
316        if (_xlabel != null && _bottomPadding < labelheight + 5) {
317            _bottomPadding = titlefontheight + 5;
318        }
319       
320        // Compute the space needed around the plot, starting with vertical.
321        // NOTE: padding of 5 pixels below title.
322        _uly = titley + 5;
323        // NOTE: 3 pixels above bottom labels.
324        _lry = drawRect.height-labelheight-_bottomPadding-3;
325        int height = _lry-_uly;
326        _yscale = height/(_yMax - _yMin);
327        _ytickscale = height/(_ytickMax - _ytickMin);
328
329        ///////////////////// vertical axis
330
331        // Number of y tick marks.
332        // NOTE: subjective spacing factor.
333        int ny = 2 + height/(labelheight+10);
334        // Compute y increment.
335        double yStep=_roundUp((_ytickMax-_ytickMin)/(double)ny);
336       
337        // Compute y starting point so it is a multiple of yStep.
338        double yStart=yStep*Math.ceil(_ytickMin/yStep);
339       
340        // NOTE: Following disables first tick.  Not a good idea?
341        // if (yStart == _ytickMin) yStart+=yStep;
342       
343        // Define the strings that will label the y axis.
344        // Meanwhile, find the width of the widest label.
345        // The labels are quantized so that they don't have excess resolution.
346        int widesty = 0;
347
348        // These do not get used unless ticks are automatic, but the
349        // compiler is not smart enough to allow us to reference them
350        // in two distinct conditional clauses unless they are
351        // allocated outside the clauses.
352        String ylabels[] = new String[ny];
353        int ylabwidth[] = new int[ny];
354
355        int ind = 0;
356        if (_yticks == null) {
357            Vector ygrid = null;
358            if (_ylog) {
359                ygrid = _gridInit(yStart, yStep, true, null);
360            }
361
362            // automatic ticks
363            // First, figure out how many digits after the decimal point
364            // will be used.
365            int numfracdigits = _numFracDigits(yStep);
366
367            // NOTE: Test cases kept in case they are needed again.
368            // System.out.println("0.1 with 3 digits: " + _formatNum(0.1, 3));
369            // System.out.println("0.0995 with 3 digits: " +
370            //                    _formatNum(0.0995, 3));
371            // System.out.println("0.9995 with 3 digits: " +
372            //                    _formatNum(0.9995, 3));
373            // System.out.println("1.9995 with 0 digits: " +
374            //                    _formatNum(1.9995, 0));
375            // System.out.println("1 with 3 digits: " + _formatNum(1, 3));
376            // System.out.println("10 with 0 digits: " + _formatNum(10, 0));
377            // System.out.println("997 with 3 digits: " + _formatNum(997,3));
378            // System.out.println("0.005 needs: " + _numFracDigits(0.005));
379            // System.out.println("1 needs: " + _numFracDigits(1));
380            // System.out.println("999 needs: " + _numFracDigits(999));
381            // System.out.println("999.0001 needs: "+_numFracDigits(999.0001));
382            // System.out.println("0.005 integer digits: " +
383            //                    _numIntDigits(0.005));
384            // System.out.println("1 integer digits: " + _numIntDigits(1));
385            // System.out.println("999 integer digits: " + _numIntDigits(999));
386            // System.out.println("-999.0001 integer digits: " +
387            //                    _numIntDigits(999.0001));
388
389           
390            double yTmpStart = yStart;
391            if (_ylog)
392                yTmpStart = _gridStep(ygrid,yStart, yStep, _ylog);
393
394            for (double ypos = yTmpStart; ypos <= _ytickMax;
395                 ypos = _gridStep(ygrid, ypos, yStep, _ylog)) {
396                // Prevent out of bounds exceptions
397                if (ind >= ny) break;
398                String yticklabel;
399                if (_ylog) {
400                    yticklabel = _formatLogNum(ypos, numfracdigits);
401                } else {
402                    yticklabel = _formatNum(ypos, numfracdigits);
403                }
404                ylabels[ind] = yticklabel;
405                int lw = _labelFontMetrics.stringWidth(yticklabel);
406                ylabwidth[ind++] = lw;
407                if (lw > widesty) {widesty = lw;}
408            }
409        } else {
410            // explictly specified ticks
411            Enumeration nl = _yticklabels.elements();
412            while (nl.hasMoreElements()) {
413                String label = (String) nl.nextElement();
414                int lw = _labelFontMetrics.stringWidth(label);
415                if (lw > widesty) {widesty = lw;}
416            }           
417        }
418
419        // Next we do the horizontal spacing.
420        if (_ylabel != null) {
421            _ulx = widesty + _labelFontMetrics.stringWidth("W") + _leftPadding;
422        } else {     
423            _ulx = widesty + _leftPadding;
424        }
425        int legendwidth = _drawLegend(graphics,
426                drawRect.width-_rightPadding, _uly);
427        _lrx = drawRect.width-legendwidth-_rightPadding;
428        int width = _lrx-_ulx;
429        _xscale = width/(_xMax - _xMin);
430        _xtickscale = width/(_xtickMax - _xtickMin);
431       
432        if (_debug > 8) {
433            System.out.println("PlotBox: drawPlot _ulx "+_ulx+" "+_uly+" "+
434                    _lrx+" "+_lry+" "+width+" "+height);
435        }
436
437        if (_background == null) {
438                throw new Error("PlotBox.drawPlot(): _background == null\n" +
439                        "Be sure to call init() before calling paint().");
440        }
441
442        // background for the plotting rectangle
443        graphics.setColor(_background);
444        graphics.fillRect(_ulx,_uly,width,height);
445
446        graphics.setColor(_foreground);
447        graphics.drawRect(_ulx,_uly,width,height);
448       
449        // NOTE: subjective tick length.
450        int tickLength = 5;
451        int xCoord1 = _ulx+tickLength;
452        int xCoord2 = _lrx-tickLength;
453       
454        if (_yticks == null) {
455            // auto-ticks
456            Vector ygrid = null;
457            double yTmpStart = yStart;
458            if (_ylog) {
459                ygrid = _gridInit(yStart, yStep, true, null);
460                yTmpStart = _gridStep(ygrid, yStart, yStep, _ylog);
461                if (_debug == 5)
462                    System.out.println("PlotBox: drawPlot: YAXIS ind="+ind+
463                            " ny="+ny+" yStart="+yStart+" yStep="+yStep+
464                            " yTmpStart="+yTmpStart);
465                ny = ind;
466            }
467            // FIXME: What does ind do here?  It is never set in the loop?
468            ind = 0;
469            // Set to false if we don't need the exponent 
470            boolean needExponent = _ylog;
471            for (double ypos=yTmpStart; ypos <= _ytickMax;
472                 ypos = _gridStep(ygrid, ypos, yStep, _ylog)) {
473                // Prevent out of bounds exceptions
474                if (ind >= ny) break;
475                int yCoord1 = _lry - (int)((ypos-_ytickMin)*_ytickscale);
476                // The lowest label is shifted up slightly to avoid
477                // colliding with x labels.
478                int offset = 0;
479                if (ind > 0 &&  ! _ylog) offset = halflabelheight;
480                graphics.drawLine(_ulx,yCoord1,xCoord1,yCoord1);
481                graphics.drawLine(_lrx,yCoord1,xCoord2,yCoord1);
482                if (_grid && yCoord1 != _uly && yCoord1 != _lry) {
483                    graphics.setColor(Color.lightGray);
484                    graphics.drawLine(xCoord1,yCoord1,xCoord2,yCoord1);
485                    graphics.setColor(_foreground);
486                }
487                // Check to see if any of the labels printed contain
488                // the exponent.  If we don't see an exponent, then print it.
489                if (_ylog && ylabels[ind].indexOf('e') != -1 )
490                    needExponent = false;
491
492                if (_debug == 5)
493                    System.out.println("PlotBox: drawPlot: ypos = "+ypos+
494                            " _ytickMax="+_ytickMax+" ny="+ny+
495                            " ylabels["+ind+"] ="+ylabels[ind] );
496                // NOTE: 4 pixel spacing between axis and labels.
497                graphics.drawString(ylabels[ind],
498                        _ulx-ylabwidth[ind++]-4, yCoord1+offset);
499            }
500
501            if (_ylog) {
502                // Draw in grid lines that don't have labels.
503                Vector unlabeledgrid  = _gridInit(yStart,yStep,false,ygrid);
504                if (unlabeledgrid.size() > 0) {
505                    // If the step is greater than 1, clamp it to 1 so that
506                    // we draw the unlabeled grid lines for each
507                    //integer interval.
508                    double tmpStep = (yStep > 1.0)? 1.0 : yStep;
509
510                    for (double ypos = _gridStep(unlabeledgrid , yStart,
511                            tmpStep, _ylog);
512                         ypos <= _ytickMax;
513                         ypos = _gridStep(unlabeledgrid, ypos,
514                                 tmpStep, _ylog)) {
515                        int yCoord1 = _lry -
516                            (int)((ypos-_ytickMin)*_ytickscale);
517                        if (_grid && yCoord1 != _uly && yCoord1 != _lry) {
518                            graphics.setColor(Color.lightGray);
519                            graphics.drawLine(_ulx+1,yCoord1,_lrx-1,yCoord1);
520                            graphics.setColor(_foreground);
521                        }                   
522                    }
523                }
524
525                if (needExponent) {
526                    // We zoomed in, so we need the exponent
527                    _yExp = (int)Math.floor(yTmpStart);
528                } else {
529                    _yExp = 0;
530                }
531            }
532
533            // Draw scaling annotation for y axis.
534            if (_yExp != 0) {
535                graphics.drawString("x10", 2, titley);
536                graphics.setFont(_superscriptfont);
537                graphics.drawString(Integer.toString(_yExp),
538                        _labelFontMetrics.stringWidth("x10") + 2,
539                        titley-halflabelheight);
540                graphics.setFont(_labelfont);
541            }
542        } else {
543            // ticks have been explicitly specified
544            Enumeration nt = _yticks.elements();
545            Enumeration nl = _yticklabels.elements();
546            while (nl.hasMoreElements()) {
547                String label = (String) nl.nextElement();
548                double ypos = ((Double)(nt.nextElement())).doubleValue();
549                if (ypos > _yMax || ypos < _yMin) continue;
550                int yCoord1 = _lry - (int)((ypos-_yMin)*_yscale);
551                int offset = 0;
552                if (ypos < _lry - labelheight) offset = halflabelheight;
553                graphics.drawLine(_ulx,yCoord1,xCoord1,yCoord1);
554                graphics.drawLine(_lrx,yCoord1,xCoord2,yCoord1);
555                if (_grid && yCoord1 != _uly && yCoord1 != _lry) {
556                    graphics.setColor(Color.lightGray);
557                    graphics.drawLine(xCoord1,yCoord1,xCoord2,yCoord1);
558                    graphics.setColor(_foreground);
559                }
560                // NOTE: 3 pixel spacing between axis and labels.
561                graphics.drawString(label,
562                        _ulx - _labelFontMetrics.stringWidth(label) - 3,
563                        yCoord1+offset);
564            }
565        }
566       
567        ///////////////////// horizontal axis
568
569        int yCoord1 = _uly+tickLength;
570        int yCoord2 = _lry-tickLength;
571        if (_xticks == null) {
572            // auto-ticks
573
574            // Number of x tick marks.
575            // Need to start with a guess and converge on a solution here.
576            int nx = 10;
577            double xStep = 0.0;
578            int numfracdigits = 0;
579            int charwidth = _labelFontMetrics.stringWidth("8");
580            if (_xlog) {
581                // X axes log labels will be at most 6 chars: -1E-02
582                nx = 2 + width/((charwidth * 6) + 10);
583            } else {
584                // Limit to 10 iterations
585                int count = 0;
586                while (count++ <= 10) {
587                    xStep=_roundUp((_xtickMax-_xtickMin)/(double)nx);
588                    // Compute the width of a label for this xStep
589                    numfracdigits = _numFracDigits(xStep);
590                    // Number of integer digits is the maximum of two endpoints
591                    int intdigits = _numIntDigits(_xtickMax);
592                    int inttemp = _numIntDigits(_xtickMin);
593                    if (intdigits < inttemp) {
594                        intdigits = inttemp;
595                    }
596                    // Allow two extra digits (decimal point and sign).
597                    int maxlabelwidth = charwidth *
598                        (numfracdigits + 2 + intdigits);
599                    // Compute new estimate of number of ticks.
600                    int savenx = nx;
601                    // NOTE: 10 additional pixels between labels.
602                    // NOTE: Try to ensure at least two tick marks.
603                    nx = 2 + width/(maxlabelwidth+10);
604                    if (nx - savenx <= 1 || savenx - nx <= 1) break;
605                }
606            }
607            xStep=_roundUp((_xtickMax-_xtickMin)/(double)nx);
608            numfracdigits = _numFracDigits(xStep);
609           
610            // Compute x starting point so it is a multiple of xStep.
611            double xStart=xStep*Math.ceil(_xtickMin/xStep);
612       
613            // NOTE: Following disables first tick.  Not a good idea?
614            // if (xStart == _xMin) xStart+=xStep;
615       
616            Vector xgrid = null;
617            double xTmpStart = xStart;
618            if (_xlog) {
619                xgrid = _gridInit(xStart, xStep, true, null);
620                //xgrid = _gridInit(xStart, xStep);
621                xTmpStart = _gridRoundUp(xgrid, xStart);
622                if (_debug == 5)
623                    System.out.println("PlotBox: drawPlot: XAXIS "+
624                            " xStart="+xStart+" nx="+nx+" xStep="+xStep+
625                            " xTmpStart="+xTmpStart+
626                            " xgrid.size()="+xgrid.size());
627            }
628
629            // Set to false if we don't need the exponent 
630            boolean needExponent = _xlog;
631
632            // Label the x axis.  The labels are quantized so that
633            // they don't have excess resolution.
634            for (double xpos = xTmpStart;
635                 xpos <= _xtickMax;
636                 xpos = _gridStep(xgrid, xpos, xStep, _xlog)) {
637                String xticklabel;
638                if (_xlog) {
639                    xticklabel = _formatLogNum(xpos, numfracdigits);
640                    if (xticklabel.indexOf('e') != -1 )
641                        needExponent = false;
642                } else {
643                    xticklabel = _formatNum(xpos, numfracdigits);
644                }
645                xCoord1 = _ulx + (int)((xpos-_xtickMin)*_xtickscale);
646                graphics.drawLine(xCoord1,_uly,xCoord1,yCoord1);
647                graphics.drawLine(xCoord1,_lry,xCoord1,yCoord2);
648                if (_grid && xCoord1 != _ulx && xCoord1 != _lrx) {
649                    graphics.setColor(Color.lightGray);
650                    graphics.drawLine(xCoord1,yCoord1,xCoord1,yCoord2);
651                    graphics.setColor(_foreground);
652                }
653                int labxpos = xCoord1 -
654                    _labelFontMetrics.stringWidth(xticklabel)/2;
655                if (_debug == 5)
656                    System.out.println("PlotBox: drawPlot: xpos = "+xpos+
657                            " _xtickMax="+_xtickMax+
658                            " xticklabel="+xticklabel);
659                // NOTE: 3 pixel spacing between axis and labels.
660                graphics.drawString(xticklabel, labxpos,
661                        _lry + 3 + labelheight);
662            }
663
664            if (_xlog) {
665                // Draw in grid lines that don't have labels.
666
667                // If the step is greater than 1, clamp it to 1 so that
668                // we draw the unlabeled grid lines for each
669                // integer interval.
670                double tmpStep = (xStep > 1.0)? 1.0 : xStep;
671
672                // Recalculate the start using the new step.
673                xTmpStart=tmpStep*Math.ceil(_xtickMin/tmpStep);
674
675                Vector unlabeledgrid  = _gridInit(xTmpStart, tmpStep,
676                        false, xgrid);
677                if (unlabeledgrid.size() > 0 ) {
678                    if (_debug == 5)
679                        System.out.println("PlotBox: drawPlot: tmpStep = "+
680                                tmpStep+" xTmpStart="+xTmpStart);
681                    for (double xpos = _gridStep(unlabeledgrid, xTmpStart,
682                            tmpStep, _xlog);
683                         xpos <= _xtickMax;
684                         xpos = _gridStep(unlabeledgrid, xpos,
685                                 tmpStep, _xlog)) {
686                        xCoord1 = _ulx + (int)((xpos-_xtickMin)*_xtickscale);
687                        if (_grid && xCoord1 != _ulx && xCoord1 != _lrx) {
688                            graphics.setColor(Color.lightGray);
689                            graphics.drawLine(xCoord1,_uly+1,xCoord1,_lry-1);
690                            graphics.setColor(_foreground);
691                        }                   
692                    }
693                }
694
695                if (needExponent) {
696                    _xExp = (int)Math.floor(xTmpStart);
697                    graphics.setFont(_superscriptfont);
698                    graphics.drawString(Integer.toString(_xExp), xSPos,
699                            ySPos - halflabelheight);
700                    xSPos -= _labelFontMetrics.stringWidth("x10");
701                    graphics.setFont(_labelfont);
702                    graphics.drawString("x10", xSPos, ySPos);
703                } else {
704                    _xExp = 0;
705                }
706            }
707
708
709        } else {
710            // ticks have been explicitly specified
711            Enumeration nt = _xticks.elements();
712            Enumeration nl = _xticklabels.elements();
713            while (nl.hasMoreElements()) {
714                String label = (String) nl.nextElement();
715                double xpos = ((Double)(nt.nextElement())).doubleValue();
716                if (xpos > _xMax || xpos < _xMin) continue;
717                xCoord1 = _ulx + (int)((xpos-_xMin)*_xscale);
718                graphics.drawLine(xCoord1,_uly,xCoord1,yCoord1);
719                graphics.drawLine(xCoord1,_lry,xCoord1,yCoord2);
720                if (_grid && xCoord1 != _ulx && xCoord1 != _lrx) {
721                    graphics.setColor(Color.lightGray);
722                    graphics.drawLine(xCoord1,yCoord1,xCoord1,yCoord2);
723                    graphics.setColor(_foreground);
724                }
725                int labxpos = xCoord1 - _labelFontMetrics.stringWidth(label)/2;
726                // NOTE: 3 pixel spacing between axis and labels.
727                graphics.drawString(label, labxpos, _lry + 3 + labelheight);
728            }
729        }
730       
731        ///////////////////// Draw title and axis labels now.
732       
733        // Center the title and X label over the plotting region, not
734        // the window.
735        graphics.setColor(_foreground);
736       
737        if (_title != null) {
738            graphics.setFont(_titlefont);
739            int titlex = _ulx +
740                (width - _titleFontMetrics.stringWidth(_title))/2;
741            graphics.drawString(_title,titlex,titley);
742        }
743       
744        graphics.setFont(_labelfont);
745        if (_xlabel != null) {
746            int labelx = _ulx +
747                (width - _labelFontMetrics.stringWidth(_xlabel))/2;
748            graphics.drawString(_xlabel,labelx,ySPos);
749        }
750       
751        int charcenter = 2 + _labelFontMetrics.stringWidth("W")/2;
752        int charheight = labelheight;
753        if (_ylabel != null) {
754            // Vertical label is fairly complex to draw.
755            int yl = _ylabel.length();
756            int starty = _uly + (_lry-_uly)/2 - yl*charheight/2 + charheight;
757            for (int i = 0; i < yl; i++) {
758                String nchar = _ylabel.substring(i,i+1);
759                int cwidth = _labelFontMetrics.stringWidth(nchar);
760                graphics.drawString(nchar,charcenter - cwidth/2, starty);
761                starty += charheight;
762            }
763        }
764    }
765   
766    /**
767     * Rescales so that the data that is currently plotted just fits.
768     */
769    public synchronized void fillPlot () {
770        if (_debug > 7) System.out.println("PlotBox: fillPlot()");
771        fillPlot(_graphics);
772    }
773
774    /**
775     * Rescales so that the data that is currently plotted just fits.
776     */
777    public synchronized void fillPlot (Graphics graphics) {
778        setXRange(_xBottom, _xTop);
779        setYRange(_yBottom, _yTop);
780        paint(graphics);
781    }
782
783    /**
784     * Get the Font by name. 
785     * @deprecated: As of JDK1.1, use Font.decode() instead.
786     * We need to compile under JDK1.0.2, so we use this method.
787     */
788    public Font getFontByName(String fullfontname) {
789        // Can't use Font.decode() here, it is not present in jdk1.0.2
790        //_labelfont = Font.decode(fullfontname);
791
792        String fontname = new String ("helvetica");
793        int style = Font.PLAIN;
794        int size = 12;
795        StringTokenizer stoken = new StringTokenizer(fullfontname,"-");
796       
797        if (stoken.hasMoreTokens()) {
798            fontname = stoken.nextToken();
799        }
800        if (stoken.hasMoreTokens()) {
801            String stylename = stoken.nextToken();
802            // FIXME: we need to be able to mix and match these
803            if (stylename.equals("PLAIN")) {
804                style = Font.PLAIN;
805            } else if (stylename.equals("BOLD")) {
806                style = Font.BOLD;
807            } else if (stylename.equals("ITALIC")) {
808                style = Font.ITALIC;
809            } else {
810                // Perhaps this is a font size?
811                try {
812                    size = Integer.valueOf(stylename).intValue();
813                } catch (NumberFormatException e) {}
814            }
815        }
816        if (stoken.hasMoreTokens()) {
817            try {
818                size = Integer.valueOf(stoken.nextToken()).intValue();
819            } catch (NumberFormatException e) {}
820        }
821        if (_debug > 7) System.out.println("PlotBox: getFontByName: "+
822                fontname+" "+style+" "+size);
823        return new Font(fontname, style, size);
824    }
825
826    /**
827     * Convert a color name into a Color.
828     */
829    public static Color getColorByName(String name) {
830        try {
831            // Check to see if it is a hexadecimal
832            // Can't use Color decode here, it is not in 1.0.2
833            //Color col = Color.decode(name);
834            Color col = new Color(Integer.parseInt(name,16));
835            return col;
836        } catch (NumberFormatException e) {}
837        // FIXME: This is a poor excuse for a list of colors and values.
838        // We should use a hash table here.
839        // Note that Color decode() wants the values to start with 0x.
840        String names[][] = {
841            {"black","00000"},{"white","ffffff"},
842            {"red","ff0000"}, {"green","00ff00"}, {"blue","0000ff"}
843        };
844        for(int i=0;i< names.length; i++) {
845            if(name.equals(names[i][0])) {
846                try {
847                    Color col = new Color(Integer.parseInt(names[i][1],16));
848                    return col;
849                } catch (NumberFormatException e) {}
850            }
851        }
852        return null;
853    }
854
855    /**
856     * Get the dataurl.
857     */
858    public String getDataurl () {
859        return _dataurl;
860    }
861
862    /** Get the document base
863     */
864    public URL getDocumentBase () {
865        return _documentBase;
866    }
867
868    /**
869     * Get the legend for a dataset.
870     */
871    public String getLegend(int dataset) {
872        int idx = _legendDatasets.indexOf(new Integer(dataset),0);
873        if (idx != -1) {
874            return (String)_legendStrings.elementAt(idx);
875        } else {
876            return null;
877        }
878    }
879     
880    /** Get the minimum size of this component.
881     */
882    public Dimension getMinimumSize() {
883        if (_debug > 8) System.out.println("PlotBox: getMinimumSize");
884        return new Dimension(_width, _height);
885    }
886
887
888    /** Get the preferred size of this component.
889     */
890    public Dimension getPreferredSize() {
891        if (_debug > 8) System.out.println("PlotBox: getPreferredSize");
892       return new Dimension(_width, _height);
893    }
894
895
896    /** Initialize the component, creating the fill button and setting
897     * the colors.  If the dataurl has been set, then parse that file.
898     */
899    public void init() {
900        //super.init();
901        if (_debug > 8) System.out.println("PlotBox: init");
902               
903        _xticks = null;
904        _xticklabels = null;
905        _yticks = null;
906        _yticklabels = null;
907
908        _graphics = getGraphics();
909
910        if (_graphics == null) {
911            System.out.println("PlotBox::init(): Internal error: " +
912                    "_graphic was null, be sure to call show()\n"+
913                    "before calling init()");
914            return;
915        }
916
917        if (_foreground != null) {
918            setForeground(_foreground);
919        } else {
920            _foreground = Color.black;
921        }
922
923        if (_background != null) {
924            setBackground(_background);
925        } else {
926            _background = Color.white;
927        }
928        if (_debug > 6)
929            System.out.println("PlotBox: color = "+_foreground+" "+
930                    _background);
931
932        // Make a button that auto-scales the plot.
933        // NOTE: The button infringes on the title space.
934        // If more buttons are added, we may have to find some other place
935        // for them, like below the legend, stacked vertically.
936        setLayout(new FlowLayout(FlowLayout.RIGHT));
937        _fillButton = new Button("fill");
938        add(_fillButton);
939        validate();
940
941        if (_dataurl != null) {
942            parseFile(_dataurl,_documentBase);
943        }
944    }
945       
946    /** The minimum size.
947     * @deprecated As of JDK1.1 in java.awt.component, but we need
948     * to compile under 1.0.2 for netscape3.x compatibility.
949     */
950    public Dimension minimumSize() {
951        if (_debug > 9)
952            System.out.println("PlotBox: minimumSize "+_width+" "+_height);
953        return getMinimumSize();
954    }
955
956    /**
957     * Set the starting point for an interactive zoom box.
958     * @deprecated As of JDK1.1 in java.awt.component
959     * but we need to compile under 1.0.2 for netscape3.x compatibility.
960     */
961    public boolean mouseDown(Event evt, int x, int y) { // deprecated
962        // constrain to be in range
963        if (_debug > 9) System.out.println("PlotBox: mouseDown "+x+" "+y);
964        if (y > _lry) y=_lry;
965        if (y < _uly) y=_uly;
966        if (x > _lrx) x=_lrx;
967        if (x < _ulx) x=_ulx;
968        _zoomx = x;
969        _zoomy = y;
970        return true;
971    }
972   
973    /**
974     * Draw a box for an interactive zoom box.
975     * Return a boolean indicating whether or not we have dealt with
976     * the event.
977     * @deprecated As of JDK1.1 in java.awt.component
978     * but we need to compile under 1.0.2 for netscape3.x compatibility.
979     */
980    public synchronized boolean mouseDrag(Event evt, int x, int y) {
981        // We make this method synchronized so that we can draw the drag
982        // box properly.  If this method is not synchronized, then
983        // we could end up calling setXORMode, being interrupted
984        // and having setPaintMode() called in another method.
985
986        if (_debug > 9) System.out.println("PlotBox: mouseDrag "+x+" "+y);
987
988        if (_graphics == null) {
989            System.out.println("PlotBox.mouseDrag(): Internal error: " +
990                    "_graphic was null, be sure to call init()");
991        }
992
993        // Bound the rectangle so it doesn't go outside the box.
994        if (y > _lry) y=_lry;
995        if (y < _uly) y=_uly;
996        if (x > _lrx) x=_lrx;
997        if (x < _ulx) x=_ulx;
998        // erase previous rectangle, if there was one.
999        if ((_zoomx != -1 || _zoomy != -1)) {
1000            // Ability to zoom out added by William Wu.
1001            // If we are not already zooming, figure out whether we
1002            // are zooming in or out.
1003            if (_zoomin == false && _zoomout == false){
1004                if (y < _zoomy) {
1005                    _zoomout = true;
1006                    // Draw reference box.
1007                    _graphics.drawRect(_zoomx-15, _zoomy-15, 30, 30);
1008
1009                } else if (y > _zoomy) {
1010                    _zoomin = true;
1011                }
1012            }
1013
1014            if (_zoomin == true){   
1015                _graphics.setXORMode(_background);
1016                // Erase the previous box if necessary.
1017                if ((_zoomxn != -1 || _zoomyn != -1) && (_drawn == true)) {
1018                    int minx = Math.min(_zoomx, _zoomxn);
1019                    int maxx = Math.max(_zoomx, _zoomxn);
1020                    int miny = Math.min(_zoomy, _zoomyn);
1021                    int maxy = Math.max(_zoomy, _zoomyn);
1022                    _graphics.drawRect(minx, miny, maxx - minx, maxy - miny);
1023                }
1024                // Draw a new box if necessary.
1025                if (y > _zoomy) {
1026                    _zoomxn = x;
1027                      _zoomyn = y;
1028                    int minx = Math.min(_zoomx, _zoomxn);
1029                    int maxx = Math.max(_zoomx, _zoomxn);
1030                    int miny = Math.min(_zoomy, _zoomyn);
1031                    int maxy = Math.max(_zoomy, _zoomyn);
1032                    _graphics.drawRect(minx, miny, maxx - minx, maxy - miny);
1033                    _graphics.setPaintMode();
1034                    _drawn = true;
1035                    return true;
1036                } else _drawn = false;
1037            } else if (_zoomout == true){
1038                _graphics.setXORMode(_background);
1039                // Erase previous box if necessary.
1040                if ((_zoomxn != -1 || _zoomyn != -1) && (_drawn == true)) {
1041                    int x_diff = Math.abs(_zoomx-_zoomxn);
1042                    int y_diff = Math.abs(_zoomy-_zoomyn);
1043                    _graphics.drawRect(_zoomx-15-x_diff, _zoomy-15-y_diff,
1044                            30+x_diff*2, 30+y_diff*2);
1045                }
1046                if (y < _zoomy){
1047                    _zoomxn = x;
1048                    _zoomyn = y;     
1049                    int x_diff = Math.abs(_zoomx-_zoomxn);
1050                    int y_diff = Math.abs(_zoomy-_zoomyn);
1051                    _graphics.drawRect(_zoomx-15-x_diff, _zoomy-15-y_diff,
1052                            30+x_diff*2, 30+y_diff*2);
1053                    _graphics.setPaintMode();
1054                    _drawn = true;
1055                    return true;
1056                } else _drawn = false;
1057            }
1058        }
1059        _graphics.setPaintMode();
1060        return false;
1061    }
1062
1063    /**
1064     * Zoom in or out based on the box that has been drawn.
1065     * @deprecated As of JDK1.1 in java.awt.component
1066     * but we need to compile under 1.0.2 for netscape3.x compatibility.
1067     */
1068    public synchronized boolean mouseUp(Event evt, int x, int y) { //deprecated
1069        // We make this method synchronized so that we can draw the drag
1070        // box properly.  If this method is not synchronized, then
1071        // we could end up calling setXORMode, being interrupted
1072        // and having setPaintMode() called in another method.
1073
1074        if (_debug > 9) System.out.println("PlotBox: mouseUp");
1075        boolean handled = false;
1076        if ((_zoomin == true) && (_drawn == true)){ 
1077            if (_zoomxn != -1 || _zoomyn != -1) {
1078                // erase previous rectangle.
1079                int minx = Math.min(_zoomx, _zoomxn);
1080                int maxx = Math.max(_zoomx, _zoomxn);
1081                int miny = Math.min(_zoomy, _zoomyn);
1082                int maxy = Math.max(_zoomy, _zoomyn);
1083                _graphics.setXORMode(_background);
1084                _graphics.drawRect(minx, miny, maxx - minx, maxy - miny);
1085                _graphics.setPaintMode();
1086                // constrain to be in range
1087                if (y > _lry) y=_lry;
1088                if (y < _uly) y=_uly;
1089                if (x > _lrx) x=_lrx;
1090                if (x < _ulx) x=_ulx;
1091                // NOTE: ignore if total drag less than 5 pixels.
1092                if ((Math.abs(_zoomx-x) > 5) && (Math.abs(_zoomy-y) > 5)) {
1093                    double a = _xMin + (_zoomx - _ulx)/_xscale;
1094                    double b = _xMin + (x - _ulx)/_xscale;
1095                    if (a < b) setXRange(a, b);
1096                    else setXRange(b, a);
1097                    a = _yMax - (_zoomy - _uly)/_yscale;
1098                    b = _yMax - (y - _uly)/_yscale;
1099                    if (a < b) setYRange(a, b);
1100                    else setYRange(b, a);
1101                }
1102                drawPlot(_graphics, true);
1103                handled = true;
1104            }
1105        } else if ((_zoomout == true) && (_drawn == true)){
1106            // Erase previous rectangle.
1107            _graphics.setXORMode(_background);
1108            int x_diff = Math.abs(_zoomx-_zoomxn);
1109            int y_diff = Math.abs(_zoomy-_zoomyn);
1110            _graphics.drawRect(_zoomx-15-x_diff, _zoomy-15-y_diff,
1111                    30+x_diff*2, 30+y_diff*2);
1112            _graphics.setPaintMode();
1113
1114            // Calculate zoom factor.
1115            double a = (double)(Math.abs(_zoomx - x)) / 30.0;
1116            double b = (double)(Math.abs(_zoomy - y)) / 30.0;
1117            double newx1 = _xMax + (_xMax - _xMin) * a;
1118            double newx2 = _xMin - (_xMax - _xMin) * a;
1119            if (newx1 > _xTop) newx1 = _xTop;
1120            if (newx2 < _xBottom) newx2 = _xBottom;
1121            double newy1 = _yMax + (_yMax - _yMin) * b;
1122            double newy2 = _yMin - (_yMax - _yMin) * b;
1123            if (newy1 > _yTop) newy1 = _yTop;
1124            if (newy2 < _yBottom) newy2 = _yBottom;
1125            setXRange(newx2, newx1);
1126            setYRange(newy2, newy1);
1127            drawPlot(_graphics, true);
1128            handled = true;
1129        } else if (_drawn == false){
1130            drawPlot(_graphics, true);
1131            handled = true;
1132        }
1133        _drawn = false;
1134        _zoomin = _zoomout = false;
1135        _zoomxn = _zoomyn = _zoomx = _zoomy = -1;
1136        return handled;
1137    }
1138
1139    /**
1140     * Paint the component contents, which in this base class is
1141     * only the axes.
1142     */
1143    public void paint(Graphics graphics) {
1144        if (_debug > 7) System.out.println("PlotBox: paint");
1145        //super.paint(graphics);
1146        drawPlot(graphics, true);
1147    }
1148
1149    /**
1150     * Syntactic sugar for parseFile(dataurl, documentBase);
1151     */ 
1152    public void parseFile(String dataurl) {
1153       parseFile(dataurl, (URL)null);
1154    }
1155
1156    /**
1157     * Open up the input file, which could be stdin, a URL or a file.
1158     * This code can be called from an application, which means that
1159     * getDocumentBase() might fail.
1160     */
1161    public void parseFile(String dataurl, URL documentBase) {
1162        DataInputStream in = null;
1163        if (_debug > 2) System.out.println("PlotBox: parseFile("+ dataurl+" "+
1164                documentBase+") _dataurl = "+_dataurl+" "+_documentBase);
1165        if (dataurl == null || dataurl.length() == 0) {
1166            // Open up stdin
1167            in = new DataInputStream(System.in);
1168        } else {
1169            try {
1170                URL url = null;
1171                if (documentBase == null && _documentBase != null) {
1172                    documentBase = _documentBase;
1173                }
1174                if (documentBase == null) {
1175                    url = new URL(_dataurl);
1176                } else {
1177                    try {
1178                        url = new URL(documentBase, dataurl);
1179                    } catch (NullPointerException e) {
1180                        // If we got a NullPointerException, then perhaps we
1181                        // are calling this as an application, not as an applet
1182                        url = new URL(_dataurl);
1183                    }
1184                }
1185                in = new DataInputStream(url.openStream());
1186            } catch (MalformedURLException e) {
1187                try {
1188                    // Just try to open it as a file.
1189                    in = new DataInputStream(new FileInputStream(dataurl));
1190                } catch (FileNotFoundException me) {
1191                    _errorMsg = new String [2];
1192                    _errorMsg[0] = "File not found: " + dataurl;
1193                    _errorMsg[1] = me.getMessage();
1194                    return;
1195                } catch (SecurityException me) {
1196                    _errorMsg = new String [2];
1197                    _errorMsg[0] = "Security Exception: " + dataurl;
1198                    _errorMsg[1] = me.getMessage();
1199                    return;
1200                }
1201            } catch (IOException ioe) {
1202                _errorMsg = new String [2];
1203                _errorMsg[0] = "Failure opening URL: " + dataurl;
1204                _errorMsg[1] = ioe.getMessage();
1205                return;
1206            }
1207        }
1208
1209        _newFile(); // Hook for child classes to do any preprocessing.
1210
1211        // At this point, we've opened the data source, now read it in
1212        try {
1213            if (_binary) {
1214                _parseBinaryStream(in);
1215            } else {
1216
1217                String line = in.readLine(); // FIXME: readLine() is
1218                // deprecated in JDK1.1, but we need to compile under
1219                //1.0.2 for netscape3.x compatibility.
1220                while (line != null) {
1221                    _parseLine(line);
1222                    line = in.readLine(); // readLine() is deprecated.
1223                }
1224            }
1225        } catch (MalformedURLException e) {
1226            _errorMsg = new String [2];
1227            _errorMsg[0] = "Malformed URL: " + dataurl;
1228            _errorMsg[1] = e.getMessage();
1229            return;
1230        } catch (IOException e) {
1231            _errorMsg = new String [2];
1232            _errorMsg[0] = "Failure reading data: " + dataurl;
1233            _errorMsg[1] = e.getMessage();
1234        } catch (PlotDataException e) {
1235            _errorMsg = new String [2];
1236            _errorMsg[0] = "Incorrectly formatted plot data in " + dataurl;
1237            _errorMsg[1] = e.getMessage();
1238        } finally {
1239            try {
1240                in.close();
1241            } catch (IOException me) {}
1242        }
1243       
1244    }
1245 
1246    /** The preferred size.
1247     * @deprecated As of JDK1.1 in java.awt.component, but we need
1248     * to compile under 1.0.2 for netscape3.x compatibility.
1249     */
1250    public Dimension preferredSize() {
1251        if (_debug > 9)
1252            System.out.println("PlotBox: preferredSize "+_width+" "+_height);
1253        return getPreferredSize();
1254    }
1255
1256    /** Reshape
1257     */
1258    public void reshape(int x, int y, int width, int height) {
1259        if (_debug > 9)
1260            System.out.println("PlotBox: reshape: "+x+" "+y+" "+
1261                    width+" "+height);
1262        _width = width;
1263        _height = height;
1264        super.reshape(x,y,_width,_height);
1265    }
1266
1267    /**
1268     * Resize the plot.
1269     * @deprecated As of JDK1.1 in java.awt.component, but we need
1270     * to compile under 1.0.2 for netscape3.x compatibility.
1271     */
1272    public void resize(int width, int height) {
1273        if (_debug > 8)
1274            System.out.println("PlotBox: resize"+width+" "+height);
1275        _width = width;
1276        _height = height;
1277        super.resize(width,height); // FIXME: resize() is deprecated.
1278    }
1279
1280    /** Set the background color.
1281     */
1282    public void setBackground (Color background) {
1283        _background = background;
1284        super.setBackground(_background);
1285    }
1286
1287    /** Set the debug value.  The higher the integer, the greater the
1288     * number of debugging messages printed to stdout.  Useful values
1289     * are 10 - argument parsing, 20 - legend parsing, 101 - data point
1290     * debugging.
1291     */
1292    public void setDebug (int debug ) {
1293        _debug = debug;
1294    }
1295
1296    /** Set the foreground color.
1297     */
1298    public void setForeground (Color foreground) {
1299        _foreground = foreground;
1300        super.setForeground(_foreground);
1301    }
1302
1303    /** Set the binary flag to true if we are reading pxgraph format binary
1304     * data.
1305     */
1306    public void setBinary (boolean binary) {
1307        _binary = binary;
1308    }
1309
1310    /** Set the dataurl.
1311     */
1312    public void setDataurl (String dataurl) {
1313        _dataurl = dataurl;
1314    }
1315
1316    /** Set the document base so that we can find the dataurl.
1317     */
1318    public void setDocumentBase (URL documentBase) {
1319        _documentBase = documentBase;
1320    }
1321
1322    /** Control whether the grid is drawn.
1323     */
1324    public void setGrid (boolean grid) {
1325        _grid = grid;
1326    }
1327   
1328    /** Set the label font, which is used for axis labels and legend labels.
1329     */
1330    public void setLabelFont (String fullfontname) {
1331        // Can't use Font.decode() here, it is not present in jdk1.0.2
1332        //_labelfont = Font.decode(fullfontname);
1333
1334        _labelfont = getFontByName(fullfontname);
1335    }
1336    /**
1337     * Set the title of the graph.  The title will appear on the subsequent
1338     * call to <code>paint()</code> or <code>drawPlot()</code>.
1339     */
1340    public void setTitle (String title) {
1341        _title = title;
1342    }
1343   
1344    /** Set the title font.
1345     */
1346    public void setTitleFont (String fullfontname) {
1347        // Can't use Font.decode() here, it is not present in jdk1.0.2
1348        //_titlefont = Font.decode(fullfontname);
1349
1350        _titlefont = getFontByName(fullfontname);
1351        _titleFontMetrics = getFontMetrics(_titlefont);
1352    }
1353
1354    /**
1355     * Set the label for the X (horizontal) axis.  The label will
1356     * appear on the subsequent call to <code>paint()</code> or
1357     * <code>drawPlot()</code>.
1358     */
1359    public void setXLabel (String label) {
1360        _xlabel = label;
1361    }
1362
1363    /**
1364     * Control whether the X axis is drawn with a logarithmic scale.
1365     */
1366    public void setXLog (boolean xlog) {
1367        _xlog = xlog;
1368    }
1369
1370    /**
1371     * Set the X (horizontal) range of the plot.  If this is not done
1372     * explicitly, then the range is computed automatically from data
1373     * available when <code>paint()</code> or <code>drawPlot()</code>
1374     * are called.  If min and max are identical, then the range is
1375     * arbitrarily spread by 1.
1376     */
1377    public void setXRange (double min, double max) {
1378        if (_debug > 7) System.out.println("PlotBox: setXRange");
1379        _xRangeGiven = true;
1380        _setXRange(min,max);
1381    }
1382
1383    /**
1384     * Set the label for the Y (vertical) axis.  The label will
1385     * appear on the subsequent call to <code>paint()</code> or
1386     * <code>drawPlot()</code>.
1387     */
1388    public void setYLabel (String label) {
1389        _ylabel = label;
1390    }
1391
1392    /**
1393     * Control whether the Y axis is drawn with a logarithmic scale.
1394     */
1395    public void setYLog (boolean ylog) {
1396        _ylog = ylog;
1397    }
1398
1399    /**
1400     * Set the Y (vertical) range of the plot.  If this is not done
1401     * explicitly, then the range is computed automatically from data
1402     * available when <code>paint()</code> or <code>drawPlot()</code>
1403     * are called.  If min and max are identical, then the range is
1404     * arbitrarily spread by 0.1.
1405     */
1406    public void setYRange (double min, double max) {
1407        _yRangeGiven = true;
1408        _setYRange(min,max);
1409    }
1410
1411    //////////////////////////////////////////////////////////////////////////
1412    ////                         protected methods                        ////
1413
1414    /**
1415     * Put a mark corresponding to the specified dataset at the
1416     * specified x and y position.   The mark is drawn in the
1417     * current color.  In this base class, a point is a
1418     * filled rectangle 6 pixels across.  Note that marks greater than
1419     * about 6 pixels in size will not look very good since they will
1420     * overlap axis labels and may not fit well in the legend.   The
1421     * <i>clip</i> argument, if <code>true</code>, states
1422     * that the point should not be drawn if
1423     * it is out of range.
1424     */
1425    protected void _drawPoint(Graphics graphics,
1426            int dataset, long xpos, long ypos, boolean clip) {
1427        boolean pointinside = ypos <= _lry && ypos >= _uly &&
1428            xpos <= _lrx && xpos >= _ulx;
1429        if (!pointinside && clip) {return;}
1430        graphics.fillRect((int)xpos-6, (int)ypos-6, 6, 6);
1431    }
1432
1433    /** Hook for child classes to do any file preprocessing
1434     */ 
1435    protected void _newFile(){
1436    }
1437
1438    /**
1439     * Hook to parse a binary stream.
1440     * @exception PlotDataException if there is a serious data format problem.
1441     * @exception java.io.IOException if an I/O error occurs.
1442     */
1443    protected void _parseBinaryStream(DataInputStream in) throws
1444    PlotDataException, IOException {
1445        throw new PlotDataException("Binary data not supported in the" +
1446                "baseclass");
1447    }
1448
1449    /**
1450     * Parse a line that gives plotting information.  In this base
1451     * class, only lines pertaining to the title and labels are processed.
1452     * Everything else is ignored. Return true if the line is recognized.
1453     */
1454    protected boolean _parseLine (String line) {
1455        // Parse commands in the input file, ignoring lines with
1456        // syntax errors or unrecognized commands.
1457        if (_debug > 20) System.out.println("PlotBox: parseLine "+ line);
1458        // We convert the line to lower case so that the command
1459        // names are case insensitive.
1460        String lcLine = new String(line.toLowerCase());
1461        if (lcLine.startsWith("#")) {
1462            // comment character
1463            return true;
1464        }
1465        if (lcLine.startsWith("titletext:")) {
1466            setTitle((line.substring(10)).trim());
1467            return true;
1468        }
1469        if (lcLine.startsWith("xlabel:")) {
1470            setXLabel((line.substring(7)).trim());
1471            return true;
1472        }
1473        if (lcLine.startsWith("ylabel:")) {
1474            setYLabel((line.substring(7)).trim());
1475            return true;
1476        }
1477        if (lcLine.startsWith("xrange:")) {
1478            int comma = line.indexOf(",", 7);
1479            if (comma > 0) {
1480                String min = (line.substring(7,comma)).trim();
1481                String max = (line.substring(comma+1)).trim();
1482                try {
1483                    Double dmin = new Double(min);
1484                    Double dmax = new Double(max);
1485                    setXRange(dmin.doubleValue(), dmax.doubleValue());
1486                } catch (NumberFormatException e) {
1487                    // ignore if format is bogus.
1488                }
1489            }
1490            return true;
1491        }
1492        if (lcLine.startsWith("yrange:")) {
1493            int comma = line.indexOf(",", 7);
1494            if (comma > 0) {
1495                String min = (line.substring(7,comma)).trim();
1496                String max = (line.substring(comma+1)).trim();
1497                try {
1498                    Double dmin = new Double(min);
1499                    Double dmax = new Double(max);
1500                    setYRange(dmin.doubleValue(), dmax.doubleValue());
1501                } catch (NumberFormatException e) {
1502                    // ignore if format is bogus.
1503                }
1504            }
1505            return true;
1506        }
1507        if (lcLine.startsWith("xticks:")) {
1508            // example:
1509            // XTicks "label" 0, "label" 1, "label" 3
1510            _parsePairs(line.substring(7), true);
1511            return true;
1512        }
1513        if (lcLine.startsWith("xlog:")) {
1514            if (lcLine.indexOf("off",5) >= 0) {
1515                _xlog = false;
1516            } else {
1517                _xlog = true;
1518            }
1519            return true;
1520        }
1521       
1522        if (lcLine.startsWith("ylog:")) {
1523            if (lcLine.indexOf("off",5) >= 0) {
1524                _ylog = false;
1525            } else {
1526                _ylog = true;
1527            }
1528            return true;
1529        }
1530       
1531        if (lcLine.startsWith("grid:")) {
1532            if (lcLine.indexOf("off",5) >= 0) {
1533                _grid = false;
1534            } else {
1535                _grid = true;
1536            }
1537            return true;
1538        }
1539        if (lcLine.startsWith("color:")) {
1540            if (lcLine.indexOf("off",6) >= 0) {
1541                _usecolor = false;
1542            } else {
1543                _usecolor = true;
1544            }
1545            return true;
1546        }
1547        return false;
1548    }
1549
1550    /** Set the visibility of the Fill button.
1551     */
1552    protected void _setButtonsVisibility(boolean vis) {
1553        // _fillButton.setVisible(vis);
1554        if (vis) {
1555            _fillButton.show(); // FIXME: show() is
1556            // deprecated in JDK1.1, but we need to compile under
1557            // 1.0.2 for netscape3.x compatibility.
1558        } else {
1559            _fillButton.hide(); // FIXME: hide() is
1560            // deprecated in JDK1.1, but we need to compile under
1561            // 1.0.2 for netscape3.x compatibility.
1562        }
1563    }
1564
1565    //////////////////////////////////////////////////////////////////////////
1566    ////                           protected variables                    ////
1567   
1568    // If non-zero, print out debugging messages.  Use setDebug() to set this.
1569    protected int _debug = 0;
1570   
1571    // The graphics context to operate in.  Note that printing will call
1572    // paint with a different graphics object, so we have to pass this
1573    // around properly.
1574    protected Graphics _graphics = null;
1575
1576    // The range of the data to be plotted.
1577    protected double _yMax = 0, _yMin = 0, _xMax = 0, _xMin = 0;
1578
1579    // The factor we pad by so that we don't plot points on the axes.
1580    protected static final double _PADDING = 0.05;
1581
1582    // Whether the ranges have been given.
1583    protected boolean _xRangeGiven = false;
1584    protected boolean _yRangeGiven = false;
1585    // The minimum and maximum values registered so far, for auto ranging.
1586    protected double _xBottom = Double.MAX_VALUE;
1587    protected double _xTop = - Double.MAX_VALUE;
1588    protected double _yBottom = Double.MAX_VALUE;
1589    protected double _yTop = - Double.MAX_VALUE;
1590   
1591    // Whether to draw the axes using a logarithmic scale.
1592    protected boolean _xlog = false, _ylog = false;
1593
1594    // For use in calculating log base 10.  A log times this is a log base 10.
1595    protected static final double _LOG10SCALE = 1/Math.log(10);
1596   
1597    // Whether to draw a background grid.
1598    protected boolean _grid = true;
1599   
1600    // Color of the background, settable from HTML.
1601    protected Color _background = null;
1602    // Color of the foreground, settable from HTML.
1603    protected Color _foreground = null;
1604
1605    // Derived classes can increment these to make space around the plot.
1606    protected int _topPadding = 10;
1607    protected int _bottomPadding = 5;
1608    protected int _rightPadding = 10;
1609    protected int _leftPadding = 10;
1610
1611    // The plot rectangle in pixels.
1612    // The naming convention is: "_ulx" = "upper left x", where "x" is
1613    // the horizontal dimension.
1614    protected int _ulx = 1 , _uly = 1, _lrx = 100, _lry = 100;
1615
1616    // Scaling used in plotting points.
1617    protected double _yscale = 1.0, _xscale = 1.0;
1618   
1619    // Indicator whether to use _colors
1620    protected boolean _usecolor = true;
1621
1622    // Default _colors, by data set.
1623    // There are 11 colors so that combined with the
1624    // 10 marks of the Plot class, we can distinguish 110
1625    // distinct data sets.
1626    static protected Color[] _colors = {
1627        new Color(0xff0000),   // red
1628        new Color(0x0000ff),   // blue
1629        new Color(0x14ff14),   // green-ish
1630        new Color(0x000000),   // black
1631        new Color(0xffa500),   // orange
1632        new Color(0x53868b),   // cadetblue4
1633        new Color(0xff7f50),   // coral
1634        new Color(0x55bb2f),   // dark green-ish
1635        new Color(0x90422d),   // sienna-ish
1636        new Color(0xa0a0a0),   // grey-ish
1637        new Color(0x00aaaa),   // cyan-ish
1638    };
1639       
1640    // Width and height of component in pixels.
1641    protected int _width = 400, _height = 400;
1642
1643    //////////////////////////////////////////////////////////////////////////
1644    ////                         private methods                          ////
1645
1646    /**
1647     * Draw the legend in the upper right corner and return the width
1648     * (in pixels)  used up.  The arguments give the upper right corner
1649     * of the region where the legend should be placed.
1650     */
1651    private int _drawLegend(Graphics graphics, int urx, int ury) {
1652        // FIXME: consolidate all these for efficiency
1653        graphics.setFont(_labelfont);
1654        int spacing = _labelFontMetrics.getHeight();
1655
1656        Enumeration v = _legendStrings.elements();
1657        Enumeration i = _legendDatasets.elements();
1658        int ypos = ury + spacing;
1659        int maxwidth = 0;
1660        while (v.hasMoreElements()) {
1661            String legend = (String) v.nextElement();
1662            // NOTE: relies on _legendDatasets having the same num. of entries.
1663            int dataset = ((Integer) i.nextElement()).intValue();
1664            if (dataset >= 0) {
1665                if (_usecolor) {
1666                    // Points are only distinguished up to the number of colors
1667                    int color = dataset % _colors.length;
1668                    graphics.setColor(_colors[color]);
1669                }
1670                _drawPoint(graphics, dataset, urx-3, ypos-3, false);
1671
1672                graphics.setColor(_foreground);
1673                int width = _labelFontMetrics.stringWidth(legend);
1674                if (width > maxwidth) maxwidth = width;
1675                graphics.drawString(legend, urx - 15 - width, ypos);
1676                ypos += spacing;
1677            }
1678        }
1679        return 22 + maxwidth;  // NOTE: subjective spacing parameter.
1680    }
1681
1682    /*
1683     * Return the number as a String for use as a label on a
1684     * logarithmic axis.
1685     * Since this is a log plot, number passed in will not have too many
1686     * digits to cause problems.
1687     * If the number is an integer, then we print 1e<num>.
1688     * If the numer is not an integer, then print only the fractional
1689     * components.
1690     */
1691    private String _formatLogNum (double num, int numfracdigits) {
1692        String results;
1693        int exponent = (int)num;
1694
1695        // Determine the exponent, prepending 0 or -0 if necessary.
1696        if (exponent >= 0 && exponent < 10) {
1697            results = "0" + exponent;
1698        } else {
1699            if (exponent < 0 && exponent > -10) {
1700                results = "-0" + (-exponent);
1701            } else {
1702                results = Integer.toString(exponent);
1703            }
1704        }
1705
1706        // Handle the mantissa.
1707        if (num >= 0.0 ) {
1708            if (num - (int)(num) < 0.001) {
1709                results = "1e" + results;
1710            } else {
1711                results = _formatNum(Math.pow(10.0,(num - (int)num)),
1712                        numfracdigits);
1713            }
1714        } else {
1715            if (-num - (int)(-num) < 0.001) {
1716                results = "1e" + results;
1717            } else {
1718                results = _formatNum(Math.pow(10.0,(num - (int)num))*10,
1719                        numfracdigits);
1720            }
1721        }
1722        if (_debug == 5)
1723            System.out.println("PlotBox: _formatLogNum: "+num+" "+
1724                    Math.pow(10.0,num)+" "+results+" "+(num -(int)num));
1725        return results;
1726    }
1727
1728    /*
1729     * Return a string for displaying the specified number
1730     * using the specified number of digits after the decimal point.
1731     * NOTE: This could be replaced by the NumberFormat class
1732     * which is available in jdk 1.1.  We don't do this now so that
1733     * it will run on today's browsers, which use jdk 1.0.2.
1734     */
1735    private String _formatNum (double num, int numfracdigits) {
1736        // First, round the number.
1737        double fudge = 0.5;
1738        if (num < 0.0) fudge = -0.5;
1739        String numString = Double.toString(num +
1740                fudge*Math.pow(10.0, -numfracdigits));
1741        // Next, find the decimal point.
1742        int dpt = numString.lastIndexOf(".");
1743        StringBuffer result = new StringBuffer();
1744        if (dpt < 0) {
1745            // The number we are given is an integer.
1746            if (numfracdigits <= 0) {
1747                // The desired result is an integer.
1748                result.append(numString);
1749                return result.toString();
1750            }
1751            // Append a decimal point and some zeros.
1752            result.append(".");
1753            for (int i = 0; i < numfracdigits; i++) {
1754                result.append("0");
1755            }
1756            return result.toString();
1757        } else {
1758            // There are two cases.  First, there may be enough digits.
1759            int shortby = numfracdigits - (numString.length() - dpt -1);
1760            if (shortby <= 0) {
1761                int numtocopy = dpt + numfracdigits + 1;
1762                if (numfracdigits == 0) {
1763                    // Avoid copying over a trailing decimal point.
1764                    numtocopy -= 1;
1765                }
1766                result.append(numString.substring(0, numtocopy));
1767                return result.toString();
1768            } else {
1769                result.append(numString);
1770                for (int i = 0; i < shortby; i++) {
1771                    result.append("0");
1772                }
1773                return result.toString();               
1774            }
1775        }
1776    }
1777 
1778    /*
1779     * Determine what values to use for log axes.
1780     * Based on initGrid() from xgraph.c by David Harrison.
1781     */
1782    private Vector _gridInit(double low, double step, boolean labeled,
1783            Vector oldgrid) {
1784
1785        // How log axes work:
1786        // _gridInit() creates a vector with the values to use for the
1787        // log axes.  For example, the vector might contain
1788        // {0.0 0.301 0.698}, which could correspond to
1789        // axis labels {1 1.2 1.5 10 12 15 100 120 150}
1790        //
1791        // _gridStep() gets the proper value.  _gridInit is cycled through
1792        // for each integer log value.
1793        //
1794        // To turn on debugging messages for the log axes, set _debug to 5.
1795        //
1796        // Bugs in log axes:
1797        // * Sometimes not enough grid lines are displayed because the
1798        // region is small.  This bug is present in the original xgraph
1799        // binary, which is the basis of this code.  The problem is that
1800        // as ratio gets closer to 1.0, we need to add more and more
1801        // grid marks.
1802
1803        Vector grid = new Vector(10);
1804        //grid.addElement(new Double(0.0));
1805        double ratio = Math.pow(10.0, step);
1806        int ngrid = 1;
1807        if (labeled) {
1808            // Set up the number of grid lines that will be labeled
1809            if (ratio <= 3.5) {
1810                if (ratio > 2.0)
1811                    ngrid = 2;
1812                else if (ratio > 1.26)
1813                    ngrid = 5;
1814                else if (ratio > 1.125)
1815                    ngrid = 10;
1816                else
1817                    ngrid = (int)Math.rint(1.0/step);
1818
1819            }
1820        } else {
1821            // Set up the number of grid lines that will not be labeled
1822            if (ratio > 10.0)
1823                ngrid = 1;
1824            else if (ratio > 3.0)
1825                ngrid = 2;
1826            else if (ratio > 2.0)
1827                ngrid = 5;
1828            else if (ratio > 1.125)
1829                ngrid = 10;
1830            else
1831                ngrid = 100;
1832            // Note: we should keep going here, but this increases the
1833            // size of the grid array and slows everything down.
1834        }       
1835
1836        if (_debug == 5)
1837            System.out.println("PlotBox: gridInit2: low="+low+" step="+step+
1838                    " labeled="+labeled+" ratio="+ratio+" ngrid="+ngrid);
1839
1840        int oldgridi = 0;
1841        for (int i = 0; i < ngrid; i++) {
1842            double gridval = i * 1.0/ngrid * 10;
1843            double logval = _LOG10SCALE*Math.log(gridval);
1844            if (logval == Double.NEGATIVE_INFINITY)
1845              logval = 0.0;
1846
1847            // If oldgrid is not null, then do not draw lines that
1848            // were already drawn in oldgrid.  This is necessary
1849            // so we avoid obliterating the tick marks on the plot borders.
1850            if (oldgrid != null && oldgridi < oldgrid.size() ) {
1851                if (_debug == 5)
1852                    System.out.println("PlotBox: gridInit2: oldgrid="+
1853                           ((Double)oldgrid.elementAt(oldgridi)).doubleValue()+
1854                            " oldgridi="+oldgridi+
1855                            " oldgrid.size()="+oldgrid.size()+
1856                            " oldgrid="+oldgrid+
1857                            " gridval ="+gridval+" logval="+logval);
1858               
1859                // Cycle through the oldgrid until we find an element
1860                // that is equal to or greater than the element we are
1861                // trying to add.
1862                while (oldgridi < oldgrid.size() &&
1863                        ((Double)oldgrid.elementAt(oldgridi)).doubleValue() <
1864                        logval) {
1865                    oldgridi++;
1866                }
1867
1868                if (oldgridi < oldgrid.size()) {
1869                    // Using == on doubles is bad if the numbers are close,
1870                    // but not exactly equal.
1871                    if (Math.abs(
1872                          ((Double)oldgrid.elementAt(oldgridi)).doubleValue() -
1873                          logval) > 0.00001) {
1874                        grid.addElement(new Double(logval));
1875                    }
1876                } else {
1877                    grid.addElement(new Double(logval));
1878                }
1879            } else {
1880                if (_debug == 5)
1881                    System.out.println("PlotBox: gridInit2: no oldgrid "+
1882                            " gridval ="+gridval+" logval="+logval);
1883                grid.addElement(new Double(logval));
1884            }
1885        }
1886       
1887        // _gridCurJuke and _gridBase are used in _gridStep();
1888        _gridCurJuke = 0;
1889        if (low == -0.0)
1890            low = 0.0;
1891        _gridBase = Math.floor(low);
1892        double x = low - _gridBase;
1893
1894        // Set gridCurJuke so that the value in grid is greater than
1895        // or equal to x.  This sets us up to process the first point.
1896        for (_gridCurJuke = -1;
1897             (_gridCurJuke+1) < grid.size() && x >=
1898                 ((Double)grid.elementAt(_gridCurJuke+1)).doubleValue();
1899             _gridCurJuke++){
1900        }
1901        if (_debug == 5)
1902            System.out.println("PlotBox: gridInit2: grid="+grid+
1903                    " _gridBase="+_gridBase+" _gridCurJuke="+_gridCurJuke);
1904
1905        return grid;
1906    }
1907
1908    /*
1909     * Round pos up to the nearest value in the grid.
1910     */
1911    private double _gridRoundUp(Vector grid, double pos) {
1912        double x = pos - Math.floor(pos);
1913        int i;
1914        for(i = 0; i < grid.size() && 
1915                x >= ((Double)grid.elementAt(i)).doubleValue();
1916            i++){}
1917        if (i >= grid.size())
1918            return pos;
1919        else 
1920            return Math.floor(pos) + ((Double)grid.elementAt(i)).doubleValue();
1921    }
1922
1923    /*
1924     * Used to find the next value for the axis label.
1925     * For non-log axes, we just return pos + step.
1926     * For log axes, we read the appropriate value in the grid Vector,
1927     * add it to _gridBase and return the sum.  We also take care
1928     * to reset _gridCurJuke if necessary.
1929     * Note that for log axes, _gridInit() must be called before
1930     * calling _gridStep().
1931     * Based on stepGrid() from xgraph.c by David Harrison.
1932     */
1933    private double _gridStep(Vector grid, double pos, double step,
1934            boolean logflag) {
1935        if (logflag) {
1936            if (++_gridCurJuke >= grid.size()) {
1937                _gridCurJuke = 0;
1938                _gridBase+=Math.ceil(step);
1939                if (_debug == 5)
1940                    System.out.println("PlotBox: _gridStep: pos = "+pos+
1941                            " _roundUp("+step+") = "+_roundUp(step));
1942            }
1943            if (_gridCurJuke >= grid.size())
1944                return pos + step;
1945            if (_debug == 5)
1946                    System.out.println("PlotBox: _gridStep: pos="+pos+
1947                            " step="+step+" "+" _gridCurJuke="+
1948                            _gridCurJuke+" results="+_gridBase+"+"+
1949                            ((Double)grid.elementAt(_gridCurJuke)).doubleValue());
1950            return _gridBase +
1951                ((Double)grid.elementAt(_gridCurJuke)).doubleValue();
1952        } else {
1953            return pos + step;
1954        }
1955    }
1956
1957
1958    /*
1959     * Measure the various fonts. 
1960     */
1961    private void _measureFonts() {
1962        // We only measure the fonts once, and we do it from addNotify().
1963        if (_labelfont == null
1964            _labelfont = new Font("Helvetica", Font.PLAIN, 12);
1965        if (_superscriptfont == null
1966            _superscriptfont = new Font("Helvetica", Font.PLAIN, 9);
1967        if (_titlefont == null
1968            _titlefont = new Font("Helvetica", Font.BOLD, 14);
1969
1970        _labelFontMetrics = getFontMetrics(_labelfont);
1971        _superscriptFontMetrics = getFontMetrics(_superscriptfont);
1972        _titleFontMetrics = getFontMetrics(_titlefont);
1973    }
1974
1975    /*
1976     * Return the number of fractional digits required to display the
1977     * given number.  No number larger than 15 is returned (if
1978     * more than 15 digits are required, 15 is returned).
1979     */
1980    private int _numFracDigits (double num) {
1981        int numdigits = 0;
1982        while (numdigits <= 15 && num != Math.floor(num)) {
1983            num *= 10.0;
1984            numdigits += 1;
1985        }
1986        return numdigits;
1987    }
1988 
1989    /*
1990     * Return the number of integer digits required to display the
1991     * given number.  No number larger than 15 is returned (if
1992     * more than 15 digits are required, 15 is returned).
1993     */
1994    private int _numIntDigits (double num) {
1995        int numdigits = 0;
1996        while (numdigits <= 15 && (int)num != 0.0) {
1997            num /= 10.0;
1998            numdigits += 1;
1999        }
2000        return numdigits;
2001    }
2002
2003    /*
2004     * Parse a string of the form: "word num, word num, word num, ..."
2005     * where the word must be enclosed in quotes if it contains spaces,
2006     * and the number is interpreted as a floating point number.  Ignore
2007     * any incorrectly formatted fields.  I <i>xtick</i> is true, then
2008     * interpret the parsed string to specify the tick labels on the x axis.
2009     * Otherwise, do the y axis.
2010     */
2011    private void _parsePairs (String line, boolean xtick) {   
2012        int start = 0;
2013        boolean cont = true;
2014        while (cont) {
2015            int comma = line.indexOf(",", start);
2016            String pair = null ;
2017            if (comma > start) {
2018                pair = (line.substring(start,comma)).trim();
2019            } else {
2020                pair = (line.substring(start)).trim();
2021                cont = false;
2022            }
2023            int close = -1;
2024            int open = 0;
2025            if (pair.startsWith("\"")) {
2026                close = pair.indexOf("\"",1);
2027                open = 1;
2028            } else {
2029                close = pair.indexOf(" ");             
2030            }
2031            if (close > 0) {
2032                String label = pair.substring(open,close);
2033                String index = (pair.substring(close+1)).trim();
2034                try {
2035                    double idx = (Double.valueOf(index)).doubleValue();
2036                    if (xtick) addXTick(label, idx);
2037                    else addYTick(label,idx);
2038                } catch (NumberFormatException e) {
2039                    // ignore if format is bogus.
2040                }
2041            }
2042            start = comma + 1;
2043            comma = line.indexOf(",",start);
2044        }
2045    }
2046
2047    /*
2048     * Given a number, round up to the nearest power of ten
2049     * times 1, 2, or 5.
2050     *
2051     * Note: The argument must be strictly positive.
2052     */
2053    private double _roundUp(double val) {
2054        int exponent = (int) Math.floor(Math.log(val)*_LOG10SCALE);
2055        val *= Math.pow(10, -exponent);
2056        if (val > 5.0) val = 10.0;
2057        else if (val > 2.0) val = 5.0;
2058        else if (val > 1.0) val = 2.0;
2059        val *= Math.pow(10, exponent);
2060        return val;
2061    }
2062
2063    /*
2064     * Internal implementation of setXRange, so that it can be called when
2065     * autoranging.
2066     */
2067    private void _setXRange (double min, double max) {
2068        // If values are invalid, try for something reasonable.
2069        if (min > max) {
2070            min = -1.0;
2071            max = 1.0;
2072        } else if (min == max) {
2073            min -= 1.0;
2074            max += 1.0;
2075        }
2076        //if (_xRangeGiven) {
2077            // The user specified the range, so don't pad.
2078        //_xMin = min;
2079        //  _xMax = max;
2080        //} else {
2081            // Pad slightly so that we don't plot points on the axes.
2082            _xMin = min - ((max - min) * _PADDING);
2083            _xMax = max + ((max - min) * _PADDING);
2084            //}
2085
2086        // Find the exponent.
2087        double largest = Math.max(Math.abs(_xMin),Math.abs(_xMax));
2088        _xExp = (int) Math.floor(Math.log(largest)*_LOG10SCALE);
2089        // Use the exponent only if it's larger than 1 in magnitude.
2090        if (_xExp > 1 || _xExp < -1) {
2091            double xs = 1.0/Math.pow(10.0,(double)_xExp);
2092            _xtickMin = _xMin*xs;
2093            _xtickMax = _xMax*xs;
2094        } else {
2095            _xtickMin = _xMin;
2096            _xtickMax = _xMax;
2097            _xExp = 0;
2098        }
2099    }
2100
2101    /*
2102     * Internal implementation of setYRange, so that it can be called when
2103     * autoranging.
2104     */
2105    private void _setYRange (double min, double max) {
2106        // If values are invalid, try for something reasonable.
2107        if (min > max) {
2108            min = -1.0;
2109            max = 1.0;
2110        } else if (min == max) {
2111            min -= 0.1;
2112            max += 0.1;
2113        }
2114        //        if (_yRangeGiven) {
2115            // The user specified the range, so don't pad.
2116        //            _yMin = min;
2117        //            _yMax = max;
2118        //        } else {
2119            // Pad slightly so that we don't plot points on the axes.
2120            _yMin = min - ((max - min) * _PADDING);
2121            _yMax = max + ((max - min) * _PADDING);
2122            //        }
2123
2124        // Find the exponent.
2125        double largest = Math.max(Math.abs(_yMin),Math.abs(_yMax));
2126        _yExp = (int) Math.floor(Math.log(largest)*_LOG10SCALE);
2127        // Use the exponent only if it's larger than 1 in magnitude.
2128        if (_yExp > 1 || _yExp < -1) {
2129            double ys = 1.0/Math.pow(10.0,(double)_yExp);
2130            _ytickMin = _yMin*ys;
2131            _ytickMax = _yMax*ys;
2132        } else {
2133            _ytickMin = _yMin;
2134            _ytickMax = _yMax;
2135            _yExp = 0;
2136        }
2137    }
2138
2139    //////////////////////////////////////////////////////////////////////////
2140    ////                         private variables                        ////
2141
2142    // The URL to be opened.
2143    private String _dataurl = null;
2144
2145    // The document base we use to find the _dataurl.
2146    private URL _documentBase = null;
2147
2148    // Set to true if we are reading in pxgraph format binary data.
2149    private boolean _binary = false;
2150
2151    // The range of the plot as labeled (multiply by 10^exp for actual range.
2152    private double _ytickMax = 0.0, _ytickMin = 0.0,
2153        _xtickMax = 0.0 , _xtickMin = 0.0 ;
2154    // The power of ten by which the range numbers should be multiplied.
2155    private int _yExp = 0, _xExp = 0;
2156
2157    // Scaling used in making tick marks
2158    private double _ytickscale = 0.0, _xtickscale = 0.0;
2159
2160    // Font information.
2161    private Font _labelfont = null, _superscriptfont = null,
2162        _titlefont = null;
2163    private FontMetrics _labelFontMetrics = null,
2164        _superscriptFontMetrics = null,
2165        _titleFontMetrics = null;
2166
2167    // Used for log axes. Index into vector of axis labels.
2168    private int _gridCurJuke = 0;
2169
2170    // Used for log axes.  Base of the grid.
2171    private double _gridBase = 0.0;
2172
2173    // An array of strings for reporting errors.
2174    private String _errorMsg[];
2175   
2176    // The title and label strings.
2177    private String _xlabel, _ylabel, _title;
2178   
2179    // Legend information.
2180    private Vector _legendStrings = new Vector();
2181    private Vector _legendDatasets = new Vector();
2182   
2183    // If XTicks or YTicks are given
2184    private Vector _xticks = null, _xticklabels = null,
2185        _yticks = null, _yticklabels = null;
2186
2187    // A button for filling the plot
2188    private Button _fillButton = null;
2189   
2190    // Variables keeping track of the interactive zoom box.
2191    // Initialize to impossible values.
2192    private int _zoomx = -1;
2193    private int _zoomy = -1;
2194    private int _zoomxn = -1;
2195    private int _zoomyn = -1;
2196
2197    // Control whether we are zooming in or out.
2198    private boolean _zoomin = false;
2199    private boolean _zoomout = false;
2200    private boolean _drawn = false;
2201}
Note: See TracBrowser for help on using the browser.