source: src/main/java/weka/gui/visualize/Plot2D.java @ 28

Last change on this file since 28 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 45.1 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 *    Plot2D.java
19 *    Copyright (C) 2000 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.visualize;
24
25import weka.core.FastVector;
26import weka.core.Instance;
27import weka.core.Instances;
28import weka.core.Utils;
29
30import java.awt.BorderLayout;
31import java.awt.Color;
32import java.awt.Font;
33import java.awt.FontMetrics;
34import java.awt.Graphics;
35import java.awt.event.InputEvent;
36import java.awt.event.MouseAdapter;
37import java.awt.event.MouseEvent;
38import java.awt.event.WindowAdapter;
39import java.awt.event.WindowEvent;
40import java.util.Random;
41import java.util.Vector;
42
43import javax.swing.JFrame;
44import javax.swing.JPanel;
45
46/**
47 * This class plots datasets in two dimensions. It can also plot
48 * classifier errors and clusterer predictions.
49 *
50 * @author Mark Hall (mhall@cs.waikato.ac.nz)
51 * @version $Revision: 5987 $
52 */
53public class Plot2D
54  extends JPanel {
55
56  /** for serialization */
57  private static final long serialVersionUID = -1673162410856660442L; 
58
59  /* constants for shape types */
60  public static final int MAX_SHAPES = 5;
61  public static final int ERROR_SHAPE = 1000;
62  public static final int MISSING_SHAPE = 2000;
63  public static final int CONST_AUTOMATIC_SHAPE = -1;
64  public static final int X_SHAPE = 0;
65  public static final int PLUS_SHAPE = 1;
66  public static final int DIAMOND_SHAPE = 2;
67  public static final int TRIANGLEUP_SHAPE = 3;
68  public static final int TRIANGLEDOWN_SHAPE = 4;
69  public static final int DEFAULT_SHAPE_SIZE = 2;
70
71  /** Default colour for the axis */
72  protected Color m_axisColour = Color.green;
73
74  /** Default colour for the plot background */
75  protected Color m_backgroundColour = Color.black;
76
77  /** The plots to display */
78  protected FastVector m_plots = new FastVector();
79
80  /** The master plot */
81  protected PlotData2D m_masterPlot = null;
82
83  /** The name of the master plot */
84  protected String m_masterName = "master plot";
85
86  /** The instances to be plotted */
87  protected Instances m_plotInstances=null;
88
89  /** An optional "compainion" of the panel. If specified, this
90      class will get to do its thing with our graphics context
91      before we do any drawing. Eg. the visualize panel may need
92      to draw polygons etc. before we draw plot axis and data points */
93  protected Plot2DCompanion m_plotCompanion=null;
94
95  /** the class for displaying instance info. */
96  protected Class m_InstanceInfoFrameClass = null;
97 
98  /** For popping up text info on data points */
99  protected JFrame m_InstanceInfo = null;
100
101  /** The list of the colors used */
102  protected FastVector m_colorList;
103
104  /** default colours for colouring discrete class */
105  protected Color [] m_DefaultColors = {Color.blue,
106                                        Color.red,
107                                        Color.green,
108                                        Color.cyan,
109                                        Color.pink,
110                                        new Color(255, 0, 255),
111                                        Color.orange,
112                                        new Color(255, 0, 0),
113                                        new Color(0, 255, 0),
114                                        Color.white};
115
116  /** Indexes of the attributes to go on the x and y axis and the attribute
117      to use for colouring and the current shape for drawing */
118  protected int m_xIndex=0;
119  protected int m_yIndex=0;
120  protected int m_cIndex=0;
121  protected int m_sIndex=0;
122
123  /** Holds the min and max values of the x, y and colouring attributes
124   over all plots */
125  protected double m_maxX;
126  protected double m_minX;
127  protected double m_maxY;
128  protected double m_minY;
129  protected double m_maxC;
130  protected double m_minC;
131   
132  /** Axis padding */
133  protected final int m_axisPad = 5;
134
135  /** Tick size */
136  protected final int m_tickSize = 5;
137
138  /**the offsets of the axes once label metrics are calculated */
139  protected int m_XaxisStart=0;
140  protected int m_YaxisStart=0;
141  protected int m_XaxisEnd=0;
142  protected int m_YaxisEnd=0;
143
144  /** if the user resizes the window, or the attributes selected for
145      the attributes change, then the lookup table for points needs
146      to be recalculated */
147  protected boolean m_plotResize = true;
148 
149  /** if the user changes attribute assigned to an axis */
150  protected boolean m_axisChanged = false;
151
152  /** An array used to show if a point is hidden or not.
153   * This is used for speeding up the drawing of the plot panel
154   * although I am not sure how much performance this grants over
155   * not having it.
156   */
157  protected int[][] m_drawnPoints;
158
159  /** Font for labels */
160  protected Font m_labelFont;
161  protected FontMetrics m_labelMetrics=null; 
162
163  /** the level of jitter */
164  protected int m_JitterVal=0;
165
166  /** random values for perterbing the data points */
167  protected Random m_JRand = new Random(0);
168
169  /** lookup table for plotted points */
170  protected double [][] m_pointLookup=null;
171
172  /** Constructor */
173  public Plot2D() {
174    super();
175    setProperties();
176    this.setBackground(m_backgroundColour);
177
178    m_drawnPoints = new int[this.getWidth()][this.getHeight()];
179
180    /** Set up some default colours */
181    m_colorList = new FastVector(10);
182    for (int noa = m_colorList.size(); noa < 10; noa++) {
183      Color pc = m_DefaultColors[noa % 10];
184      int ija =  noa / 10;
185      ija *= 2; 
186      for (int j=0;j<ija;j++) {
187        pc = pc.darker();
188      }
189     
190      m_colorList.addElement(pc);
191    }
192  }
193
194  /**
195   * Set the properties for Plot2D
196   */
197  private void setProperties() {
198    if (VisualizeUtils.VISUALIZE_PROPERTIES != null) {
199      String thisClass = this.getClass().getName();
200      String axisKey = thisClass+".axisColour";
201      String backgroundKey = thisClass+".backgroundColour";
202
203      String axisColour = VisualizeUtils.VISUALIZE_PROPERTIES.
204        getProperty(axisKey);
205      if (axisColour == null) {
206        /*
207        System.err.println("Warning: no configuration property found in "
208                           +VisualizeUtils.PROPERTY_FILE
209                           +" for "+axisKey);*/
210      } else {
211        //System.err.println("Setting axis colour to: "+axisColour);
212        m_axisColour = VisualizeUtils.processColour(axisColour, m_axisColour);
213      }
214
215      String backgroundColour = 
216        VisualizeUtils.VISUALIZE_PROPERTIES.getProperty(backgroundKey);
217      if (backgroundColour == null) {
218        /*
219        System.err.println("Warning: no configuration property found in "
220                           +VisualizeUtils.PROPERTY_FILE
221                           +" for "+backgroundKey);*/
222      } else {
223        //System.err.println("Setting background colour to: "+backgroundColour);
224        m_backgroundColour = VisualizeUtils.processColour(backgroundColour, 
225                                                          m_backgroundColour);
226      }
227     
228      try {
229        m_InstanceInfoFrameClass = Class.forName(VisualizeUtils.VISUALIZE_PROPERTIES.getProperty(thisClass + ".instanceInfoFrame", "weka.gui.visualize.InstanceInfoFrame"));
230      }
231      catch (Exception e) {
232        e.printStackTrace();
233        m_InstanceInfoFrameClass = InstanceInfoFrame.class;
234      }
235    }
236  }
237
238  /**
239   * This will check the values of the screen points passed and make sure
240   * that they land on the screen
241   * @param x1 The x coord.
242   * @param y1 The y coord.
243   */
244  private boolean checkPoints(double x1, double y1) {
245    if (x1 < 0 || x1 > this.getSize().width || y1 < 0 
246        || y1 > this.getSize().height) {
247      return false;
248    }
249    return true;
250  }
251
252  /**
253   * Set a companion class. This is a class that might want
254   * to render something on the plot before we do our thing. Eg,
255   * Malcolm's shape drawing stuff needs to happen before we plot
256   * axis and points
257   * @param p a companion class
258   */
259  public void setPlotCompanion(Plot2DCompanion p) {
260    m_plotCompanion = p;
261  }
262
263  /**
264   * Set level of jitter and repaint the plot using the new jitter value
265   * @param j the level of jitter
266   */
267  public void setJitter(int j) {
268    if (m_plotInstances.numAttributes() > 0 
269        && m_plotInstances.numInstances() > 0) {
270      if (j >= 0) {
271        m_JitterVal = j;
272        m_JRand = new Random(m_JitterVal);
273        //      if (m_pointLookup != null) {
274        m_drawnPoints = new int[m_XaxisEnd - m_XaxisStart + 1]
275          [m_YaxisEnd - m_YaxisStart + 1];
276        updatePturb();
277        //      }
278        this.repaint();
279      }
280    }
281  }
282
283  /**
284   * Set a list of colours to use when colouring points according
285   * to class values or cluster numbers
286   * @param cols the list of colours to use
287   */
288  public void setColours (FastVector cols) {
289    m_colorList = cols;
290  }
291
292  /**
293   * Set the index of the attribute to go on the x axis
294   * @param x the index of the attribute to use on the x axis
295   */
296  public void setXindex(int x) {
297    m_xIndex = x;
298    for (int i=0;i<m_plots.size();i++) {
299      ((PlotData2D)m_plots.elementAt(i)).setXindex(m_xIndex);
300    }
301    determineBounds();
302    if (m_JitterVal != 0) {
303      updatePturb();
304    }
305    m_axisChanged = true;
306    this.repaint();
307  }
308   
309  /**
310   * Set the index of the attribute to go on the y axis
311   * @param y the index of the attribute to use on the y axis
312   */
313  public void setYindex(int y) {
314    m_yIndex = y;
315    for (int i=0;i<m_plots.size();i++) {
316      ((PlotData2D)m_plots.elementAt(i)).setYindex(m_yIndex);
317    }
318    determineBounds();
319    if (m_JitterVal != 0) {
320      updatePturb();
321    }
322    m_axisChanged = true;
323    this.repaint();
324  }
325
326  /**
327   * Set the index of the attribute to use for colouring
328   * @param c the index of the attribute to use for colouring
329   */
330  public void setCindex(int c) {
331    m_cIndex = c;
332    for (int i=0;i<m_plots.size();i++) {
333      ((PlotData2D)m_plots.elementAt(i)).setCindex(m_cIndex);
334    }
335    determineBounds();
336    m_axisChanged = true;
337    this.repaint();
338  }
339
340  /**
341   * Return the list of plots
342   * @return the list of plots
343   */
344  public FastVector getPlots() {
345    return m_plots;
346  }
347
348  /**
349   * Get the master plot
350   * @return the master plot
351   */
352  public PlotData2D getMasterPlot() {
353    return m_masterPlot;
354  }
355
356  /**
357   * Return the current max value of the attribute plotted on the x axis
358   * @return the max x value
359   */
360  public double getMaxX() {
361    return m_maxX;
362  }
363
364  /**
365   * Return the current max value of the attribute plotted on the y axis
366   * @return the max y value
367   */
368  public double getMaxY() {
369    return m_maxY;
370  }
371
372  /**
373   * Return the current min value of the attribute plotted on the x axis
374   * @return the min x value
375   */
376  public double getMinX() {
377    return m_minX;
378  }
379 
380  /**
381   * Return the current min value of the attribute plotted on the y axis
382   * @return the min y value
383   */
384  public double getMinY() {
385    return m_minY;
386  }
387
388  /**
389   * Return the current max value of the colouring attribute
390   * @return the max colour value
391   */
392  public double getMaxC() {
393    return m_maxC;
394  }
395 
396  /**
397   * Return the current min value of the colouring attribute
398   * @return the min colour value
399   */
400  public double getMinC() {
401    return m_minC;
402  }
403   
404  /**
405   * Sets the master plot from a set of instances
406   * @param inst the instances
407   * @exception Exception if instances could not be set
408   */
409  public void setInstances(Instances inst) throws Exception {
410    //System.err.println("Setting Instances");
411    PlotData2D tempPlot = new PlotData2D(inst);
412    tempPlot.setPlotName("master plot");
413    setMasterPlot(tempPlot);
414  }
415
416  /**
417   * Set the master plot.
418   * @param master the plot to make the master plot
419   * @exception Exception if the plot could not be set.
420   */
421  public void setMasterPlot(PlotData2D master) throws Exception {
422    if (master.m_plotInstances == null) {
423      throw new Exception("No instances in plot data!");
424    }
425    removeAllPlots();
426    m_masterPlot = master;
427    m_plots.addElement(m_masterPlot);
428    m_plotInstances = m_masterPlot.m_plotInstances;
429   
430    m_xIndex=0;
431    m_yIndex=0;
432    m_cIndex=0;
433   
434    determineBounds();
435  }
436
437  /**
438   * Clears all plots
439   */
440  public void removeAllPlots() {
441    m_masterPlot = null;
442    m_plotInstances = null;
443    m_plots = new FastVector();
444    m_xIndex = 0; m_yIndex = 0; m_cIndex = 0;
445  }
446
447  /**
448   * Add a plot to the list of plots to display
449   * @param newPlot the new plot to add
450   * @exception Exception if the plot could not be added
451   */
452  public void addPlot(PlotData2D newPlot) throws Exception {
453    if (newPlot.m_plotInstances == null) {
454      throw new Exception("No instances in plot data!");
455    }
456
457    if (m_masterPlot != null) {
458      if (m_masterPlot.m_plotInstances.
459          equalHeaders(newPlot.m_plotInstances) == false) {
460        throw new Exception("Plot2D :Plot data's instances are incompatable "
461                            +" with master plot");
462      }
463    } else {
464      m_masterPlot = newPlot;
465      m_plotInstances = m_masterPlot.m_plotInstances;
466    }
467    m_plots.addElement(newPlot);
468    setXindex(m_xIndex);
469    setYindex(m_yIndex);
470    setCindex(m_cIndex);
471  }
472
473  /**
474   * Set up fonts and font metrics
475   * @param gx the graphics context
476   */
477  private void setFonts(Graphics gx) {
478    if (m_labelMetrics == null) {
479      m_labelFont = new Font("Monospaced", Font.PLAIN, 12);
480      m_labelMetrics = gx.getFontMetrics(m_labelFont);
481    }
482    gx.setFont(m_labelFont);
483  }
484
485  /**
486   * Pops up a window displaying attribute information on any instances
487   * at a point+-plotting_point_size (in panel coordinates)
488   *
489   * @param x the x value of the clicked point
490   * @param y the y value of the clicked point
491   * @param newFrame true if instance info is to be displayed in a
492   * new frame.
493   */
494  public void searchPoints(int x, int y, final boolean newFrame) {
495    if (m_masterPlot.m_plotInstances != null) {
496      int longest=0;
497      for (int j=0;j<m_masterPlot.m_plotInstances.numAttributes();j++) {
498        if (m_masterPlot.m_plotInstances.attribute(j).name().length() > 
499            longest) {
500          longest = m_masterPlot.m_plotInstances.attribute(j).name().length();
501        }
502      }
503
504      StringBuffer insts = new StringBuffer(); 
505      Vector<Instances> data = new Vector<Instances>();
506      for (int jj=0;jj<m_plots.size();jj++) {
507        PlotData2D temp_plot = (PlotData2D)(m_plots.elementAt(jj));
508        data.add(new Instances(temp_plot.m_plotInstances, 0));
509       
510        for (int i=0;i<temp_plot.m_plotInstances.numInstances();i++) {
511          if (temp_plot.m_pointLookup[i][0] != Double.NEGATIVE_INFINITY) {
512            double px = temp_plot.m_pointLookup[i][0] + 
513              temp_plot.m_pointLookup[i][2];
514            double py = temp_plot.m_pointLookup[i][1] + 
515              temp_plot.m_pointLookup[i][3];
516            //      double size = temp_plot.m_pointLookup[i][2];
517            double size = temp_plot.m_shapeSize[i];
518            if ((x >= px-size) && (x <= px+size) &&
519                (y >= py-size) && (y <= py+size)) {
520              {
521                data.get(jj).add((Instance)temp_plot.m_plotInstances.instance(i).copy());
522                insts.append("\nPlot : "+temp_plot.m_plotName
523                             + "\nInstance: " + (i + 1 ) + "\n");
524                for (int j=0;j<temp_plot.m_plotInstances.numAttributes();j++) {
525                  for (int k = 0;k < 
526                         (longest-temp_plot.m_plotInstances.
527                          attribute(j).name().length()); k++) {
528                    insts.append(" ");
529                  }
530                  insts.append(temp_plot.m_plotInstances.attribute(j).name()); 
531                  insts.append(" : ");
532                 
533                  if (temp_plot.m_plotInstances.instance(i).isMissing(j)) {
534                    insts.append("Missing");
535                  } else if (temp_plot.m_plotInstances.attribute(j).
536                             isNominal()) {
537                    insts.append(temp_plot.m_plotInstances.
538                                 attribute(j).
539                                 value((int)temp_plot.m_plotInstances.
540                                       instance(i).value(j)));
541                  } else {
542                    insts.append(temp_plot.m_plotInstances.
543                                 instance(i).value(j));
544                  }
545                  insts.append("\n");
546                }
547              }
548            }
549          }
550        }
551      }
552      // remove datasets that contain no instances
553      int i = 0;
554      while (data.size() > i) {
555        if (data.get(i).numInstances() == 0)
556          data.remove(i);
557        else
558          i++;
559      }
560
561      if (insts.length() > 0) {
562        // Pop up a new frame
563        if (newFrame || m_InstanceInfo == null) {
564          try {
565            final JFrame jf = (JFrame) m_InstanceInfoFrameClass.newInstance();
566            ((InstanceInfo) jf).setInfoText(insts.toString());
567            ((InstanceInfo) jf).setInfoData(data);
568            final JFrame testf = m_InstanceInfo;
569            jf.addWindowListener(new WindowAdapter() {
570              public void windowClosing(WindowEvent e) {
571                if (!newFrame || testf == null) {
572                  m_InstanceInfo = null;
573                }
574                jf.dispose();
575              }
576            });
577            jf.setVisible(true);
578            if (m_InstanceInfo == null)
579              m_InstanceInfo = jf;
580          }
581          catch (Exception e) {
582            e.printStackTrace();
583          }
584        }
585        else {
586          // Overwrite info in existing frame     
587          ((InstanceInfo) m_InstanceInfo).setInfoText(insts.toString());
588          ((InstanceInfo) m_InstanceInfo).setInfoData(data);
589        }
590      }
591    }
592  }
593 
594
595  /**
596   * Determine the min and max values for axis and colouring attributes
597   */
598  public void determineBounds() {
599    double value,min,max;
600   
601    // find maximums minimums over all plots
602    m_minX = ((PlotData2D)m_plots.elementAt(0)).m_minX;
603    m_maxX = ((PlotData2D)m_plots.elementAt(0)).m_maxX;
604    m_minY = ((PlotData2D)m_plots.elementAt(0)).m_minY;
605    m_maxY = ((PlotData2D)m_plots.elementAt(0)).m_maxY;
606    m_minC = ((PlotData2D)m_plots.elementAt(0)).m_minC;
607    m_maxC = ((PlotData2D)m_plots.elementAt(0)).m_maxC;
608    for (int i=1;i<m_plots.size();i++) {
609      value = ((PlotData2D)m_plots.elementAt(i)).m_minX;
610      if (value < m_minX) {
611        m_minX = value;
612      }
613      value = ((PlotData2D)m_plots.elementAt(i)).m_maxX;
614      if (value > m_maxX) {
615        m_maxX = value;
616      }
617      value = ((PlotData2D)m_plots.elementAt(i)).m_minY;
618      if (value < m_minY) {
619        m_minY= value;
620      }
621      value = ((PlotData2D)m_plots.elementAt(i)).m_maxY;
622      if (value > m_maxY) {
623        m_maxY = value;
624      }
625      value = ((PlotData2D)m_plots.elementAt(i)).m_minC;
626      if (value < m_minC) {
627        m_minC = value;
628      }
629      value = ((PlotData2D)m_plots.elementAt(i)).m_maxC;
630      if (value > m_maxC) {
631        m_maxC = value;
632      }
633    }
634
635    fillLookup();
636    this.repaint();
637  }
638
639   
640  //to convert screen coords to attrib values
641  // note that I use a double to avoid accuracy
642  //headaches with ints
643  /**
644   * convert a Panel x coordinate to a raw x value.
645   * @param scx The Panel x coordinate
646   * @return A raw x value.
647   */
648  public double convertToAttribX(double scx) {
649    double temp = m_XaxisEnd - m_XaxisStart;
650    double temp2 = ((scx - m_XaxisStart) * (m_maxX - m_minX)) / temp;
651     
652    temp2 = temp2 + m_minX;
653     
654    return temp2;
655  }
656   
657  /**
658   * convert a Panel y coordinate to a raw y value.
659   * @param scy The Panel y coordinate
660   * @return A raw y value.
661   */
662  public double convertToAttribY(double scy) {
663    double temp = m_YaxisEnd - m_YaxisStart;
664    double temp2 = ((scy - m_YaxisEnd) * (m_maxY - m_minY)) / temp;
665     
666    temp2 = -(temp2 - m_minY);
667     
668    return temp2;
669  }
670  //////
671   
672  /**
673   * returns a value by which an x value can be peturbed. Makes sure
674   * that the x value+pturb stays within the plot bounds
675   * @param xvalP the x coordinate to be peturbed
676   * @param xj a random number to use in calculating a peturb value
677   * @return a peturb value
678   */
679  int pturbX(double xvalP, double xj) {
680    int xpturb = 0;
681    if (m_JitterVal > 0) {
682      xpturb = (int)((double)m_JitterVal * (xj / 2.0));
683      if (((xvalP + xpturb) < m_XaxisStart) || 
684          ((xvalP + xpturb) > m_XaxisEnd)) {
685        xpturb *= -1;
686      }
687    }
688    return xpturb;
689  }
690
691  /**
692   * Convert an raw x value to Panel x coordinate.
693   * @param xval the raw x value
694   * @return an x value for plotting in the panel.
695   */
696  public double convertToPanelX(double xval) {
697    double temp = (xval - m_minX)/(m_maxX - m_minX);
698    double temp2 = temp * (m_XaxisEnd - m_XaxisStart);
699     
700    temp2 = temp2 + m_XaxisStart;
701       
702    return temp2;
703  }
704
705  /**
706   * returns a value by which a y value can be peturbed. Makes sure
707   * that the y value+pturb stays within the plot bounds
708   * @param yvalP the y coordinate to be peturbed
709   * @param yj a random number to use in calculating a peturb value
710   * @return a peturb value
711   */
712  int pturbY(double yvalP, double yj) {
713    int ypturb = 0;
714    if (m_JitterVal > 0) {
715      ypturb = (int)((double)m_JitterVal * (yj / 2.0));
716      if (((yvalP + ypturb) < m_YaxisStart) || 
717          ((yvalP + ypturb) > m_YaxisEnd)) {
718        ypturb *= -1;
719      }
720    }
721    return ypturb;
722  }
723
724  /**
725   * Convert an raw y value to Panel y coordinate.
726   * @param yval the raw y value
727   * @return an y value for plotting in the panel.
728   */
729  public double convertToPanelY(double yval) {
730    double temp = (yval - m_minY)/(m_maxY - m_minY);
731    double temp2 = temp * (m_YaxisEnd - m_YaxisStart);
732     
733    temp2 = m_YaxisEnd - temp2;
734
735    return temp2;
736  }
737
738  /**
739   * Draws an X.
740   * @param gx the graphics context
741   * @param x the x coord
742   * @param y the y coord
743   * @param size the size of the shape
744   */
745  private static void drawX(Graphics gx, double x, double y, int size) {
746     gx.drawLine((int)(x-size),(int)(y-size),
747                  (int)(x+size),(int)(y+size));
748     
749      gx.drawLine((int)(x+size),(int)(y-size),
750                  (int)(x-size),(int)(y+size));     
751  }
752
753  /**
754   * Draws a plus.
755   * @param gx the graphics context
756   * @param x the x coord
757   * @param y the y coord
758   * @param size the size of the shape
759   */
760  private static void drawPlus(Graphics gx, double x, double y, int size) {
761     gx.drawLine((int)(x-size),(int)(y),
762                  (int)(x+size),(int)(y));
763     
764      gx.drawLine((int)(x),(int)(y-size),
765                  (int)(x),(int)(y+size));     
766  }
767
768  /**
769   * Draws a diamond.
770   * @param gx the graphics context
771   * @param x the x coord
772   * @param y the y coord
773   * @param size the size of the shape
774   */
775  private static void drawDiamond(Graphics gx, double x, double y, int size) {
776    gx.drawLine((int)(x-size),(int)(y),
777                (int)(x),(int)(y-size));
778   
779    gx.drawLine((int)(x),(int)(y-size),
780                  (int)(x+size),(int)(y));
781
782    gx.drawLine((int)(x+size),(int)(y),
783                  (int)(x),(int)(y+size));
784
785     gx.drawLine((int)(x),(int)(y+size),
786                  (int)(x-size),(int)(y));
787  }
788
789  /**
790   * Draws an triangle (point at top).
791   * @param gx the graphics context
792   * @param x the x coord
793   * @param y the y coord
794   * @param size the size of the shape
795   */
796  private static void drawTriangleUp(Graphics gx, double x, 
797                                     double y, int size) {
798    gx.drawLine((int)(x),(int)(y-size),
799                (int)(x-size),(int)(y+size));
800
801    gx.drawLine((int)(x-size),(int)(y+size),
802                (int)(x+size),(int)(y+size));
803
804    gx.drawLine((int)(x+size),(int)(y+size),
805                (int)(x),(int)(y-size));
806
807  }
808
809  /**
810   * Draws an triangle (point at bottom).
811   * @param gx the graphics context
812   * @param x the x coord
813   * @param y the y coord
814   * @param size the size of the shape
815   */
816  private static void drawTriangleDown(Graphics gx, double x, 
817                                       double y, int size) {
818    gx.drawLine((int)(x),(int)(y+size),
819                (int)(x-size),(int)(y-size));
820
821    gx.drawLine((int)(x-size),(int)(y-size),
822                (int)(x+size),(int)(y-size));
823
824    gx.drawLine((int)(x+size),(int)(y-size),
825                (int)(x),(int)(y+size));
826
827  }
828
829  /**
830   * Draws a data point at a given set of panel coordinates at a given
831   * size and connects a line to the previous point.
832   * @param x the x coord
833   * @param y the y coord
834   * @param xprev the x coord of the previous point
835   * @param yprev the y coord of the previous point
836   * @param size the size of the point
837   * @param shape the shape of the data point (square is reserved for nominal
838   * error data points). Shapes: 0=x, 1=plus, 2=diamond, 3=triangle(up),
839   * 4 = triangle (down).
840   * @param gx the graphics context
841   */
842  protected static void drawDataPoint(double x, 
843                               double y,
844                               double xprev,
845                               double yprev,
846                               int size,
847                               int shape,
848                               Graphics gx) {
849
850    drawDataPoint(x,y,size,shape,gx);
851
852    // connect a line to the previous point
853    gx.drawLine((int)x, (int)y, (int)xprev, (int)yprev);
854  }
855
856  /**
857   * Draws a data point at a given set of panel coordinates at a given
858   * size.
859   * @param x the x coord
860   * @param y the y coord
861   * @param size the size of the point
862   * @param shape the shape of the data point (square is reserved for nominal
863   * error data points). Shapes: 0=x, 1=plus, 2=diamond, 3=triangle(up),
864   * 4 = triangle (down).
865   * @param gx the graphics context
866   */
867  protected static void drawDataPoint(double x, 
868                                      double y,
869                                      int size,
870                                      int shape,
871                                      Graphics gx) {
872
873    Font lf = new Font("Monospaced", Font.PLAIN, 12);
874    FontMetrics fm = gx.getFontMetrics(lf);
875
876    if (size == 0) {
877      size = 1;
878    }
879
880    if (shape != ERROR_SHAPE && shape != MISSING_SHAPE) {
881      shape = shape % 5;
882    }
883
884    switch (shape) {
885    case X_SHAPE:
886      drawX(gx, x, y, size);
887      break;     
888    case PLUS_SHAPE:
889      drawPlus(gx, x, y, size);
890      break;
891    case DIAMOND_SHAPE:
892      drawDiamond(gx, x, y, size);
893      break;
894    case TRIANGLEUP_SHAPE:
895      drawTriangleUp(gx, x, y, size);
896      break;
897    case TRIANGLEDOWN_SHAPE:
898      drawTriangleDown(gx, x, y, size);
899      break;
900    case ERROR_SHAPE: // draws the nominal error shape
901      gx.drawRect((int)(x-size),(int)(y-size),(size*2),(size*2));
902      break;
903    case MISSING_SHAPE:
904      int hf = fm.getAscent();
905      int width = fm.stringWidth("M");
906      gx.drawString("M",(int)(x-(width / 2)), (int)(y+(hf / 2)));
907      break;
908    }
909  }
910
911  /**
912   * Updates the perturbed values for the plots when the jitter value is
913   * changed
914   */
915  private void updatePturb() {
916    double xj=0;
917    double yj=0;
918    for (int j=0;j<m_plots.size();j++) {
919      PlotData2D temp_plot = (PlotData2D)(m_plots.elementAt(j));
920      for (int i=0;i<temp_plot.m_plotInstances.numInstances();i++) {
921        if (temp_plot.m_plotInstances.instance(i).isMissing(m_xIndex) ||
922            temp_plot.m_plotInstances.instance(i).isMissing(m_yIndex)) {
923        } else {
924          if (m_JitterVal > 0) {
925            xj = m_JRand.nextGaussian();
926            yj = m_JRand.nextGaussian();
927          }
928          temp_plot.m_pointLookup[i][2] = 
929            pturbX(temp_plot.m_pointLookup[i][0],xj);
930          temp_plot.m_pointLookup[i][3] = 
931            pturbY(temp_plot.m_pointLookup[i][1],yj);
932        }
933      }
934    }
935  }
936
937  /**
938   * Fills the lookup caches for the plots. Also calculates errors for
939   * numeric predictions (if any) in plots
940   */
941  private void fillLookup() {
942
943    for (int j=0;j<m_plots.size();j++) {
944      PlotData2D temp_plot = (PlotData2D)(m_plots.elementAt(j));
945
946      if (temp_plot.m_plotInstances.numInstances() > 0 &&
947          temp_plot.m_plotInstances.numAttributes() > 0) {
948        for (int i=0;i<temp_plot.m_plotInstances.numInstances();i++) {
949          if (temp_plot.m_plotInstances.instance(i).isMissing(m_xIndex) ||
950              temp_plot.m_plotInstances.instance(i).isMissing(m_yIndex)) {
951            temp_plot.m_pointLookup[i][0] = Double.NEGATIVE_INFINITY;
952            temp_plot.m_pointLookup[i][1] = Double.NEGATIVE_INFINITY;
953          } else {
954            double x = convertToPanelX(temp_plot.m_plotInstances.
955                                       instance(i).value(m_xIndex));
956            double y = convertToPanelY(temp_plot.m_plotInstances.
957                                       instance(i).value(m_yIndex));
958            temp_plot.m_pointLookup[i][0] = x;
959            temp_plot.m_pointLookup[i][1] = y;
960          }
961        }
962      }
963    }
964  }
965   
966  /**
967   * Draws the data points and predictions (if provided).
968   * @param gx the graphics context
969   */
970  private void paintData(Graphics gx) {
971
972    for (int j=0;j<m_plots.size();j++) {
973      PlotData2D temp_plot = (PlotData2D)(m_plots.elementAt(j));
974
975      for (int i=0;i<temp_plot.m_plotInstances.numInstances();i++) {
976        if (temp_plot.m_plotInstances.instance(i).isMissing(m_xIndex) ||
977            temp_plot.m_plotInstances.instance(i).isMissing(m_yIndex)) {
978        } else {
979          double x = (temp_plot.m_pointLookup[i][0] + 
980                      temp_plot.m_pointLookup[i][2]);
981          double y = (temp_plot.m_pointLookup[i][1] + 
982                      temp_plot.m_pointLookup[i][3]);
983
984          double prevx = 0;
985          double prevy = 0;
986          if (i > 0) {
987            prevx = (temp_plot.m_pointLookup[i - 1][0] + 
988                        temp_plot.m_pointLookup[i - 1][2]);
989            prevy = (temp_plot.m_pointLookup[i - 1][1] + 
990                        temp_plot.m_pointLookup[i - 1][3]);
991          }
992
993          int x_range = (int)x - m_XaxisStart;
994          int y_range = (int)y - m_YaxisStart;
995
996          if (x_range >= 0 && y_range >= 0) {
997            if (m_drawnPoints[x_range][y_range] == i
998                || m_drawnPoints[x_range][y_range] == 0
999                || temp_plot.m_shapeSize[i] == temp_plot.m_alwaysDisplayPointsOfThisSize 
1000                || temp_plot.m_displayAllPoints == true) {
1001              m_drawnPoints[x_range][y_range] = i;
1002              if (temp_plot.m_plotInstances.attribute(m_cIndex).isNominal()) {
1003                if (temp_plot.m_plotInstances.attribute(m_cIndex).numValues() >
1004                    m_colorList.size() && 
1005                    !temp_plot.m_useCustomColour) {
1006                  extendColourMap(temp_plot.m_plotInstances.
1007                                  attribute(m_cIndex).numValues());
1008                }
1009
1010                Color ci;
1011                if (temp_plot.m_plotInstances.instance(i).
1012                    isMissing(m_cIndex)) {
1013                  ci = Color.gray;
1014                } else {
1015                  int ind = (int)temp_plot.m_plotInstances.instance(i).
1016                    value(m_cIndex);
1017                  ci = (Color)m_colorList.elementAt(ind);
1018                }
1019
1020                if (!temp_plot.m_useCustomColour) {
1021                  gx.setColor(ci);         
1022                } else {
1023                  gx.setColor(temp_plot.m_customColour);
1024                }
1025
1026                if (temp_plot.m_plotInstances.instance(i).
1027                    isMissing(m_cIndex)) {
1028                  if (temp_plot.m_connectPoints[i] == true) {
1029                    drawDataPoint(x,y,prevx,prevy,temp_plot.m_shapeSize[i],
1030                                  MISSING_SHAPE,gx);
1031                  } else {
1032                    drawDataPoint(x,y,temp_plot.m_shapeSize[i],
1033                                  MISSING_SHAPE,gx);
1034                  }
1035                } else {
1036                  if (temp_plot.m_shapeType[i] == CONST_AUTOMATIC_SHAPE) {
1037                    if (temp_plot.m_connectPoints[i] == true) {
1038                      drawDataPoint(x,y,prevx,prevy,
1039                                    temp_plot.m_shapeSize[i],j,gx);
1040                    } else {
1041                      drawDataPoint(x,y,temp_plot.m_shapeSize[i],j,gx);
1042                    }
1043                  } else {
1044                    if (temp_plot.m_connectPoints[i] == true) {
1045                       drawDataPoint(x,y,prevx,prevy,temp_plot.m_shapeSize[i],
1046                                     temp_plot.m_shapeType[i],gx);
1047                    } else {
1048                      drawDataPoint(x,y,temp_plot.m_shapeSize[i],
1049                                    temp_plot.m_shapeType[i],gx);
1050                    }
1051                  }
1052                }
1053              } else {
1054                double r;
1055                Color ci = null;
1056                if (!temp_plot.m_plotInstances.instance(i).
1057                    isMissing(m_cIndex)) {
1058                  r = (temp_plot.m_plotInstances.instance(i).
1059                       value(m_cIndex) - m_minC) / (m_maxC - m_minC);
1060                  r = (r * 240) + 15;
1061                  ci = new Color((int)r,150,(int)(255-r));
1062                } else {
1063                  ci = Color.gray;
1064                }
1065                if (!temp_plot.m_useCustomColour) {
1066                  gx.setColor(ci);
1067                } else {
1068                  gx.setColor(temp_plot.m_customColour);
1069                }
1070                if (temp_plot.m_plotInstances.instance(i).
1071                    isMissing(m_cIndex)) {
1072                  if (temp_plot.m_connectPoints[i] == true) {
1073                    drawDataPoint(x,y,prevx,prevy,temp_plot.m_shapeSize[i],
1074                                  MISSING_SHAPE,gx);
1075                  } else {
1076                    drawDataPoint(x,y,temp_plot.m_shapeSize[i],
1077                                  MISSING_SHAPE,gx);
1078                  }
1079                } else {
1080                  if (temp_plot.m_shapeType[i] == CONST_AUTOMATIC_SHAPE) {
1081                    if (temp_plot.m_connectPoints[i] == true) {
1082                      drawDataPoint(x,y,prevx,prevy,
1083                                    temp_plot.m_shapeSize[i],j,gx);
1084                    } else {
1085                      drawDataPoint(x,y,temp_plot.m_shapeSize[i],j,gx);
1086                    }
1087                  } else {
1088                    if (temp_plot.m_connectPoints[i] == true) {
1089                      drawDataPoint(x,y,prevx,prevy,temp_plot.m_shapeSize[i],
1090                                    temp_plot.m_shapeType[i],gx);
1091                    } else {
1092                      drawDataPoint(x,y,temp_plot.m_shapeSize[i],
1093                                    temp_plot.m_shapeType[i],gx);
1094                    }
1095                  }
1096                }
1097              }
1098            }
1099          }
1100        }
1101      }
1102    }
1103  }
1104
1105  /*
1106  public void determineAxisPositions(Graphics gx) {
1107    setFonts(gx);
1108    int mxs = m_XaxisStart;
1109    int mxe = m_XaxisEnd;
1110    int mys = m_YaxisStart;
1111    int mye = m_YaxisEnd;
1112    m_axisChanged = false;
1113
1114    int h = this.getHeight();
1115    int w = this.getWidth();
1116    int hf = m_labelMetrics.getAscent();
1117    int mswx=0;
1118    int mswy=0;
1119
1120    //      determineBounds();
1121    int fieldWidthX = (int)((Math.log(m_maxX)/Math.log(10)))+1;
1122    int precisionX = 1;
1123    if ((Math.abs(m_maxX-m_minX) < 1) && ((m_maxY-m_minX) != 0)) {
1124      precisionX = (int)Math.abs(((Math.log(Math.abs(m_maxX-m_minX)) /
1125                                   Math.log(10))))+1;
1126    }
1127    String maxStringX = Utils.doubleToString(m_maxX,
1128                                             fieldWidthX+1+precisionX
1129                                             ,precisionX);
1130    mswx = m_labelMetrics.stringWidth(maxStringX);
1131    int fieldWidthY = (int)((Math.log(m_maxY)/Math.log(10)))+1;
1132    int precisionY = 1;
1133    if (Math.abs((m_maxY-m_minY)) < 1 && ((m_maxY-m_minY) != 0)) {
1134      precisionY = (int)Math.abs(((Math.log(Math.abs(m_maxY-m_minY)) /
1135                                   Math.log(10))))+1;
1136    }
1137    String maxStringY = Utils.doubleToString(m_maxY,
1138                                             fieldWidthY+1+precisionY
1139                                             ,precisionY);
1140    String minStringY = Utils.doubleToString(m_minY,
1141                                             fieldWidthY+1+precisionY
1142                                             ,precisionY);
1143
1144    if (m_plotInstances.attribute(m_yIndex).isNumeric()) {
1145      mswy = (m_labelMetrics.stringWidth(maxStringY) >
1146              m_labelMetrics.stringWidth(minStringY))
1147        ? m_labelMetrics.stringWidth(maxStringY)
1148        : m_labelMetrics.stringWidth(minStringY);
1149    } else {
1150      mswy = m_labelMetrics.stringWidth("MM");
1151    }
1152
1153    m_YaxisStart = m_axisPad;
1154    m_XaxisStart = 0+m_axisPad+m_tickSize+mswy;
1155
1156    m_XaxisEnd = w-m_axisPad-(mswx/2);
1157     
1158    m_YaxisEnd = h-m_axisPad-(2 * hf)-m_tickSize;
1159    } */
1160
1161  /**
1162   * Draws the axis and a spectrum if the colouring attribute is numeric
1163   * @param gx the graphics context
1164   */
1165  private void paintAxis(Graphics gx) {
1166    setFonts(gx);
1167    int mxs = m_XaxisStart;
1168    int mxe = m_XaxisEnd;
1169    int mys = m_YaxisStart;
1170    int mye = m_YaxisEnd;
1171    m_plotResize = false;
1172
1173    int h = this.getHeight();
1174    int w = this.getWidth();
1175    int hf = m_labelMetrics.getAscent();
1176    int mswx=0;
1177    int mswy=0;
1178
1179    //      determineBounds();
1180    int precisionXmax = 1;
1181    int precisionXmin = 1;
1182    int precisionXmid = 1;
1183    /*if ((Math.abs(m_maxX-m_minX) < 1) && ((m_maxY-m_minX) != 0)) {
1184      precisionX = (int)Math.abs(((Math.log(Math.abs(m_maxX-m_minX)) /
1185                                   Math.log(10))))+1;
1186                                   } */
1187    int whole = (int)Math.abs(m_maxX);
1188    double decimal = Math.abs(m_maxX) - whole;
1189    int nondecimal;
1190    nondecimal = (whole > 0) 
1191      ? (int)(Math.log(whole) / Math.log(10))
1192      : 1;
1193   
1194    precisionXmax = (decimal > 0) 
1195      ? (int)Math.abs(((Math.log(Math.abs(m_maxX)) / 
1196                                      Math.log(10))))+2
1197      : 1;
1198    if (precisionXmax > VisualizeUtils.MAX_PRECISION) {
1199      precisionXmax = 1;
1200    }
1201
1202    String maxStringX = Utils.doubleToString(m_maxX,
1203                                             nondecimal+1+precisionXmax
1204                                             ,precisionXmax);
1205
1206    whole = (int)Math.abs(m_minX);
1207    decimal = Math.abs(m_minX) - whole;
1208    nondecimal = (whole > 0) 
1209      ? (int)(Math.log(whole) / Math.log(10))
1210      : 1;
1211    precisionXmin = (decimal > 0) 
1212      ? (int)Math.abs(((Math.log(Math.abs(m_minX)) / 
1213                                      Math.log(10))))+2
1214      : 1;
1215    if (precisionXmin > VisualizeUtils.MAX_PRECISION) {
1216      precisionXmin = 1;
1217    }
1218   
1219    String minStringX = Utils.doubleToString(m_minX,
1220                                             nondecimal+1+precisionXmin,
1221                                             precisionXmin);
1222
1223    mswx = m_labelMetrics.stringWidth(maxStringX);
1224
1225    int precisionYmax = 1;
1226    int precisionYmin = 1;
1227    int precisionYmid = 1;
1228    whole = (int)Math.abs(m_maxY);
1229    decimal = Math.abs(m_maxY) - whole;
1230    nondecimal = (whole > 0) 
1231      ? (int)(Math.log(whole) / Math.log(10))
1232      : 1;
1233    precisionYmax = (decimal > 0) 
1234      ? (int)Math.abs(((Math.log(Math.abs(m_maxY)) / 
1235                                      Math.log(10))))+2
1236      : 1;
1237    if (precisionYmax > VisualizeUtils.MAX_PRECISION) {
1238      precisionYmax = 1;
1239    }
1240   
1241    String maxStringY = Utils.doubleToString(m_maxY,
1242                                             nondecimal+1+precisionYmax
1243                                             ,precisionYmax);
1244
1245
1246    whole = (int)Math.abs(m_minY);
1247    decimal = Math.abs(m_minY) - whole;
1248    nondecimal = (whole > 0) 
1249      ? (int)(Math.log(whole) / Math.log(10))
1250      : 1;
1251    precisionYmin = (decimal > 0) 
1252      ? (int)Math.abs(((Math.log(Math.abs(m_minY)) / 
1253                                      Math.log(10))))+2
1254      : 1;
1255    if (precisionYmin > VisualizeUtils.MAX_PRECISION) {
1256      precisionYmin = 1;
1257    }
1258   
1259    String minStringY = Utils.doubleToString(m_minY,
1260                                             nondecimal+1+precisionYmin
1261                                             ,precisionYmin);
1262
1263    if (m_plotInstances.attribute(m_yIndex).isNumeric()) {
1264      mswy = (m_labelMetrics.stringWidth(maxStringY) > 
1265              m_labelMetrics.stringWidth(minStringY))
1266        ? m_labelMetrics.stringWidth(maxStringY)
1267        : m_labelMetrics.stringWidth(minStringY);
1268      mswy += m_labelMetrics.stringWidth("M");
1269    } else {
1270      mswy = m_labelMetrics.stringWidth("MM");
1271    }
1272
1273    m_YaxisStart = m_axisPad;
1274    m_XaxisStart = 0+m_axisPad+m_tickSize+mswy;
1275
1276    m_XaxisEnd = w-m_axisPad-(mswx/2);
1277     
1278    m_YaxisEnd = h-m_axisPad-(2 * hf)-m_tickSize;
1279
1280    // draw axis
1281    gx.setColor(m_axisColour);
1282    if (m_plotInstances.attribute(m_xIndex).isNumeric()) {
1283      if (w > (2 * mswx)) {
1284       
1285        gx.drawString(maxStringX, 
1286                      m_XaxisEnd-(mswx/2),
1287                      m_YaxisEnd+hf+m_tickSize);
1288       
1289        mswx = m_labelMetrics.stringWidth(minStringX);
1290        gx.drawString(minStringX,
1291                      (m_XaxisStart-(mswx/2)),
1292                      m_YaxisEnd+hf+m_tickSize);
1293
1294        // draw the middle value
1295        if (w > (3 * mswx) && 
1296            (m_plotInstances.attribute(m_xIndex).isNumeric())) {
1297          double mid = m_minX+((m_maxX-m_minX)/2.0);
1298           whole = (int)Math.abs(mid);
1299           decimal = Math.abs(mid) - whole;
1300           nondecimal = (whole > 0) 
1301             ? (int)(Math.log(whole) / Math.log(10))
1302             : 1;
1303           precisionXmid = (decimal > 0) 
1304             ? (int)Math.abs(((Math.log(Math.abs(mid)) / 
1305                               Math.log(10))))+2
1306             : 1;
1307           if (precisionXmid > VisualizeUtils.MAX_PRECISION) {
1308             precisionXmid = 1;
1309           }
1310         
1311          String maxString = Utils.doubleToString(mid,
1312                                                  nondecimal+1+precisionXmid,
1313                                                  precisionXmid);
1314          int sw = m_labelMetrics.stringWidth(maxString);
1315          double mx = m_XaxisStart+((double)(m_XaxisEnd-m_XaxisStart)/2.0);
1316          gx.drawString(maxString,
1317                        (int)(mx-(((double)sw)/2.0)),
1318                        m_YaxisEnd+hf+m_tickSize);
1319          gx.drawLine((int)mx,m_YaxisEnd,(int)mx,m_YaxisEnd+m_tickSize);
1320        }
1321      }
1322    } else {
1323      int numValues = m_plotInstances.attribute(m_xIndex).numValues();
1324      int div = (numValues % 2 > 0) ? (numValues/2)+1 : (numValues/2);
1325      int maxXStringWidth = (m_XaxisEnd - m_XaxisStart) / numValues;
1326
1327      for (int i=0;i<numValues;i++) {
1328        String val = m_plotInstances.attribute(m_xIndex).value(i);
1329        int sw = m_labelMetrics.stringWidth(val);
1330        int rm;
1331        // truncate string if necessary
1332        if (sw > maxXStringWidth) {
1333          int incr = (sw / val.length());
1334          rm = (sw - maxXStringWidth) / incr;
1335          if (rm == 0) {
1336            rm = 1;
1337          }
1338          val = val.substring(0,val.length()-rm);
1339          sw = m_labelMetrics.stringWidth(val);
1340        }
1341        if (i == 0) {
1342          gx.drawString(val,(int)convertToPanelX(i),
1343                        m_YaxisEnd+hf+m_tickSize);
1344        } else if (i == numValues -1) {
1345          if ((i % 2) == 0) {
1346            gx.drawString(val,
1347                          m_XaxisEnd-sw,
1348                          m_YaxisEnd+hf+m_tickSize);
1349          } else {
1350            gx.drawString(val,
1351                          m_XaxisEnd-sw,
1352                          m_YaxisEnd+(2*hf)+m_tickSize);
1353          }
1354        } else {
1355          if ((i % 2) == 0) {
1356            gx.drawString(val,
1357                          (int)convertToPanelX(i)-(sw/2),
1358                          m_YaxisEnd+hf+m_tickSize);
1359          } else {
1360            gx.drawString(val,
1361                          (int)convertToPanelX(i)-(sw/2),
1362                          m_YaxisEnd+(2*hf)+m_tickSize);
1363          }
1364        }
1365        gx.drawLine((int)convertToPanelX(i),
1366                    m_YaxisEnd,
1367                    (int)convertToPanelX(i),
1368                    m_YaxisEnd+m_tickSize);
1369      }
1370       
1371    }
1372
1373    // draw the y axis
1374    if (m_plotInstances.attribute(m_yIndex).isNumeric()) {
1375      if (h > (2 * hf)) {
1376        gx.drawString(maxStringY, 
1377                      m_XaxisStart-mswy-m_tickSize,
1378                      m_YaxisStart+(hf));
1379
1380        gx.drawString(minStringY,
1381                      (m_XaxisStart-mswy-m_tickSize),
1382                      m_YaxisEnd);
1383
1384        // draw the middle value
1385        if (w > (3 * hf) && 
1386            (m_plotInstances.attribute(m_yIndex).isNumeric())) {
1387          double mid = m_minY+((m_maxY-m_minY)/2.0);
1388          whole = (int)Math.abs(mid);
1389          decimal = Math.abs(mid) - whole;
1390          nondecimal = (whole > 0) 
1391            ? (int)(Math.log(whole) / Math.log(10))
1392            : 1;
1393          precisionYmid = (decimal > 0) 
1394            ? (int)Math.abs(((Math.log(Math.abs(mid)) / 
1395                              Math.log(10))))+2
1396            : 1;
1397          if (precisionYmid > VisualizeUtils.MAX_PRECISION) {
1398            precisionYmid = 1;
1399          }
1400         
1401          String maxString = Utils.doubleToString(mid,
1402                                                  nondecimal+1+precisionYmid,
1403                                                  precisionYmid);
1404          int sw = m_labelMetrics.stringWidth(maxString);
1405          double mx = m_YaxisStart+((double)(m_YaxisEnd-m_YaxisStart)/2.0);
1406          gx.drawString(maxString,
1407                        m_XaxisStart-sw-m_tickSize-1,
1408                        (int)(mx+(((double)hf)/2.0)));
1409          gx.drawLine(m_XaxisStart-m_tickSize,(int)mx,m_XaxisStart,(int)mx);
1410        }
1411      }
1412    } else {
1413      int numValues = m_plotInstances.attribute(m_yIndex).numValues();
1414      int div = ((numValues % 2) == 0) ? (numValues/2) : (numValues/2+1);
1415      int maxYStringHeight = (m_YaxisEnd - m_XaxisStart) / div;
1416      int sw = m_labelMetrics.stringWidth("M");
1417      for (int i=0;i<numValues;i++) {
1418        // can we at least print 2 characters
1419        if (maxYStringHeight >= (2*hf)) {
1420          String val = m_plotInstances.attribute(m_yIndex).value(i);
1421          int numPrint = ((maxYStringHeight/hf) > val.length()) ?
1422            val.length() :
1423            (maxYStringHeight/hf);
1424           
1425          for (int j=0;j<numPrint;j++) {
1426            String ll = val.substring(j,j+1);
1427            if (val.charAt(j) == '_' || val.charAt(j) == '-') {
1428              ll = "|";
1429            }
1430            if (i == 0) {
1431              gx.drawString(ll,m_XaxisStart-sw-m_tickSize-1,
1432                            (int)convertToPanelY(i)
1433                            -((numPrint-1)*hf)
1434                            +(j*hf)
1435                            +(hf/2));
1436            } else if (i == (numValues-1)) {
1437              if ((i % 2) == 0) {
1438                gx.drawString(ll,m_XaxisStart-sw-m_tickSize-1,
1439                              (int)convertToPanelY(i)
1440                              +(j*hf)
1441                              +(hf/2));
1442              } else {
1443                gx.drawString(ll,m_XaxisStart-(2*sw)-m_tickSize-1,
1444                              (int)convertToPanelY(i)
1445                              +(j*hf)
1446                              +(hf/2));
1447              }
1448            } else {
1449              if ((i % 2) == 0) {
1450                gx.drawString(ll,m_XaxisStart-sw-m_tickSize-1,
1451                              (int)convertToPanelY(i)
1452                              -(((numPrint-1)*hf)/2)
1453                              +(j*hf)
1454                              +(hf/2));
1455              } else {
1456                gx.drawString(ll,m_XaxisStart-(2*sw)-m_tickSize-1,
1457                              (int)convertToPanelY(i)
1458                              -(((numPrint-1)*hf)/2)
1459                              +(j*hf)
1460                              +(hf/2));
1461              }
1462            }
1463          }
1464        }
1465        gx.drawLine(m_XaxisStart-m_tickSize,
1466                    (int)convertToPanelY(i),
1467                    m_XaxisStart,
1468                    (int)convertToPanelY(i));
1469      }
1470    }
1471
1472    gx.drawLine(m_XaxisStart,
1473                m_YaxisStart,
1474                m_XaxisStart,
1475                m_YaxisEnd);
1476    gx.drawLine(m_XaxisStart,
1477                m_YaxisEnd,
1478                m_XaxisEnd,
1479                m_YaxisEnd);
1480
1481    if (m_XaxisStart != mxs || m_XaxisEnd != mxe ||
1482        m_YaxisStart != mys || m_YaxisEnd != mye) {
1483      m_plotResize = true;
1484    }
1485  }
1486
1487  /**
1488   * Add more colours to the colour map
1489   */
1490  private void extendColourMap(int highest) {
1491    //System.err.println("Extending colour map");
1492    for (int i = m_colorList.size(); i < highest; i++) {
1493      Color pc = m_DefaultColors[i % 10];
1494      int ija =  i / 10;
1495      ija *= 2; 
1496      for (int j=0;j<ija;j++) {
1497        pc = pc.brighter();
1498      }
1499     
1500      m_colorList.addElement(pc);
1501    }
1502  }
1503
1504  /**
1505   * Renders this component
1506   * @param gx the graphics context
1507   */
1508  public void paintComponent(Graphics gx) {
1509    super.paintComponent(gx);
1510    if (m_plotInstances != null 
1511        && m_plotInstances.numInstances() > 0
1512        && m_plotInstances.numAttributes() > 0) {
1513      if (m_plotCompanion != null) {
1514        m_plotCompanion.prePlot(gx);
1515      }
1516
1517      m_JRand = new Random(m_JitterVal);
1518      paintAxis(gx);
1519      if (m_axisChanged || m_plotResize) {
1520        int x_range = m_XaxisEnd - m_XaxisStart;
1521        int y_range = m_YaxisEnd - m_YaxisStart;
1522        if (x_range < 10) {
1523          x_range = 10;
1524        }
1525        if (y_range < 10) {
1526          y_range = 10;
1527        }
1528
1529        m_drawnPoints = new int[x_range + 1][y_range + 1];
1530        fillLookup();
1531        m_plotResize = false;
1532        m_axisChanged = false;
1533      }
1534      paintData(gx);
1535    }
1536  }
1537 
1538  protected static Color checkAgainstBackground(Color c, Color background) {
1539    if (background == null) {
1540      return c;
1541    }
1542   
1543    if (c.equals(background)) {
1544      int red = c.getRed();
1545      int blue = c.getBlue();
1546      int green = c.getGreen();
1547      red += (red < 128) ? (255 - red) / 2 : -(red / 2);
1548      blue += (blue < 128) ? (blue - red) / 2 : -(blue / 2);
1549      green += (green< 128) ? (255 - green) / 2 : -(green / 2);
1550      c = new Color(red, green, blue);
1551    }
1552    return c;
1553  }
1554
1555  /**
1556   * Main method for testing this class
1557   * @param args arguments
1558   */
1559  public static void main(String [] args) {
1560    try {
1561      if (args.length < 1) {
1562        System.err.println("Usage : weka.gui.visualize.Plot2D "
1563                           +"<dataset> [<dataset> <dataset>...]");
1564        System.exit(1);
1565      }
1566
1567      final javax.swing.JFrame jf = 
1568        new javax.swing.JFrame("Weka Explorer: Visualize");
1569      jf.setSize(500,400);
1570      jf.getContentPane().setLayout(new BorderLayout());
1571      final Plot2D p2 = new Plot2D();
1572      jf.getContentPane().add(p2, BorderLayout.CENTER);
1573      jf.addWindowListener(new java.awt.event.WindowAdapter() {
1574          public void windowClosing(java.awt.event.WindowEvent e) {
1575            jf.dispose();
1576            System.exit(0);
1577          }
1578        });
1579     
1580      p2.addMouseListener(new MouseAdapter() {
1581          public void mouseClicked(MouseEvent e) {
1582            if ((e.getModifiers() & InputEvent.BUTTON1_MASK) ==
1583                InputEvent.BUTTON1_MASK) {
1584              p2.searchPoints(e.getX(), e.getY(), false);
1585            } else {
1586              p2.searchPoints(e.getX(), e.getY(), true);
1587            }
1588          }
1589        });
1590
1591      jf.setVisible(true);
1592      if (args.length >= 1) {
1593        for (int j = 0; j < args.length; j++) {
1594          System.err.println("Loading instances from " + args[j]);
1595          java.io.Reader r = new java.io.BufferedReader(
1596                             new java.io.FileReader(args[j]));
1597          Instances i = new Instances(r);
1598          i.setClassIndex(i.numAttributes()-1);
1599          PlotData2D pd1 = new PlotData2D(i);
1600
1601          if (j == 0) {
1602            pd1.setPlotName("Master plot");
1603            p2.setMasterPlot(pd1);
1604            p2.setXindex(2);
1605            p2.setYindex(3);
1606            p2.setCindex(i.classIndex());
1607          } else {
1608            pd1.setPlotName("Plot "+(j+1));
1609            pd1.m_useCustomColour = true;
1610            pd1.m_customColour = (j % 2 == 0) ? Color.red : Color.blue; 
1611            p2.addPlot(pd1);
1612          }
1613        }
1614      }
1615    } catch (Exception ex) {
1616      ex.printStackTrace();
1617      System.err.println(ex.getMessage());
1618    }
1619  }
1620}
Note: See TracBrowser for help on using the repository browser.