source: src/main/java/weka/gui/visualize/VisualizePanel.java @ 11

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

Import di weka.

File size: 76.8 KB
RevLine 
[4]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 *    VisualizePanel.java
19 *    Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.visualize;
24
25import weka.core.Attribute;
26import weka.core.FastVector;
27import weka.core.Instance;
28import weka.core.Instances;
29import weka.gui.ExtensionFileFilter;
30import weka.gui.Logger;
31
32import java.awt.BorderLayout;
33import java.awt.Color;
34import java.awt.Component;
35import java.awt.Dimension;
36import java.awt.Graphics;
37import java.awt.GridBagConstraints;
38import java.awt.GridBagLayout;
39import java.awt.GridLayout;
40import java.awt.Insets;
41import java.awt.event.ActionEvent;
42import java.awt.event.ActionListener;
43import java.awt.event.InputEvent;
44import java.awt.event.MouseAdapter;
45import java.awt.event.MouseEvent;
46import java.awt.event.MouseMotionAdapter;
47import java.io.BufferedReader;
48import java.io.BufferedWriter;
49import java.io.File;
50import java.io.FileReader;
51import java.io.FileWriter;
52import java.io.Writer;
53import java.util.Random;
54
55import javax.swing.BorderFactory;
56import javax.swing.DefaultComboBoxModel;
57import javax.swing.JButton;
58import javax.swing.JComboBox;
59import javax.swing.JFileChooser;
60import javax.swing.JFrame;
61import javax.swing.JLabel;
62import javax.swing.JOptionPane;
63import javax.swing.JPanel;
64import javax.swing.JSlider;
65import javax.swing.SwingConstants;
66import javax.swing.event.ChangeEvent;
67import javax.swing.event.ChangeListener;
68import javax.swing.filechooser.FileFilter;
69
70/**
71 * This panel allows the user to visualize a dataset (and if provided) a
72 * classifier's/clusterer's predictions in two dimensions.
73 *
74 * If the user selects a nominal attribute as the colouring attribute then
75 * each point is drawn in a colour that corresponds to the discrete value
76 * of that attribute for the instance. If the user selects a numeric
77 * attribute to colour on, then the points are coloured using a spectrum
78 * ranging from blue to red (low values to high).
79 *
80 * When a classifier's predictions are supplied they are plotted in one
81 * of two ways (depending on whether the class is nominal or numeric).<br>
82 * For nominal class: an error made by a classifier is plotted as a square
83 * in the colour corresponding to the class it predicted.<br>
84 * For numeric class: predictions are plotted as varying sized x's, where
85 * the size of the x is related to the magnitude of the error.
86 *
87 * @author Mark Hall (mhall@cs.waikato.ac.nz)
88 * @author Malcolm Ware (mfw4@cs.waikato.ac.nz)
89 * @version $Revision: 5747 $
90 */
91public class VisualizePanel
92  extends PrintablePanel {
93
94  /** for serialization */
95  private static final long serialVersionUID = 240108358588153943L;
96
97  /** Inner class to handle plotting */
98  protected class PlotPanel
99    extends PrintablePanel
100    implements Plot2DCompanion {
101
102    /** for serialization */
103    private static final long serialVersionUID = -4823674171136494204L;
104
105    /** The actual generic plotting panel */
106    protected Plot2D m_plot2D = new Plot2D();
107
108    /** The instances from the master plot */
109    protected Instances m_plotInstances=null;
110
111    /** The master plot */
112    protected PlotData2D m_originalPlot=null;
113   
114    /** Indexes of the attributes to go on the x and y axis and the attribute
115        to use for colouring and the current shape for drawing */
116    protected int m_xIndex=0;
117    protected int m_yIndex=0;
118    protected int m_cIndex=0;
119    protected int m_sIndex=0;
120
121    /**the offsets of the axes once label metrics are calculated */
122    private int m_XaxisStart=0;
123    private int m_YaxisStart=0;
124    private int m_XaxisEnd=0;
125    private int m_YaxisEnd=0;
126
127    /** True if the user is currently dragging a box. */
128    private boolean m_createShape;
129   
130    /** contains all the shapes that have been drawn for these attribs */
131    private FastVector m_shapes;
132
133    /** contains the points of the shape currently being drawn. */
134    private FastVector m_shapePoints;
135
136    /** contains the position of the mouse (used for rubberbanding). */
137    private Dimension m_newMousePos;
138
139    /** Constructor */
140    public PlotPanel() {
141      this.setBackground(m_plot2D.getBackground());
142      this.setLayout(new BorderLayout());
143      this.add(m_plot2D, BorderLayout.CENTER);
144      m_plot2D.setPlotCompanion(this);
145
146      m_createShape = false;       
147      m_shapes = null;////
148      m_shapePoints = null;
149      m_newMousePos = new Dimension();
150
151      this.addMouseListener(new MouseAdapter() {
152          ///////     
153          public void mousePressed(MouseEvent e) {
154            if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK) {
155              //
156              if (m_sIndex == 0) {
157                //do nothing it will get dealt to in the clicked method
158              }
159              else if (m_sIndex == 1) {
160                m_createShape = true;
161                m_shapePoints = new FastVector(5);
162                m_shapePoints.addElement(new Double(m_sIndex));
163                m_shapePoints.addElement(new Double(e.getX()));
164                m_shapePoints.addElement(new Double(e.getY()));
165                m_shapePoints.addElement(new Double(e.getX()));
166                m_shapePoints.addElement(new Double(e.getY()));
167                //              Graphics g = PlotPanel.this.getGraphics();
168                Graphics g = m_plot2D.getGraphics();
169                g.setColor(Color.black);
170                g.setXORMode(Color.white);
171                g.drawRect(((Double)m_shapePoints.elementAt(1)).intValue(),
172                           ((Double)m_shapePoints.elementAt(2)).intValue(),
173                           ((Double)m_shapePoints.elementAt(3)).intValue() -
174                           ((Double)m_shapePoints.elementAt(1)).intValue(), 
175                           ((Double)m_shapePoints.elementAt(4)).intValue() -
176                           ((Double)m_shapePoints.elementAt(2)).intValue());
177                g.dispose();
178              }
179              //System.out.println("clicked");
180            }
181            //System.out.println("clicked");
182          }
183          //////
184          public void mouseClicked(MouseEvent e) {
185           
186            if ((m_sIndex == 2 || m_sIndex == 3) && 
187                (m_createShape || 
188                 (e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK)) {
189              if (m_createShape) {
190                //then it has been started already.
191
192                Graphics g = m_plot2D.getGraphics();
193                g.setColor(Color.black);
194                g.setXORMode(Color.white);
195                if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK &&
196                    !e.isAltDown()) {
197                  m_shapePoints.addElement(new 
198                    Double(m_plot2D.convertToAttribX(e.getX())));
199                 
200                  m_shapePoints.addElement(new 
201                    Double(m_plot2D.convertToAttribY(e.getY())));
202                 
203                  m_newMousePos.width = e.getX();
204                  m_newMousePos.height = e.getY();
205                  g.drawLine((int)Math.ceil
206                             (m_plot2D.convertToPanelX
207                              (((Double)m_shapePoints.
208                                elementAt(m_shapePoints.size() - 2)).
209                               doubleValue())),
210                             
211                             (int)Math.ceil
212                             (m_plot2D.convertToPanelY
213                              (((Double)m_shapePoints.
214                                elementAt(m_shapePoints.size() - 1)).
215                               doubleValue())),
216                             m_newMousePos.width, m_newMousePos.height);
217                 
218                }
219                else if (m_sIndex == 3) {
220                  //then extend the lines to infinity
221                  //(100000 or so should be enough).
222                  //the area is selected by where the user right clicks
223                  //the mouse button
224                 
225                  m_createShape = false;
226                  if (m_shapePoints.size() >= 5) {
227                    double cx = Math.ceil
228                      (m_plot2D.convertToPanelX
229                       (((Double)m_shapePoints.elementAt
230                         (m_shapePoints.size() - 4)).doubleValue()));
231                   
232                    double cx2 = Math.ceil
233                      (m_plot2D.convertToPanelX
234                       (((Double)m_shapePoints.elementAt
235                         (m_shapePoints.size() - 2)).doubleValue())) - 
236                      cx;
237                   
238                    cx2 *= 50000;
239                   
240                    double cy = Math.ceil
241                      (m_plot2D.
242                       convertToPanelY(((Double)m_shapePoints.
243                                        elementAt(m_shapePoints.size() - 3)).
244                                       doubleValue()));
245                    double cy2 = Math.ceil
246                      (m_plot2D.convertToPanelY(((Double)m_shapePoints.
247                                          elementAt(m_shapePoints.size() - 1)).
248                                          doubleValue())) - cy;
249                    cy2 *= 50000;
250                           
251                   
252                    double cxa = Math.ceil(m_plot2D.convertToPanelX
253                                           (((Double)m_shapePoints.
254                                             elementAt(3)).
255                                            doubleValue()));
256                    double cxa2 = Math.ceil(m_plot2D.convertToPanelX
257                                            (((Double)m_shapePoints.
258                                              elementAt(1)).
259                                             doubleValue())) - cxa;
260                    cxa2 *= 50000;
261                   
262                   
263                    double cya = Math.ceil
264                      (m_plot2D.convertToPanelY
265                       (((Double)m_shapePoints.elementAt(4)).
266                        doubleValue()));
267                    double cya2 = Math.ceil
268                      (m_plot2D.convertToPanelY
269                       (((Double)m_shapePoints.elementAt(2)).
270                        doubleValue())) - cya;
271                   
272                    cya2 *= 50000;
273                   
274                    m_shapePoints.setElementAt
275                      (new Double(m_plot2D.convertToAttribX(cxa2 + cxa)), 1);
276                   
277                    m_shapePoints.setElementAt
278                      (new Double(m_plot2D.convertToAttribY(cy2 + cy)), 
279                       m_shapePoints.size() - 1);
280                   
281                    m_shapePoints.setElementAt
282                      (new Double(m_plot2D.convertToAttribX(cx2 + cx)), 
283                       m_shapePoints.size() - 2);
284                   
285                    m_shapePoints.setElementAt
286                      (new Double(m_plot2D.convertToAttribY(cya2 + cya)), 2);
287                   
288                   
289                    //determine how infinity line should be built
290                   
291                    cy = Double.POSITIVE_INFINITY;
292                    cy2 = Double.NEGATIVE_INFINITY;
293                    if (((Double)m_shapePoints.elementAt(1)).
294                        doubleValue() > 
295                        ((Double)m_shapePoints.elementAt(3)).
296                        doubleValue()) {
297                      if (((Double)m_shapePoints.elementAt(2)).
298                          doubleValue() == 
299                          ((Double)m_shapePoints.elementAt(4)).
300                          doubleValue()) {
301                        cy = ((Double)m_shapePoints.elementAt(2)).
302                          doubleValue();
303                      }
304                    }
305                    if (((Double)m_shapePoints.elementAt
306                         (m_shapePoints.size() - 2)).doubleValue() > 
307                        ((Double)m_shapePoints.elementAt
308                         (m_shapePoints.size() - 4)).doubleValue()) {
309                      if (((Double)m_shapePoints.elementAt
310                           (m_shapePoints.size() - 3)).
311                          doubleValue() == 
312                          ((Double)m_shapePoints.elementAt
313                           (m_shapePoints.size() - 1)).doubleValue()) {
314                        cy2 = ((Double)m_shapePoints.lastElement()).
315                          doubleValue();
316                      }
317                    }
318                    m_shapePoints.addElement(new Double(cy));
319                    m_shapePoints.addElement(new Double(cy2));
320                   
321                    if (!inPolyline(m_shapePoints, m_plot2D.convertToAttribX
322                                    (e.getX()), 
323                                    m_plot2D.convertToAttribY(e.getY()))) {
324                      Double tmp = (Double)m_shapePoints.
325                        elementAt(m_shapePoints.size() - 2);
326                      m_shapePoints.setElementAt
327                        (m_shapePoints.lastElement(), 
328                         m_shapePoints.size() - 2);
329                      m_shapePoints.setElementAt
330                        (tmp, m_shapePoints.size() - 1);
331                    }
332                   
333                    if (m_shapes == null) {
334                      m_shapes = new FastVector(4);
335                    }
336                    m_shapes.addElement(m_shapePoints);
337
338                    m_submit.setText("Submit");
339                    m_submit.setActionCommand("Submit");
340                   
341                    m_submit.setEnabled(true);
342                  }
343                 
344                  m_shapePoints = null;
345                  PlotPanel.this.repaint();
346                 
347                }
348                else {
349                  //then close the shape
350                  m_createShape = false;
351                  if (m_shapePoints.size() >= 7) {
352                    m_shapePoints.addElement(m_shapePoints.elementAt(1));
353                    m_shapePoints.addElement(m_shapePoints.elementAt(2));
354                    if (m_shapes == null) {
355                      m_shapes = new FastVector(4);
356                    }
357                    m_shapes.addElement(m_shapePoints);
358                           
359                    m_submit.setText("Submit");
360                    m_submit.setActionCommand("Submit");
361                   
362                    m_submit.setEnabled(true);
363                  }
364                  m_shapePoints = null;
365                  PlotPanel.this.repaint();
366                }
367                g.dispose();
368                //repaint();
369              }
370              else if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK) {
371                //then this is the first point
372                m_createShape = true;
373                m_shapePoints = new FastVector(17);
374                m_shapePoints.addElement(new Double(m_sIndex));
375                m_shapePoints.addElement(new 
376                  Double(m_plot2D.convertToAttribX(e.getX()))); //the new point
377                m_shapePoints.addElement(new 
378                  Double(m_plot2D.convertToAttribY(e.getY())));
379                m_newMousePos.width = e.getX();      //the temp mouse point
380                m_newMousePos.height = e.getY();
381
382                Graphics g = m_plot2D.getGraphics();
383                g.setColor(Color.black);
384                g.setXORMode(Color.white);
385                g.drawLine((int)Math.ceil
386                           (m_plot2D.convertToPanelX(((Double)m_shapePoints.
387                                             elementAt(1)).doubleValue())),
388                           (int)Math.ceil
389                           (m_plot2D.convertToPanelY(((Double)m_shapePoints.
390                                             elementAt(2)).doubleValue())),
391                           m_newMousePos.width, m_newMousePos.height);
392                g.dispose();
393              }
394            }
395            else {
396              if ((e.getModifiers() & InputEvent.BUTTON1_MASK) == 
397                  InputEvent.BUTTON1_MASK) {
398               
399                m_plot2D.searchPoints(e.getX(),e.getY(), false);
400              } else {
401                m_plot2D.searchPoints(e.getX(), e.getY(), true);
402              }
403            }
404          }
405         
406          /////////             
407          public void mouseReleased(MouseEvent e) {
408
409            if (m_createShape) {
410              if (((Double)m_shapePoints.elementAt(0)).intValue() == 1) {
411                m_createShape = false;
412                Graphics g = m_plot2D.getGraphics();
413                g.setColor(Color.black);
414                g.setXORMode(Color.white);
415                g.drawRect(((Double)m_shapePoints.elementAt(1)).
416                           intValue(), 
417                           ((Double)m_shapePoints.elementAt(2)).intValue(),
418                           ((Double)m_shapePoints.elementAt(3)).intValue() -
419                           ((Double)m_shapePoints.elementAt(1)).intValue(), 
420                           ((Double)m_shapePoints.elementAt(4)).intValue() -
421                           ((Double)m_shapePoints.elementAt(2)).intValue());
422               
423                g.dispose();
424                if (checkPoints(((Double)m_shapePoints.elementAt(1)).
425                                doubleValue(), 
426                                ((Double)m_shapePoints.elementAt(2)).
427                                doubleValue()) &&
428                    checkPoints(((Double)m_shapePoints.elementAt(3)).
429                                doubleValue(), 
430                                ((Double)m_shapePoints.elementAt(4)).
431                                doubleValue())) {
432                  //then the points all land on the screen
433                  //now do special check for the rectangle
434                  if (((Double)m_shapePoints.elementAt(1)).doubleValue() <
435                      ((Double)m_shapePoints.elementAt(3)).doubleValue() 
436                      &&
437                      ((Double)m_shapePoints.elementAt(2)).doubleValue() <
438                      ((Double)m_shapePoints.elementAt(4)).doubleValue()) {
439                    //then the rectangle is valid
440                    if (m_shapes == null) {
441                      m_shapes = new FastVector(2);
442                    }
443                    m_shapePoints.setElementAt(new 
444                      Double(m_plot2D.convertToAttribX(((Double)m_shapePoints.
445                                               elementAt(1)).
446                                              doubleValue())), 1);
447                    m_shapePoints.setElementAt(new 
448                      Double(m_plot2D.convertToAttribY(((Double)m_shapePoints.
449                                               elementAt(2)).
450                                              doubleValue())), 2);
451                    m_shapePoints.setElementAt(new 
452                      Double(m_plot2D.convertToAttribX(((Double)m_shapePoints.
453                                               elementAt(3)).
454                                              doubleValue())), 3);
455                    m_shapePoints.setElementAt(new 
456                      Double(m_plot2D.convertToAttribY(((Double)m_shapePoints.
457                                               elementAt(4)).
458                                              doubleValue())), 4);
459                   
460                    m_shapes.addElement(m_shapePoints);
461                   
462                    m_submit.setText("Submit");
463                    m_submit.setActionCommand("Submit");
464                   
465                    m_submit.setEnabled(true);
466
467                    PlotPanel.this.repaint();
468                  }
469                }
470                m_shapePoints = null;
471              }
472            }
473          }
474        });
475     
476      this.addMouseMotionListener(new MouseMotionAdapter() {
477          public void mouseDragged(MouseEvent e) {
478            //check if the user is dragging a box
479            if (m_createShape) {
480              if (((Double)m_shapePoints.elementAt(0)).intValue() == 1) {
481                Graphics g = m_plot2D.getGraphics();
482                g.setColor(Color.black);
483                g.setXORMode(Color.white);
484                g.drawRect(((Double)m_shapePoints.elementAt(1)).intValue(), 
485                           ((Double)m_shapePoints.elementAt(2)).intValue(),
486                           ((Double)m_shapePoints.elementAt(3)).intValue() -
487                           ((Double)m_shapePoints.elementAt(1)).intValue(), 
488                           ((Double)m_shapePoints.elementAt(4)).intValue() -
489                           ((Double)m_shapePoints.elementAt(2)).intValue());
490               
491                m_shapePoints.setElementAt(new Double(e.getX()), 3);
492                m_shapePoints.setElementAt(new Double(e.getY()), 4);
493               
494                g.drawRect(((Double)m_shapePoints.elementAt(1)).intValue(), 
495                           ((Double)m_shapePoints.elementAt(2)).intValue(),
496                           ((Double)m_shapePoints.elementAt(3)).intValue() -
497                           ((Double)m_shapePoints.elementAt(1)).intValue(), 
498                           ((Double)m_shapePoints.elementAt(4)).intValue() -
499                           ((Double)m_shapePoints.elementAt(2)).intValue());
500                g.dispose();
501              }
502            }
503          }
504         
505          public void mouseMoved(MouseEvent e) {
506            if (m_createShape) {
507              if (((Double)m_shapePoints.elementAt(0)).intValue() == 2 || 
508                  ((Double)m_shapePoints.elementAt(0)).intValue() == 3) {
509                Graphics g = m_plot2D.getGraphics();
510                g.setColor(Color.black);
511                g.setXORMode(Color.white);
512                g.drawLine((int)Math.ceil(m_plot2D.convertToPanelX
513                                          (((Double)m_shapePoints.elementAt
514                                            (m_shapePoints.size() - 2)).
515                                           doubleValue())),
516                           (int)Math.ceil(m_plot2D.convertToPanelY
517                                          (((Double)m_shapePoints.elementAt
518                                            (m_shapePoints.size() - 1)).
519                                           doubleValue())),
520                           m_newMousePos.width, m_newMousePos.height);
521               
522                m_newMousePos.width = e.getX();
523                m_newMousePos.height = e.getY();
524               
525                g.drawLine((int)Math.ceil(m_plot2D.convertToPanelX
526                                          (((Double)m_shapePoints.elementAt
527                                            (m_shapePoints.size() - 2)).
528                                           doubleValue())),
529                           (int)Math.ceil(m_plot2D.convertToPanelY
530                                          (((Double)m_shapePoints.elementAt
531                                            (m_shapePoints.size() - 1)).
532                                           doubleValue())),
533                           m_newMousePos.width, m_newMousePos.height);
534                g.dispose();
535              }
536            }
537          }
538        });
539     
540      m_submit.addActionListener(new ActionListener() {
541          public void actionPerformed(ActionEvent e) {
542         
543            if (e.getActionCommand().equals("Submit")) {
544              if (m_splitListener != null && m_shapes != null) {
545                //then send the split to the listener
546                Instances sub_set1 = new Instances(m_plot2D.getMasterPlot().
547                                                   m_plotInstances, 500);
548                Instances sub_set2 = new Instances(m_plot2D.getMasterPlot().
549                                                   m_plotInstances, 500);
550               
551                if (m_plot2D.getMasterPlot().
552                    m_plotInstances != null) {
553                 
554                  for (int noa = 0 ; noa < m_plot2D.getMasterPlot().
555                         m_plotInstances.numInstances(); noa++) {
556                    if (!m_plot2D.getMasterPlot().
557                        m_plotInstances.instance(noa).isMissing(m_xIndex) &&
558                        !m_plot2D.getMasterPlot().
559                        m_plotInstances.instance(noa).isMissing(m_yIndex)){
560                     
561                      if (inSplit(m_plot2D.getMasterPlot().
562                                  m_plotInstances.instance(noa))) {
563                        sub_set1.add(m_plot2D.getMasterPlot().
564                                     m_plotInstances.instance(noa));
565                      }
566                      else {
567                        sub_set2.add(m_plot2D.getMasterPlot().
568                                     m_plotInstances.instance(noa));
569                      }
570                    }
571                  }
572                  FastVector tmp = m_shapes;
573                  cancelShapes();
574                  m_splitListener.userDataEvent(new 
575                    VisualizePanelEvent(tmp, sub_set1, sub_set2, m_xIndex, 
576                                        m_yIndex));
577                }
578              }
579              else if (m_shapes != null && 
580                       m_plot2D.getMasterPlot().m_plotInstances != null) { 
581                Instances sub_set1 = new Instances(m_plot2D.getMasterPlot().
582                                                   m_plotInstances, 500);
583                int count = 0;
584                for (int noa = 0 ; noa < m_plot2D.getMasterPlot().
585                       m_plotInstances.numInstances(); noa++) {
586                  if (inSplit(m_plot2D.getMasterPlot().
587                              m_plotInstances.instance(noa))) {
588                    sub_set1.add(m_plot2D.getMasterPlot().
589                                 m_plotInstances.instance(noa));
590                    count++;
591                  }
592                 
593                }
594
595                int [] nSizes = null;
596                int [] nTypes = null;
597                int x = m_xIndex;
598                int y = m_yIndex;
599
600                if (m_originalPlot == null) {
601                  //this sets these instances as the instances
602                  //to go back to.
603                  m_originalPlot = m_plot2D.getMasterPlot();
604                }
605
606                if (count > 0) {
607                  nTypes = new int[count];
608                  nSizes = new int[count];
609                  count = 0;
610                  for (int noa = 0; noa < m_plot2D.getMasterPlot().
611                         m_plotInstances.numInstances(); 
612                       noa++) {
613                    if (inSplit(m_plot2D.getMasterPlot().
614                                m_plotInstances.instance(noa))) {
615
616                      nTypes[count] = m_plot2D.getMasterPlot().
617                        m_shapeType[noa];
618                      nSizes[count] = m_plot2D.getMasterPlot().
619                        m_shapeSize[noa];
620                      count++;
621                    }
622                  }
623                }
624                cancelShapes();
625
626                PlotData2D newPlot = new PlotData2D(sub_set1);
627
628                try {
629                  newPlot.setShapeSize(nSizes);
630                  newPlot.setShapeType(nTypes);
631               
632                  m_plot2D.removeAllPlots();
633                 
634                  VisualizePanel.this.addPlot(newPlot);
635                } catch (Exception ex) {
636                  System.err.println(ex);
637                  ex.printStackTrace();
638                }
639
640                try {
641                  VisualizePanel.this.setXIndex(x);
642                  VisualizePanel.this.setYIndex(y);
643                } catch(Exception er) {
644                  System.out.println("Error : " + er);
645                  //  System.out.println("Part of user input so had to" +
646                  //             " catch here");
647                }
648              }
649            }
650            else if (e.getActionCommand().equals("Reset")) {
651              int x = m_xIndex;
652              int y = m_yIndex;
653
654              m_plot2D.removeAllPlots();
655              try {
656                VisualizePanel.this.addPlot(m_originalPlot);
657              } catch (Exception ex) {
658                System.err.println(ex);
659                ex.printStackTrace();
660              }
661
662              try {
663                VisualizePanel.this.setXIndex(x);
664                VisualizePanel.this.setYIndex(y);
665              } catch(Exception er) {
666                System.out.println("Error : " + er);
667              }
668            }
669          } 
670        });
671
672      m_cancel.addActionListener(new ActionListener() {
673          public void actionPerformed(ActionEvent e) {
674            cancelShapes();
675            PlotPanel.this.repaint();
676          }
677        });
678      ////////////
679    }
680
681    /**
682     * Removes all the plots.
683     */
684    public void removeAllPlots() {
685      m_plot2D.removeAllPlots();
686      m_legendPanel.setPlotList(m_plot2D.getPlots());
687    }
688   
689    /**
690     * @return The FastVector containing all the shapes.
691     */
692    public FastVector getShapes() {
693     
694      return m_shapes;
695    }
696   
697    /**
698     * Sets the list of shapes to empty and also cancels
699     * the current shape being drawn (if applicable).
700     */
701    public void cancelShapes() {
702       
703      if (m_splitListener == null) {
704        m_submit.setText("Reset");
705        m_submit.setActionCommand("Reset");
706
707        if (m_originalPlot == null || 
708            m_originalPlot.m_plotInstances == m_plotInstances) {
709          m_submit.setEnabled(false);
710        }
711        else {
712          m_submit.setEnabled(true);
713        }
714      }
715      else {
716        m_submit.setEnabled(false);
717      }
718     
719      m_createShape = false;
720      m_shapePoints = null;
721      m_shapes = null;
722      this.repaint();
723    }
724
725    /**
726     * This can be used to set the shapes that should appear.
727     * @param v The list of shapes.
728     */
729    public void setShapes(FastVector v) {
730      //note that this method should be fine for doubles,
731      //but anything else that uses something other than doubles
732      //(or uneditable objects) could have unsafe copies.
733      if (v != null) {
734        FastVector temp;
735        m_shapes = new FastVector(v.size());
736        for (int noa = 0; noa < v.size(); noa++) {
737          temp = new FastVector(((FastVector)v.elementAt(noa)).size());
738          m_shapes.addElement(temp);
739          for (int nob = 0; nob < ((FastVector)v.elementAt(noa)).size()
740                 ; nob++) {
741           
742            temp.addElement(((FastVector)v.elementAt(noa)).elementAt(nob));
743           
744          }
745        }
746      }
747      else {
748        m_shapes = null;
749      }
750      this.repaint();
751    }
752   
753    /**
754     * This will check the values of the screen points passed and make sure
755     * that they land on the screen
756     * @param x1 The x coord.
757     * @param y1 The y coord.
758     * @return true if the point would land on the screen
759     */
760    private boolean checkPoints(double x1, double y1) {
761      if (x1 < 0 || x1 > this.getSize().width || y1 < 0 
762          || y1 > this.getSize().height) {
763        return false;
764      }
765      return true;
766    }
767   
768    /**
769     * This will check if an instance is inside or outside of the current
770     * shapes.
771     * @param i The instance to check.
772     * @return True if 'i' falls inside the shapes, false otherwise.
773     */
774    public boolean inSplit(Instance i) {
775      //this will check if the instance lies inside the shapes or not
776     
777      if (m_shapes != null) {
778        FastVector stmp;
779        double x1, y1, x2, y2;
780        for (int noa = 0; noa < m_shapes.size(); noa++) {
781          stmp = (FastVector)m_shapes.elementAt(noa);
782          if (((Double)stmp.elementAt(0)).intValue() == 1) {
783            //then rectangle
784            x1 = ((Double)stmp.elementAt(1)).doubleValue();
785            y1 = ((Double)stmp.elementAt(2)).doubleValue();
786            x2 = ((Double)stmp.elementAt(3)).doubleValue();
787            y2 = ((Double)stmp.elementAt(4)).doubleValue();
788            if (i.value(m_xIndex) >= x1 && i.value(m_xIndex) <= x2 &&
789                i.value(m_yIndex) <= y1 && i.value(m_yIndex) >= y2) {
790              //then is inside split so return true;
791              return true;
792            }
793          }
794          else if (((Double)stmp.elementAt(0)).intValue() == 2) {
795            //then polygon
796            if (inPoly(stmp, i.value(m_xIndex), i.value(m_yIndex))) {
797              return true;
798            }
799          }
800          else if (((Double)stmp.elementAt(0)).intValue() == 3) {
801            //then polyline
802            if (inPolyline(stmp, i.value(m_xIndex), i.value(m_yIndex))) {
803              return true;
804            }
805          }
806        }
807      }
808      return false;
809    }
810   
811    /**
812     * Checks to see if the coordinate passed is inside the ployline
813     * passed, Note that this is done using attribute values and not there
814     * respective screen values.
815     * @param ob The polyline.
816     * @param x The x coord.
817     * @param y The y coord.
818     * @return True if it falls inside the polyline, false otherwise.
819     */
820    private boolean inPolyline(FastVector ob, double x, double y) {
821      //this works similar to the inPoly below except that
822      //the first and last lines are treated as extending infinite in one
823      //direction and
824      //then infinitly in the x dirction their is a line that will
825      //normaly be infinite but
826      //can be finite in one or both directions
827     
828      int countx = 0;
829      double vecx, vecy;
830      double change;
831      double x1, y1, x2, y2;
832     
833      for (int noa = 1; noa < ob.size() - 4; noa+= 2) {
834        y1 = ((Double)ob.elementAt(noa+1)).doubleValue();
835        y2 = ((Double)ob.elementAt(noa+3)).doubleValue();
836        x1 = ((Double)ob.elementAt(noa)).doubleValue();
837        x2 = ((Double)ob.elementAt(noa+2)).doubleValue();
838       
839        //System.err.println(y1 + " " + y2 + " " + x1 + " " + x2);
840        vecy = y2 - y1;
841        vecx = x2 - x1;
842        if (noa == 1 && noa == ob.size() - 6) {
843          //then do special test first and last edge
844          if (vecy != 0) {
845            change = (y - y1) / vecy;
846            if (vecx * change + x1 >= x) {
847              //then intersection
848              countx++;
849            }
850          }
851        }
852        else if (noa == 1) {
853          if ((y < y2 && vecy > 0) || (y > y2 && vecy < 0)) {
854            //now just determine intersection or not
855            change = (y - y1) / vecy;
856            if (vecx * change + x1 >= x) {
857              //then intersection on horiz
858              countx++;
859            }
860          }
861        }
862        else if (noa == ob.size() - 6) {
863          //then do special test on last edge
864          if ((y <= y1 && vecy < 0) || (y >= y1 && vecy > 0)) {
865            change = (y - y1) / vecy;
866            if (vecx * change + x1 >= x) {
867              countx++;
868            }
869          }
870        }
871        else if ((y1 <= y && y < y2) || (y2 < y && y <= y1)) {
872          //then continue tests.
873          if (vecy == 0) {
874            //then lines are parallel stop tests in
875            //ofcourse it should never make it this far
876          }
877          else {
878            change = (y - y1) / vecy;
879            if (vecx * change + x1 >= x) {
880              //then intersects on horiz
881              countx++;
882            }
883          }
884        }
885      }
886     
887      //now check for intersection with the infinity line
888      y1 = ((Double)ob.elementAt(ob.size() - 2)).doubleValue();
889      y2 = ((Double)ob.elementAt(ob.size() - 1)).doubleValue();
890     
891      if (y1 > y2) {
892        //then normal line
893        if (y1 >= y && y > y2) {
894          countx++;
895        }
896      }
897      else {
898        //then the line segment is inverted
899        if (y1 >= y || y > y2) {
900          countx++;
901        }
902      }
903     
904      if ((countx % 2) == 1) {
905        return true;
906      }
907      else {
908        return false;
909      }
910    }
911
912
913    /**
914     * This checks to see if The coordinate passed is inside
915     * the polygon that was passed.
916     * @param ob The polygon.
917     * @param x The x coord.
918     * @param y The y coord.
919     * @return True if the coordinate is in the polygon, false otherwise.
920     */
921    private boolean inPoly(FastVector ob, double x, double y) {
922      //brief on how this works
923      //it draws a line horizontally from the point to the right (infinitly)
924      //it then sees how many lines of the polygon intersect this,
925      //if it is even then the point is
926      // outside the polygon if it's odd then it's inside the polygon
927      int count = 0;
928      double vecx, vecy;
929      double change;
930      double x1, y1, x2, y2;
931      for (int noa = 1; noa < ob.size() - 2; noa += 2) {
932        y1 = ((Double)ob.elementAt(noa+1)).doubleValue();
933        y2 = ((Double)ob.elementAt(noa+3)).doubleValue();
934        if ((y1 <= y && y < y2) || (y2 < y && y <= y1)) {
935          //then continue tests.
936          vecy = y2 - y1;
937          if (vecy == 0) {
938            //then lines are parallel stop tests for this line
939          }
940          else {
941            x1 = ((Double)ob.elementAt(noa)).doubleValue();
942            x2 = ((Double)ob.elementAt(noa+2)).doubleValue();
943            vecx = x2 - x1;
944            change = (y - y1) / vecy;
945            if (vecx * change + x1 >= x) {
946              //then add to count as an intersected line
947              count++;
948            }
949          }
950        }
951      }
952      if ((count % 2) == 1) {
953        //then lies inside polygon
954        //System.out.println("in");
955        return true;
956      }
957      else {
958        //System.out.println("out");
959        return false;
960      }
961      //System.out.println("WHAT?!?!?!?!!?!??!?!");
962      //return false;
963    }
964
965    /**
966     * Set level of jitter and repaint the plot using the new jitter value
967     * @param j the level of jitter
968     */
969    public void setJitter(int j) {
970      m_plot2D.setJitter(j);
971    }
972
973    /**
974     * Set the index of the attribute to go on the x axis
975     * @param x the index of the attribute to use on the x axis
976     */
977    public void setXindex(int x) {
978
979      // this just ensures that the shapes get disposed of
980      //if the attribs change
981      if (x != m_xIndex) {
982        cancelShapes();
983      }
984      m_xIndex = x;
985      m_plot2D.setXindex(x);
986      if (m_showAttBars) {
987        m_attrib.setX(x);
988      }
989      //      this.repaint();
990    }
991   
992    /**
993     * Set the index of the attribute to go on the y axis
994     * @param y the index of the attribute to use on the y axis
995     */
996    public void setYindex(int y) {
997   
998      // this just ensures that the shapes get disposed of
999      //if the attribs change
1000      if (y != m_yIndex) {
1001        cancelShapes();
1002      }
1003      m_yIndex = y;
1004      m_plot2D.setYindex(y);
1005      if (m_showAttBars) {
1006        m_attrib.setY(y);
1007      }
1008      //      this.repaint();
1009    }
1010
1011    /**
1012     * Set the index of the attribute to use for colouring
1013     * @param c the index of the attribute to use for colouring
1014     */
1015    public void setCindex(int c) {
1016      m_cIndex = c;
1017      m_plot2D.setCindex(c);
1018      if (m_showAttBars) {
1019        m_attrib.setCindex(c, m_plot2D.getMaxC(), m_plot2D.getMinC());
1020      }
1021      m_classPanel.setCindex(c);
1022      this.repaint();
1023    }
1024
1025    /**
1026     * Set the index of the attribute to use for the shape.
1027     * @param s the index of the attribute to use for the shape
1028     */
1029    public void setSindex(int s) {
1030      if (s != m_sIndex) {
1031        m_shapePoints = null;
1032        m_createShape = false;
1033      }
1034      m_sIndex = s;
1035      this.repaint();
1036    }
1037   
1038    /**
1039     * Clears all existing plots and sets a new master plot
1040     * @param newPlot the new master plot
1041     * @exception Exception if plot could not be added
1042     */
1043    public void setMasterPlot(PlotData2D newPlot) throws Exception {
1044      m_plot2D.removeAllPlots();
1045      this.addPlot(newPlot);
1046    }
1047
1048    /**
1049     * Adds a plot. If there are no plots so far this plot becomes
1050     * the master plot and, if it has a custom colour defined then
1051     * the class panel is removed.
1052     * @param newPlot the plot to add.
1053     * @exception Exception if plot could not be added
1054     */
1055    public void addPlot(PlotData2D newPlot) throws Exception {
1056      if (m_plot2D.getPlots().size() == 0) {
1057        m_plot2D.addPlot(newPlot);
1058        if (m_plotSurround.getComponentCount() > 1 && 
1059            m_plotSurround.getComponent(1) == m_attrib &&
1060            m_showAttBars) {
1061          try {
1062            m_attrib.setInstances(newPlot.m_plotInstances);
1063            m_attrib.setCindex(0);m_attrib.setX(0); m_attrib.setY(0);
1064          } catch (Exception ex) {
1065            // more attributes than the panel can handle?
1066            // Due to hard coded constraints in GridBagLayout
1067            m_plotSurround.remove(m_attrib);
1068            System.err.println("Warning : data contains more attributes "
1069                               +"than can be displayed as attribute bars.");
1070            if (m_Log != null) {
1071              m_Log.logMessage("Warning : data contains more attributes "
1072                               +"than can be displayed as attribute bars.");
1073            }
1074          }
1075        } else if (m_showAttBars) {
1076          try {
1077            m_attrib.setInstances(newPlot.m_plotInstances);
1078            m_attrib.setCindex(0);m_attrib.setX(0); m_attrib.setY(0);
1079            GridBagConstraints constraints = new GridBagConstraints();
1080            constraints.fill = GridBagConstraints.BOTH;
1081            constraints.insets = new Insets(0, 0, 0, 0);
1082            constraints.gridx=4;constraints.gridy=0;constraints.weightx=1;
1083            constraints.gridwidth=1;constraints.gridheight=1;
1084            constraints.weighty=5;
1085            m_plotSurround.add(m_attrib, constraints);
1086          } catch (Exception ex) {
1087            System.err.println("Warning : data contains more attributes "
1088                               +"than can be displayed as attribute bars.");
1089            if (m_Log != null) {
1090              m_Log.logMessage("Warning : data contains more attributes "
1091                               +"than can be displayed as attribute bars.");
1092            }
1093          }
1094        }
1095        m_classPanel.setInstances(newPlot.m_plotInstances);
1096
1097        plotReset(newPlot.m_plotInstances, newPlot.getCindex());
1098        if (newPlot.m_useCustomColour && m_showClassPanel) {
1099          VisualizePanel.this.remove(m_classSurround);
1100          switchToLegend();
1101          m_legendPanel.setPlotList(m_plot2D.getPlots());
1102          m_ColourCombo.setEnabled(false);
1103        }
1104      } else  {
1105        if (!newPlot.m_useCustomColour && m_showClassPanel) {
1106          VisualizePanel.this.add(m_classSurround, BorderLayout.SOUTH);
1107          m_ColourCombo.setEnabled(true);
1108        }
1109        if (m_plot2D.getPlots().size() == 1) {
1110          switchToLegend();
1111        }
1112        m_plot2D.addPlot(newPlot);
1113        m_legendPanel.setPlotList(m_plot2D.getPlots());
1114      }
1115    }
1116
1117    /**
1118     * Remove the attibute panel and replace it with the legend panel
1119     */
1120    protected void switchToLegend() {
1121
1122      if (m_plotSurround.getComponentCount() > 1 && 
1123          m_plotSurround.getComponent(1) == m_attrib) {
1124        m_plotSurround.remove(m_attrib);
1125      }
1126       
1127      if (m_plotSurround.getComponentCount() > 1 &&
1128          m_plotSurround.getComponent(1) == m_legendPanel) {
1129        return;
1130      }
1131
1132      GridBagConstraints constraints = new GridBagConstraints();
1133      constraints.fill = GridBagConstraints.BOTH;
1134      constraints.insets = new Insets(0, 0, 0, 0);
1135      constraints.gridx=4;constraints.gridy=0;constraints.weightx=1;
1136      constraints.gridwidth=1;constraints.gridheight=1;
1137      constraints.weighty=5;
1138      m_plotSurround.add(m_legendPanel, constraints);
1139      setSindex(0);
1140      m_ShapeCombo.setEnabled(false);
1141    }
1142   
1143    protected void switchToBars() {
1144      if (m_plotSurround.getComponentCount() > 1 && 
1145          m_plotSurround.getComponent(1) == m_legendPanel) {
1146        m_plotSurround.remove(m_legendPanel);
1147      }
1148     
1149      if (m_plotSurround.getComponentCount() > 1 &&
1150          m_plotSurround.getComponent(1) == m_attrib) {
1151        return;
1152      }
1153     
1154      if (m_showAttBars) {
1155        try {
1156          m_attrib.setInstances(m_plot2D.getMasterPlot().m_plotInstances);
1157          m_attrib.setCindex(0);m_attrib.setX(0); m_attrib.setY(0);
1158          GridBagConstraints constraints = new GridBagConstraints();
1159          constraints.fill = GridBagConstraints.BOTH;
1160          constraints.insets = new Insets(0, 0, 0, 0);
1161          constraints.gridx=4;constraints.gridy=0;constraints.weightx=1;
1162          constraints.gridwidth=1;constraints.gridheight=1;
1163          constraints.weighty=5;
1164          m_plotSurround.add(m_attrib, constraints);
1165        } catch (Exception ex) {
1166          System.err.println("Warning : data contains more attributes "
1167                             +"than can be displayed as attribute bars.");
1168          if (m_Log != null) {
1169            m_Log.logMessage("Warning : data contains more attributes "
1170                             +"than can be displayed as attribute bars.");
1171          }
1172        }
1173      }
1174    }
1175
1176    /**
1177     * Reset the visualize panel's buttons and the plot panels instances
1178     *
1179     * @param inst      the data
1180     * @param cIndex    the color index
1181     */
1182    private void plotReset(Instances inst, int cIndex) {
1183      if (m_splitListener == null) {
1184        m_submit.setText("Reset");
1185        m_submit.setActionCommand("Reset");
1186        //if (m_origInstances == null || m_origInstances == inst) {
1187        if (m_originalPlot == null || m_originalPlot.m_plotInstances == inst) {
1188          m_submit.setEnabled(false);
1189        }
1190        else {
1191          m_submit.setEnabled(true);
1192        }
1193      } 
1194      else {
1195        m_submit.setEnabled(false);
1196      }
1197
1198      m_plotInstances = inst;
1199      if (m_splitListener != null) {
1200        m_plotInstances.randomize(new Random());
1201      }
1202      m_xIndex=0;
1203      m_yIndex=0;
1204      m_cIndex=cIndex;
1205      cancelShapes();
1206    }
1207
1208    /**
1209     * Set a list of colours to use for plotting points
1210     * @param cols a list of java.awt.Colors
1211     */
1212    public void setColours(FastVector cols) {
1213      m_plot2D.setColours(cols);
1214      m_colorList = cols;
1215    }
1216   
1217    /**
1218     * This will draw the shapes created onto the panel.
1219     * For best visual, this should be the first thing to be drawn
1220     * (and it currently is).
1221     * @param gx The graphics context.
1222     */
1223    private void drawShapes(Graphics gx) {
1224      //FastVector tmp = m_plot.getShapes();
1225     
1226      if (m_shapes != null) {
1227        FastVector stmp;
1228        int x1, y1, x2, y2;
1229        for (int noa = 0; noa < m_shapes.size(); noa++) {
1230          stmp = (FastVector)m_shapes.elementAt(noa);
1231          if (((Double)stmp.elementAt(0)).intValue() == 1) {
1232            //then rectangle
1233            x1 = (int)m_plot2D.convertToPanelX(((Double)stmp.elementAt(1)).
1234                                      doubleValue());
1235            y1 = (int)m_plot2D.convertToPanelY(((Double)stmp.elementAt(2)).
1236                                      doubleValue());
1237            x2 = (int)m_plot2D.convertToPanelX(((Double)stmp.elementAt(3)).
1238                                      doubleValue());
1239            y2 = (int)m_plot2D.convertToPanelY(((Double)stmp.elementAt(4)).
1240                                      doubleValue());
1241           
1242            gx.setColor(Color.gray);
1243            gx.fillRect(x1, y1, x2 - x1, y2 - y1);
1244            gx.setColor(Color.black);
1245            gx.drawRect(x1, y1, x2 - x1, y2 - y1);
1246           
1247          }
1248          else if (((Double)stmp.elementAt(0)).intValue() == 2) {
1249            //then polygon
1250            int[] ar1, ar2;
1251            ar1 = getXCoords(stmp);
1252            ar2 = getYCoords(stmp);
1253            gx.setColor(Color.gray);
1254            gx.fillPolygon(ar1, ar2, (stmp.size() - 1) / 2); 
1255            gx.setColor(Color.black);
1256            gx.drawPolyline(ar1, ar2, (stmp.size() - 1) / 2);
1257          }
1258          else if (((Double)stmp.elementAt(0)).intValue() == 3) {
1259            //then polyline
1260            int[] ar1, ar2;
1261            FastVector tmp = makePolygon(stmp);
1262            ar1 = getXCoords(tmp);
1263            ar2 = getYCoords(tmp);
1264           
1265            gx.setColor(Color.gray);
1266            gx.fillPolygon(ar1, ar2, (tmp.size() - 1) / 2);
1267            gx.setColor(Color.black);
1268            gx.drawPolyline(ar1, ar2, (tmp.size() - 1) / 2);
1269          }
1270        }
1271      }
1272     
1273      if (m_shapePoints != null) {
1274        //then the current image needs to be refreshed
1275        if (((Double)m_shapePoints.elementAt(0)).intValue() == 2 ||
1276            ((Double)m_shapePoints.elementAt(0)).intValue() == 3) {
1277          gx.setColor(Color.black);
1278          gx.setXORMode(Color.white);
1279          int[] ar1, ar2;
1280          ar1 = getXCoords(m_shapePoints);
1281          ar2 = getYCoords(m_shapePoints);
1282          gx.drawPolyline(ar1, ar2, (m_shapePoints.size() - 1) / 2);
1283          m_newMousePos.width = (int)Math.ceil
1284            (m_plot2D.convertToPanelX(((Double)m_shapePoints.elementAt
1285                              (m_shapePoints.size() - 2)).doubleValue()));
1286         
1287          m_newMousePos.height = (int)Math.ceil
1288            (m_plot2D.convertToPanelY(((Double)m_shapePoints.elementAt
1289                              (m_shapePoints.size() - 1)).doubleValue()));
1290         
1291          gx.drawLine((int)Math.ceil
1292                     (m_plot2D.convertToPanelX(((Double)m_shapePoints.elementAt
1293                                                (m_shapePoints.size() - 2)).
1294                                               doubleValue())),
1295                      (int)Math.ceil(m_plot2D.convertToPanelY
1296                                     (((Double)m_shapePoints.elementAt
1297                                       (m_shapePoints.size() - 1)).
1298                                      doubleValue())),
1299                      m_newMousePos.width, m_newMousePos.height);
1300          gx.setPaintMode();
1301        }
1302      }
1303    }
1304   
1305    /**
1306     * This is called for polylines to see where there two lines that
1307     * extend to infinity cut the border of the view.
1308     * @param x1 an x point along the line
1309     * @param y1 the accompanying y point.
1310     * @param x2 The x coord of the end point of the line.
1311     * @param y2 The y coord of the end point of the line.
1312     * @param x 0 or the width of the border line if it has one.
1313     * @param y 0 or the height of the border line if it has one.
1314     * @param offset The offset for the border line (either for x or y
1315     * dependant on which one doesn't change).
1316     * @return double array that contains the coordinate for the point
1317     * that the polyline cuts the border (which ever side that may be).
1318     */
1319    private double[] lineIntersect(double x1, double y1, double x2, double y2, 
1320                                   double x, double y, double offset) {
1321      //the first 4 params are thestart and end points of a line
1322      //the next param is either 0 for no change in x or change in x,
1323      //the next param is the same for y
1324      //the final 1 is the offset for either x or y (which ever has no change)
1325      double xval;
1326      double yval;
1327      double xn = -100, yn = -100;
1328      double change;
1329      if (x == 0) {
1330        if ((x1 <= offset && offset < x2) || (x1 >= offset && offset > x2)) {
1331          //then continue
1332          xval = x1 - x2;
1333          change = (offset - x2) / xval;
1334          yn = (y1 - y2) * change + y2;
1335          if (0 <= yn && yn <= y) {
1336            //then good
1337            xn = offset;
1338          }
1339          else {
1340            //no intersect
1341            xn = -100;
1342          }
1343        }
1344      }
1345      else if (y == 0) {
1346        if ((y1 <= offset && offset < y2) || (y1 >= offset && offset > y2)) {
1347          //the continue
1348          yval = (y1 - y2);
1349          change = (offset - y2) / yval;
1350          xn = (x1 - x2) * change + x2;
1351          if (0 <= xn && xn <= x) {
1352            //then good
1353            yn = offset;
1354          }
1355          else {
1356            xn = -100;
1357          }
1358        }
1359      }
1360      double[] ret = new double[2];
1361      ret[0] = xn;
1362      ret[1] = yn;
1363      return ret;
1364    }
1365
1366
1367    /**
1368     * This will convert a polyline to a polygon for drawing purposes
1369     * So that I can simply use the polygon drawing function.
1370     * @param v The polyline to convert.
1371     * @return A FastVector containing the polygon.
1372     */
1373    private FastVector makePolygon(FastVector v) {
1374      FastVector building = new FastVector(v.size() + 10);
1375      double x1, y1, x2, y2;
1376      int edge1 = 0, edge2 = 0;
1377      for (int noa = 0; noa < v.size() - 2; noa++) {
1378        building.addElement(new Double(((Double)v.elementAt(noa)).
1379                                       doubleValue()));
1380      }
1381     
1382      //now clip the lines
1383      double[] new_coords;
1384      //note lineIntersect , expects the values to have been converted to
1385      //screen coords
1386      //note the first point passed is the one that gets shifted.
1387      x1 = m_plot2D.convertToPanelX(((Double)v.elementAt(1)).doubleValue());
1388      y1 = m_plot2D.convertToPanelY(((Double)v.elementAt(2)).doubleValue());
1389      x2 = m_plot2D.convertToPanelX(((Double)v.elementAt(3)).doubleValue());
1390      y2 = m_plot2D.convertToPanelY(((Double)v.elementAt(4)).doubleValue());
1391
1392      if (x1 < 0) {
1393        //test left
1394        new_coords = lineIntersect(x1, y1, x2, y2, 0, this.getHeight(), 0);
1395        edge1 = 0;
1396        if (new_coords[0] < 0) {
1397          //then not left
1398          if (y1 < 0) {
1399            //test top
1400            new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
1401            edge1 = 1;
1402          }
1403          else {
1404            //test bottom
1405            new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 
1406                                       this.getHeight());
1407            edge1 = 3;
1408          }
1409        }
1410      }
1411      else if (x1 > this.getWidth()) {
1412        //test right
1413        new_coords = lineIntersect(x1, y1, x2, y2, 0, this.getHeight(), 
1414                                   this.getWidth());
1415        edge1 = 2;
1416        if (new_coords[0] < 0) {
1417          //then not right
1418          if (y1 < 0) {
1419            //test top
1420            new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
1421            edge1 = 1;
1422          }
1423          else {
1424            //test bottom
1425            new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 
1426                                       this.getHeight());
1427            edge1 = 3;
1428          }
1429        }
1430      }
1431      else if (y1 < 0) {
1432        //test top
1433        new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
1434        edge1 = 1;
1435      }
1436      else {
1437        //test bottom
1438        new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 
1439                                   this.getHeight());
1440        edge1 = 3;
1441      }
1442     
1443      building.setElementAt(new 
1444        Double(m_plot2D.convertToAttribX(new_coords[0])), 1);
1445      building.setElementAt(new 
1446        Double(m_plot2D.convertToAttribY(new_coords[1])), 2);
1447
1448      x1 = m_plot2D.convertToPanelX(((Double)v.elementAt(v.size() - 4)).
1449                                    doubleValue());
1450      y1 = m_plot2D.convertToPanelY(((Double)v.elementAt(v.size() - 3)).
1451                                    doubleValue());
1452      x2 = m_plot2D.convertToPanelX(((Double)v.elementAt(v.size() - 6)).
1453                                    doubleValue());
1454      y2 = m_plot2D.convertToPanelY(((Double)v.elementAt(v.size() - 5)).
1455                                    doubleValue());
1456     
1457      if (x1 < 0) {
1458        //test left
1459        new_coords = lineIntersect(x1, y1, x2, y2, 0, this.getHeight(), 0);
1460        edge2 = 0;
1461        if (new_coords[0] < 0) {
1462          //then not left
1463          if (y1 < 0) {
1464            //test top
1465            new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
1466            edge2 = 1;
1467          }
1468          else {
1469            //test bottom
1470            new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 
1471                                       this.getHeight());
1472            edge2 = 3;
1473          }
1474        }
1475      }
1476      else if (x1 > this.getWidth()) {
1477        //test right
1478        new_coords = lineIntersect(x1, y1, x2, y2, 0, this.getHeight(), 
1479                                   this.getWidth());
1480        edge2 = 2;
1481        if (new_coords[0] < 0) {
1482          //then not right
1483          if (y1 < 0) {
1484            //test top
1485            new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
1486            edge2 = 1;
1487          }
1488          else {
1489            //test bottom
1490            new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 
1491                                       this.getHeight());
1492            edge2 = 3;
1493          }
1494        }
1495      }
1496      else if (y1 < 0) {
1497        //test top
1498        new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
1499        edge2 = 1;
1500      }
1501      else {
1502        //test bottom
1503        new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 
1504                                   this.getHeight());
1505        edge2 = 3;
1506      }
1507     
1508      building.setElementAt(new 
1509        Double(m_plot2D.convertToAttribX(new_coords[0])), building.size() - 2);
1510      building.setElementAt(new 
1511        Double(m_plot2D.convertToAttribY(new_coords[1])), building.size() - 1);
1512     
1513
1514      //trust me this complicated piece of code will
1515      //determine what points on the boundary of the view to add to the polygon
1516      int xp, yp;
1517
1518      xp = this.getWidth() * ((edge2 & 1) ^ ((edge2 & 2) / 2));
1519      yp = this.getHeight() * ((edge2 & 2) / 2);
1520      //System.out.println(((-1 + 4) % 4) + " hoi");
1521     
1522      if (inPolyline(v, m_plot2D.convertToAttribX(xp), 
1523                     m_plot2D.convertToAttribY(yp))) {
1524        //then add points in a clockwise direction
1525        building.addElement(new Double(m_plot2D.convertToAttribX(xp)));
1526        building.addElement(new Double(m_plot2D.convertToAttribY(yp)));
1527        for (int noa = (edge2 + 1) % 4; noa != edge1; noa = (noa + 1) % 4) {
1528          xp = this.getWidth() * ((noa & 1) ^ ((noa & 2) / 2));
1529          yp = this.getHeight() * ((noa & 2) / 2);
1530          building.addElement(new Double(m_plot2D.convertToAttribX(xp)));
1531          building.addElement(new Double(m_plot2D.convertToAttribY(yp)));
1532        }
1533      }
1534      else {
1535        xp = this.getWidth() * ((edge2 & 2) / 2);
1536        yp = this.getHeight() * (1 & ~((edge2 & 1) ^ ((edge2 & 2) / 2)));
1537        if (inPolyline(v, m_plot2D.convertToAttribX(xp), 
1538                       m_plot2D.convertToAttribY(yp))) {
1539          //then add points in anticlockwise direction
1540          building.addElement(new Double(m_plot2D.convertToAttribX(xp)));
1541          building.addElement(new Double(m_plot2D.convertToAttribY(yp)));
1542          for (int noa = (edge2 + 3) % 4; noa != edge1; noa = (noa + 3) % 4) {
1543            xp = this.getWidth() * ((noa & 2) / 2);
1544            yp = this.getHeight() * (1 & ~((noa & 1) ^ ((noa & 2) / 2)));
1545            building.addElement(new Double(m_plot2D.convertToAttribX(xp)));
1546            building.addElement(new Double(m_plot2D.convertToAttribY(yp)));
1547          }
1548        }
1549      }
1550      return building;
1551    }
1552
1553    /**
1554     * This will extract from a polygon shape its x coodrdinates
1555     * so that an awt.Polygon can be created.
1556     * @param v The polygon shape.
1557     * @return an int array containing the screen x coords for the polygon.
1558     */
1559    private int[] getXCoords(FastVector v) {
1560      int cach = (v.size() - 1) / 2;
1561      int[] ar = new int[cach];
1562      for (int noa = 0; noa < cach; noa ++) {
1563        ar[noa] = (int)m_plot2D.convertToPanelX(((Double)v.elementAt(noa * 2 +
1564                                                1)).doubleValue());
1565      }
1566      return ar;
1567    }
1568
1569    /**
1570     * This will extract from a polygon shape its y coordinates
1571     * so that an awt.Polygon can be created.
1572     * @param v The polygon shape.
1573     * @return an int array containing the screen y coords for the polygon.
1574     */
1575    private int[] getYCoords(FastVector v) {
1576      int cach = (v.size() - 1) / 2;
1577      int[] ar = new int[cach];
1578      for (int noa = 0; noa < cach; noa ++) {
1579        ar[noa] = (int)m_plot2D.
1580          convertToPanelY(((Double)v.elementAt(noa * 2 + 2)).
1581                          doubleValue());
1582      }
1583      return ar;
1584    }
1585   
1586    /**
1587     * Renders the polygons if necessary
1588     * @param gx the graphics context
1589     */
1590    public void prePlot(Graphics gx) {
1591      super.paintComponent(gx);
1592      if (m_plotInstances != null) {
1593        drawShapes(gx); // will be in paintComponent of ShapePlot2D
1594      }
1595    }
1596  }
1597
1598
1599
1600  /** default colours for colouring discrete class */
1601  protected Color [] m_DefaultColors = {Color.blue,
1602                                        Color.red,
1603                                        Color.green,
1604                                        Color.cyan,
1605                                        Color.pink,
1606                                        new Color(255, 0, 255),
1607                                        Color.orange,
1608                                        new Color(255, 0, 0),
1609                                        new Color(0, 255, 0),
1610                                        Color.white};
1611 
1612  /** Lets the user select the attribute for the x axis */
1613  protected JComboBox m_XCombo = new JComboBox();
1614
1615  /** Lets the user select the attribute for the y axis */
1616  protected JComboBox m_YCombo = new JComboBox();
1617
1618  /** Lets the user select the attribute to use for colouring */
1619  protected JComboBox m_ColourCombo = new JComboBox();
1620 
1621  /** Lets the user select the shape they want to create for instance
1622   * selection. */
1623  protected JComboBox m_ShapeCombo = new JComboBox();
1624
1625  /** Button for the user to enter the splits. */
1626  protected JButton m_submit = new JButton("Submit");
1627 
1628  /** Button for the user to remove all splits. */
1629  protected JButton m_cancel = new JButton("Clear");
1630
1631  /** Button for the user to open the visualized set of instances */
1632  protected JButton m_openBut = new JButton("Open");
1633
1634  /** Button for the user to save the visualized set of instances */
1635  protected JButton m_saveBut = new JButton("Save");
1636
1637  /** Stop the combos from growing out of control */
1638  private Dimension COMBO_SIZE = new Dimension(250, m_saveBut
1639                                               .getPreferredSize().height);
1640
1641  /** file chooser for saving instances */
1642  protected JFileChooser m_FileChooser
1643    = new JFileChooser(new File(System.getProperty("user.dir")));
1644
1645  /** Filter to ensure only arff files are selected */ 
1646  protected FileFilter m_ArffFilter =
1647    new ExtensionFileFilter(Instances.FILE_EXTENSION, "Arff data files");
1648
1649  /** Label for the jitter slider */
1650  protected JLabel m_JitterLab= new JLabel("Jitter",SwingConstants.RIGHT);
1651
1652  /** The jitter slider */
1653  protected JSlider m_Jitter = new JSlider(0,50,0);
1654
1655  /** The panel that displays the plot */
1656  protected PlotPanel m_plot = new PlotPanel();
1657
1658  /** The panel that displays the attributes , using color to represent
1659   * another attribute. */
1660  protected AttributePanel m_attrib = 
1661    new AttributePanel(m_plot.m_plot2D.getBackground());
1662
1663  /** The panel that displays legend info if there is more than one plot */
1664  protected LegendPanel m_legendPanel = new LegendPanel();
1665
1666  /** Panel that surrounds the plot panel with a titled border */
1667  protected JPanel m_plotSurround = new JPanel();
1668
1669  /** Panel that surrounds the class panel with a titled border */
1670  protected JPanel m_classSurround = new JPanel();
1671
1672  /** An optional listener that we will inform when ComboBox selections
1673      change */
1674  protected ActionListener listener = null;
1675
1676  /** An optional listener that we will inform when the user creates a
1677   * split to seperate instances. */
1678  protected VisualizePanelListener m_splitListener = null;
1679
1680  /** The name of the plot (not currently displayed, but can be used
1681      in the containing Frame or Panel) */
1682  protected String m_plotName = "";
1683
1684  /** The panel that displays the legend for the colouring attribute */
1685  protected ClassPanel m_classPanel = 
1686    new ClassPanel(m_plot.m_plot2D.getBackground());
1687 
1688  /** The list of the colors used */
1689  protected FastVector m_colorList;
1690
1691  /** These hold the names of preferred columns to visualize on---if the
1692      user has defined them in the Visualize.props file */
1693  protected String m_preferredXDimension = null;
1694  protected String m_preferredYDimension = null;
1695  protected String m_preferredColourDimension = null;
1696
1697  /** Show the attribute bar panel */
1698  protected boolean m_showAttBars = true;
1699 
1700  /** Show the class panel **/
1701  protected boolean m_showClassPanel = true;
1702
1703  /** the logger */
1704  protected Logger m_Log;
1705 
1706  /**
1707   * Sets the Logger to receive informational messages
1708   *
1709   * @param newLog the Logger that will now get info messages
1710   */
1711  public void setLog(Logger newLog) {
1712    m_Log = newLog;
1713  }
1714 
1715  /**
1716   * Set whether the attribute bars should be shown or not.
1717   * If turned off via this method then any setting in the
1718   * properties file (if exists) is ignored.
1719   *
1720   * @param sab false if attribute bars are not to be displayed.
1721   */
1722  public void setShowAttBars(boolean sab) {
1723    if (!sab && m_showAttBars) {
1724      m_plotSurround.remove(m_attrib);
1725    } else if (sab && !m_showAttBars) {
1726      GridBagConstraints constraints = new GridBagConstraints();
1727      constraints.insets = new Insets(0, 0, 0, 0);
1728      constraints.gridx=4;constraints.gridy=0;constraints.weightx=1;
1729      constraints.gridwidth=1;constraints.gridheight=1;constraints.weighty=5;
1730      m_plotSurround.add(m_attrib, constraints);
1731    }
1732    m_showAttBars = sab;
1733    repaint();
1734  }
1735 
1736  /**
1737   * Gets whether or not attribute bars are being displayed.
1738   *
1739   * @return true if attribute bars are being displayed.
1740   */
1741  public boolean getShowAttBars() {
1742    return m_showAttBars;
1743  }
1744 
1745  /**
1746   * Set whether the class panel should be shown or not.
1747   *
1748   * @param scp false if class panel is not to be displayed
1749   */
1750  public void setShowClassPanel(boolean scp) {
1751    if (!scp && m_showClassPanel) {
1752      remove(m_classSurround);
1753    } else if (scp && !m_showClassPanel) {
1754      add(m_classSurround, BorderLayout.SOUTH);
1755    }
1756    m_showClassPanel = scp;
1757    repaint();
1758  }
1759 
1760  /**
1761   * Gets whether or not the class panel is being displayed.
1762   *
1763   * @return true if the class panel is being displayed.
1764   */
1765  public boolean getShowClassPanel() {
1766    return m_showClassPanel;
1767  }
1768
1769  /**
1770   * This constructor allows a VisualizePanelListener to be set.
1771   *
1772   * @param ls          the listener to use
1773   */
1774  public VisualizePanel(VisualizePanelListener ls) {
1775    this();
1776    m_splitListener = ls;
1777  }
1778
1779  /**
1780   * Set the properties for the VisualizePanel
1781   *
1782   * @param relationName        the name of the relation, can be null
1783   */
1784  private void setProperties(String relationName) {
1785    if (VisualizeUtils.VISUALIZE_PROPERTIES != null) {
1786      String thisClass = this.getClass().getName();
1787      if (relationName == null) {
1788       
1789        String showAttBars = thisClass+".displayAttributeBars";
1790
1791        String val = VisualizeUtils.VISUALIZE_PROPERTIES.
1792          getProperty(showAttBars);
1793        if (val == null) {
1794          //System.err.println("Displaying attribute bars ");
1795//        m_showAttBars = true;
1796        } else {
1797          // only check if this hasn't been turned off programatically
1798          if (m_showAttBars) {
1799            if (val.compareTo("true") == 0 || val.compareTo("on") == 0) {
1800              //System.err.println("Displaying attribute bars ");
1801              m_showAttBars = true;
1802            } else {
1803              m_showAttBars = false;
1804            }
1805          }
1806        }
1807      } else {
1808        /*
1809        System.err.println("Looking for preferred visualization dimensions for "
1810                           +relationName);
1811        */
1812        String xcolKey = thisClass+"."+relationName+".XDimension";
1813        String ycolKey = thisClass+"."+relationName+".YDimension";
1814        String ccolKey = thisClass+"."+relationName+".ColourDimension";
1815     
1816        m_preferredXDimension = VisualizeUtils.VISUALIZE_PROPERTIES.
1817          getProperty(xcolKey);
1818        /*
1819        if (m_preferredXDimension == null) {
1820          System.err.println("No preferred X dimension found in "
1821                             +VisualizeUtils.PROPERTY_FILE
1822                             +" for "+xcolKey);
1823        } else {
1824          System.err.println("Setting preferred X dimension to "
1825                             +m_preferredXDimension);
1826                             }*/
1827        m_preferredYDimension = VisualizeUtils.VISUALIZE_PROPERTIES.
1828          getProperty(ycolKey);
1829        /*
1830        if (m_preferredYDimension == null) {
1831          System.err.println("No preferred Y dimension found in "
1832                             +VisualizeUtils.PROPERTY_FILE
1833                             +" for "+ycolKey);
1834        } else {
1835          System.err.println("Setting preferred dimension Y to "
1836                             +m_preferredYDimension);
1837                             }*/
1838        m_preferredColourDimension = VisualizeUtils.VISUALIZE_PROPERTIES.
1839          getProperty(ccolKey);
1840        /*
1841        if (m_preferredColourDimension == null) {
1842          System.err.println("No preferred Colour dimension found in "
1843                             +VisualizeUtils.PROPERTY_FILE
1844                             +" for "+ycolKey);
1845        } else {
1846          System.err.println("Setting preferred Colour dimension to "
1847                             +m_preferredColourDimension);
1848                             }*/
1849      }
1850    }
1851  }
1852
1853  /**
1854   * Constructor
1855   */
1856  public VisualizePanel() {
1857    super();
1858    setProperties(null);
1859    m_FileChooser.setFileFilter(m_ArffFilter);
1860    m_FileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
1861
1862    m_XCombo.setToolTipText("Select the attribute for the x axis");
1863    m_YCombo.setToolTipText("Select the attribute for the y axis");
1864    m_ColourCombo.setToolTipText("Select the attribute to colour on");
1865    m_ShapeCombo.setToolTipText("Select the shape to use for data selection"); 
1866
1867    m_XCombo.setPreferredSize(COMBO_SIZE);
1868    m_YCombo.setPreferredSize(COMBO_SIZE);
1869    m_ColourCombo.setPreferredSize(COMBO_SIZE);
1870    m_ShapeCombo.setPreferredSize(COMBO_SIZE);
1871
1872    m_XCombo.setMaximumSize(COMBO_SIZE);
1873    m_YCombo.setMaximumSize(COMBO_SIZE);
1874    m_ColourCombo.setMaximumSize(COMBO_SIZE);
1875    m_ShapeCombo.setMaximumSize(COMBO_SIZE);
1876   
1877    m_XCombo.setMinimumSize(COMBO_SIZE);
1878    m_YCombo.setMinimumSize(COMBO_SIZE);
1879    m_ColourCombo.setMinimumSize(COMBO_SIZE);
1880    m_ShapeCombo.setMinimumSize(COMBO_SIZE);
1881    //////////
1882    m_XCombo.setEnabled(false);
1883    m_YCombo.setEnabled(false);
1884    m_ColourCombo.setEnabled(false);
1885    m_ShapeCombo.setEnabled(false);
1886
1887    // tell the class panel and the legend panel that we want to know when
1888    // colours change
1889    m_classPanel.addRepaintNotify(this);
1890    m_legendPanel.addRepaintNotify(this);
1891   
1892    // Check the default colours against the background colour of the
1893    // plot panel. If any are equal to the background colour then
1894    // change them (so they are visible :-)
1895    for (int i = 0; i < m_DefaultColors.length; i++) {
1896      Color c = m_DefaultColors[i];
1897      if (c.equals(m_plot.m_plot2D.getBackground())) {
1898        int red = c.getRed();
1899        int blue = c.getBlue();
1900        int green = c.getGreen();
1901        red += (red < 128) ? (255 - red) / 2 : -(red / 2);
1902        blue += (blue < 128) ? (blue - red) / 2 : -(blue / 2);
1903        green += (green< 128) ? (255 - green) / 2 : -(green / 2);
1904        m_DefaultColors[i] = new Color(red, green, blue);
1905      }
1906    }
1907    m_classPanel.setDefaultColourList(m_DefaultColors);
1908    m_attrib.setDefaultColourList(m_DefaultColors);
1909
1910    m_colorList = new FastVector(10);
1911    for (int noa = m_colorList.size(); noa < 10; noa++) {
1912      Color pc = m_DefaultColors[noa % 10];
1913      int ija =  noa / 10;
1914      ija *= 2; 
1915      for (int j=0;j<ija;j++) {
1916        pc = pc.darker();
1917      }
1918     
1919      m_colorList.addElement(pc);
1920    }
1921    m_plot.setColours(m_colorList);
1922    m_classPanel.setColours(m_colorList);
1923    m_attrib.setColours(m_colorList);
1924    m_attrib.addAttributePanelListener(new AttributePanelListener() {
1925        public void attributeSelectionChange(AttributePanelEvent e) {
1926          if (e.m_xChange) {
1927            m_XCombo.setSelectedIndex(e.m_indexVal);
1928          } else {
1929            m_YCombo.setSelectedIndex(e.m_indexVal);
1930          }
1931        }
1932      });
1933   
1934    m_XCombo.addActionListener(new ActionListener() {
1935        public void actionPerformed(ActionEvent e) {
1936          int selected = m_XCombo.getSelectedIndex();
1937          if (selected < 0) {
1938            selected = 0;
1939          }
1940          m_plot.setXindex(selected);
1941         
1942          // try sending on the event if anyone is listening
1943          if (listener != null) {
1944            listener.actionPerformed(e);
1945          }
1946        }
1947      });
1948
1949    m_YCombo.addActionListener(new ActionListener() {
1950        public void actionPerformed(ActionEvent e) {
1951          int selected = m_YCombo.getSelectedIndex();
1952          if (selected < 0) {
1953            selected = 0;
1954          }
1955          m_plot.setYindex(selected);
1956         
1957          // try sending on the event if anyone is listening
1958          if (listener != null) {
1959            listener.actionPerformed(e);
1960          }
1961        }
1962      });
1963
1964    m_ColourCombo.addActionListener(new ActionListener() {
1965        public void actionPerformed(ActionEvent e) {
1966          int selected = m_ColourCombo.getSelectedIndex();
1967          if (selected < 0) {
1968            selected = 0;
1969          }
1970          m_plot.setCindex(selected);
1971
1972          if (listener != null) {
1973            listener.actionPerformed(e);
1974          }
1975        }
1976      });
1977   
1978    ///////
1979    m_ShapeCombo.addActionListener(new ActionListener() {
1980        public void actionPerformed(ActionEvent e) {
1981          int selected = m_ShapeCombo.getSelectedIndex();
1982          if (selected < 0) {
1983            selected = 0;
1984          }
1985          m_plot.setSindex(selected);
1986          // try sending on the event if anyone is listening
1987          if (listener != null) {
1988            listener.actionPerformed(e);
1989          }
1990        }
1991      });
1992
1993
1994    ///////////////////////////////////////
1995
1996    m_Jitter.addChangeListener(new ChangeListener() {
1997        public void stateChanged(ChangeEvent e) {
1998          m_plot.setJitter(m_Jitter.getValue());
1999        }
2000      });
2001
2002    m_openBut.setToolTipText("Loads previously saved instances from a file");
2003    m_openBut.addActionListener(new ActionListener() {
2004        public void actionPerformed(ActionEvent e) {
2005          openVisibleInstances();
2006        }
2007      });
2008   
2009    m_saveBut.setEnabled(false);
2010    m_saveBut.setToolTipText("Save the visible instances to a file");
2011    m_saveBut.addActionListener(new ActionListener() {
2012        public void actionPerformed(ActionEvent e) {
2013          saveVisibleInstances();
2014        }
2015      });
2016   
2017    JPanel combos = new JPanel();
2018    GridBagLayout gb = new GridBagLayout();
2019    GridBagConstraints constraints = new GridBagConstraints();
2020
2021
2022    m_XCombo.setLightWeightPopupEnabled(false);
2023    m_YCombo.setLightWeightPopupEnabled(false);
2024    m_ColourCombo.setLightWeightPopupEnabled(false);
2025    m_ShapeCombo.setLightWeightPopupEnabled(false);
2026    combos.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5));
2027
2028    combos.setLayout(gb);
2029    constraints.gridx=0;constraints.gridy=0;constraints.weightx=5;
2030    constraints.fill = GridBagConstraints.HORIZONTAL;
2031    constraints.gridwidth=2;constraints.gridheight=1;
2032    constraints.insets = new Insets(0,2,0,2);
2033    combos.add(m_XCombo,constraints);
2034    constraints.gridx=2;constraints.gridy=0;constraints.weightx=5;
2035    constraints.gridwidth=2;constraints.gridheight=1;
2036    combos.add(m_YCombo,constraints);
2037    constraints.gridx=0;constraints.gridy=1;constraints.weightx=5;
2038    constraints.gridwidth=2;constraints.gridheight=1;
2039    combos.add(m_ColourCombo,constraints);
2040    //
2041    constraints.gridx=2;constraints.gridy=1;constraints.weightx=5;
2042    constraints.gridwidth=2;constraints.gridheight=1;
2043    combos.add(m_ShapeCombo,constraints);
2044
2045   
2046    JPanel mbts = new JPanel();
2047    mbts.setLayout(new GridLayout(1,4));
2048    mbts.add(m_submit); mbts.add(m_cancel); mbts.add(m_openBut); mbts.add(m_saveBut);
2049
2050    constraints.gridx=0;constraints.gridy=2;constraints.weightx=5;
2051    constraints.gridwidth=2;constraints.gridheight=1;
2052    combos.add(mbts, constraints);
2053
2054    ////////////////////////////////
2055    constraints.gridx=2;constraints.gridy=2;constraints.weightx=5;
2056    constraints.gridwidth=1;constraints.gridheight=1;
2057    constraints.insets = new Insets(10,0,0,5);
2058    combos.add(m_JitterLab,constraints);
2059    constraints.gridx=3;constraints.gridy=2;
2060    constraints.weightx=5;
2061    constraints.insets = new Insets(10,0,0,0);
2062    combos.add(m_Jitter,constraints);
2063
2064    m_classSurround = new JPanel();
2065    m_classSurround.
2066      setBorder(BorderFactory.createTitledBorder("Class colour")); 
2067    m_classSurround.setLayout(new BorderLayout());
2068
2069    m_classPanel.setBorder(BorderFactory.createEmptyBorder(15,10,10,10));
2070    m_classSurround.add(m_classPanel, BorderLayout.CENTER);
2071
2072    GridBagLayout gb2 = new GridBagLayout();
2073    m_plotSurround.setBorder(BorderFactory.createTitledBorder("Plot"));
2074    m_plotSurround.setLayout(gb2);
2075
2076    constraints.fill = GridBagConstraints.BOTH;
2077    constraints.insets = new Insets(0, 0, 0, 10);
2078    constraints.gridx=0;constraints.gridy=0;constraints.weightx=3;
2079    constraints.gridwidth=4;constraints.gridheight=1;constraints.weighty=5;
2080    m_plotSurround.add(m_plot, constraints);
2081   
2082    if (m_showAttBars) {
2083      constraints.insets = new Insets(0, 0, 0, 0);
2084      constraints.gridx=4;constraints.gridy=0;constraints.weightx=1;
2085      constraints.gridwidth=1;constraints.gridheight=1;constraints.weighty=5;
2086      m_plotSurround.add(m_attrib, constraints);
2087    }
2088
2089    setLayout(new BorderLayout());
2090    add(combos, BorderLayout.NORTH);
2091    add(m_plotSurround, BorderLayout.CENTER);
2092    add(m_classSurround, BorderLayout.SOUTH);
2093   
2094    String [] SNames = new String [4];
2095    SNames[0] = "Select Instance";
2096    SNames[1] = "Rectangle";
2097    SNames[2] = "Polygon";
2098    SNames[3] = "Polyline";
2099
2100    m_ShapeCombo.setModel(new DefaultComboBoxModel(SNames));
2101    m_ShapeCombo.setEnabled(true);
2102  }
2103
2104  /**
2105   * displays the previously saved instances
2106   *
2107   * @param insts       the instances to display
2108   * @throws Exception  if display is not possible
2109   */
2110  protected void openVisibleInstances(Instances insts) throws Exception {
2111    PlotData2D tempd = new PlotData2D(insts);
2112    tempd.setPlotName(insts.relationName());
2113    tempd.addInstanceNumberAttribute();
2114    m_plot.m_plot2D.removeAllPlots();
2115    addPlot(tempd);
2116   
2117    // modify title
2118    Component parent = getParent();
2119    while (parent != null) {
2120      if (parent instanceof JFrame) {
2121        ((JFrame) parent).setTitle(
2122            "Weka Classifier Visualize: " 
2123            + insts.relationName() 
2124            + " (display only)");
2125        break;
2126      }
2127      else {
2128        parent = parent.getParent();
2129      }
2130    }
2131  }
2132 
2133  /**
2134   * Loads previously saved instances from a file
2135   */
2136  protected void openVisibleInstances() {
2137    try {
2138      int returnVal = m_FileChooser.showOpenDialog(this);
2139      if (returnVal == JFileChooser.APPROVE_OPTION) {
2140        File sFile = m_FileChooser.getSelectedFile();
2141        if (!sFile.getName().toLowerCase().
2142            endsWith(Instances.FILE_EXTENSION)) {
2143          sFile = new File(sFile.getParent(), sFile.getName() + Instances.FILE_EXTENSION);
2144        }
2145        File selected = sFile;
2146        Instances insts = new Instances(new BufferedReader(new FileReader(selected)));
2147        openVisibleInstances(insts);
2148      }
2149    } catch (Exception ex) {
2150      ex.printStackTrace();
2151      m_plot.m_plot2D.removeAllPlots();
2152      JOptionPane.showMessageDialog(
2153          this, 
2154          ex.getMessage(), 
2155          "Error loading file...", 
2156          JOptionPane.ERROR_MESSAGE);
2157    }
2158  }
2159
2160  /**
2161   * Save the currently visible set of instances to a file
2162   */
2163  private void saveVisibleInstances() {
2164    FastVector plots = m_plot.m_plot2D.getPlots();
2165    if (plots != null) {
2166      PlotData2D master = (PlotData2D)plots.elementAt(0);
2167      Instances saveInsts = new Instances(master.getPlotInstances());
2168      for (int i = 1; i < plots.size(); i++) {
2169        PlotData2D temp = (PlotData2D)plots.elementAt(i);
2170        Instances addInsts = temp.getPlotInstances();
2171        for (int j = 0; j < addInsts.numInstances(); j++) {
2172          saveInsts.add(addInsts.instance(j));
2173        }
2174      }
2175      try {
2176        int returnVal = m_FileChooser.showSaveDialog(this);
2177        if (returnVal == JFileChooser.APPROVE_OPTION) {
2178          File sFile = m_FileChooser.getSelectedFile();
2179          if (!sFile.getName().toLowerCase().
2180              endsWith(Instances.FILE_EXTENSION)) {
2181            sFile = new File(sFile.getParent(), sFile.getName() 
2182                             + Instances.FILE_EXTENSION);
2183          }
2184          File selected = sFile;
2185          Writer w = new BufferedWriter(new FileWriter(selected));
2186          w.write(saveInsts.toString());
2187          w.close();
2188        }
2189      } catch (Exception ex) {
2190        ex.printStackTrace();
2191      }
2192    }
2193  }
2194
2195
2196  /**
2197   * Sets the index used for colouring. If this method is called then
2198   * the supplied index is used and the combo box for selecting colouring
2199   * attribute is disabled.
2200   * @param index the index of the attribute to use for colouring
2201   */
2202  public void setColourIndex(int index) {
2203    if (index >= 0) {
2204      m_ColourCombo.setSelectedIndex(index);
2205    } else {
2206      m_ColourCombo.setSelectedIndex(0);
2207    }
2208    m_ColourCombo.setEnabled(false);
2209  }
2210 
2211 
2212  /**
2213   * Set the index of the attribute for the x axis
2214   * @param index the index for the x axis
2215   * @exception Exception if index is out of range.
2216   */
2217  public void setXIndex(int index) throws Exception {
2218    if (index >= 0 && index < m_XCombo.getItemCount()) {
2219      m_XCombo.setSelectedIndex(index);
2220    } else {
2221      throw new Exception("x index is out of range!");
2222    }
2223  }
2224
2225  /**
2226   * Get the index of the attribute on the x axis
2227   * @return the index of the attribute on the x axis
2228   */
2229  public int getXIndex() {
2230    return m_XCombo.getSelectedIndex();
2231  }
2232
2233  /**
2234   * Set the index of the attribute for the y axis
2235   * @param index the index for the y axis
2236   * @exception Exception if index is out of range.
2237   */
2238  public void setYIndex(int index) throws Exception {
2239    if (index >= 0 && index < m_YCombo.getItemCount()) {
2240      m_YCombo.setSelectedIndex(index);
2241    } else {
2242      throw new Exception("y index is out of range!");
2243    }
2244  }
2245 
2246  /**
2247   * Get the index of the attribute on the y axis
2248   * @return the index of the attribute on the x axis
2249   */
2250  public int getYIndex() {
2251    return m_YCombo.getSelectedIndex();
2252  }
2253
2254  /**
2255   * Get the index of the attribute selected for coloring
2256   * @return the index of the attribute on the x axis
2257   */
2258  public int getCIndex() {
2259    return m_ColourCombo.getSelectedIndex();
2260  }
2261
2262  /**
2263   * Get the index of the shape selected for creating splits.
2264   * @return The index of the shape.
2265   */
2266  public int getSIndex() {
2267    return m_ShapeCombo.getSelectedIndex();
2268  }
2269 
2270  /**
2271   * Set the shape for creating splits.
2272   * @param index The index of the shape.
2273   * @exception Exception if index is out of range.
2274   */
2275  public void setSIndex(int index) throws Exception {
2276    if (index >= 0 && index < m_ShapeCombo.getItemCount()) {
2277      m_ShapeCombo.setSelectedIndex(index);
2278    }
2279    else {
2280      throw new Exception("s index is out of range!");
2281    }
2282  }
2283
2284  /**
2285   * Add a listener for this visualize panel
2286   * @param act an ActionListener
2287   */
2288  public void addActionListener(ActionListener act) {
2289    listener = act;
2290  }
2291
2292  /**
2293   * Set a name for this plot
2294   * @param plotName the name for the plot
2295   */
2296  public void setName(String plotName) {
2297    m_plotName = plotName;
2298  }
2299
2300  /**
2301   * Returns the name associated with this plot. "" is returned if no
2302   * name is set.
2303   * @return the name of the plot
2304   */
2305  public String getName() {
2306    return m_plotName;
2307  }
2308
2309  /**
2310   * Get the master plot's instances
2311   * @return the master plot's instances
2312   */
2313  public Instances getInstances() {
2314    return m_plot.m_plotInstances;
2315  }
2316
2317  /**
2318   * Sets the Colors in use for a different attrib
2319   * if it is not a nominal attrib and or does not have
2320   * more possible values then this will do nothing.
2321   * otherwise it will add default colors to see that
2322   * there is a color for the attrib to begin with.
2323   * @param a The index of the attribute to color.
2324   * @param i The instances object that contains the attribute.
2325   */
2326  protected void newColorAttribute(int a, Instances i) {
2327    if (i.attribute(a).isNominal()) {
2328      for (int noa = m_colorList.size(); noa < i.attribute(a).numValues();
2329           noa++) {
2330        Color pc = m_DefaultColors[noa % 10];
2331        int ija =  noa / 10;
2332        ija *= 2; 
2333        for (int j=0;j<ija;j++) {
2334          pc = pc.brighter();
2335        }
2336       
2337        m_colorList.addElement(pc);
2338      }
2339      m_plot.setColours(m_colorList);
2340      m_attrib.setColours(m_colorList);
2341      m_classPanel.setColours(m_colorList);
2342    }
2343  }
2344
2345
2346  /**
2347   * This will set the shapes for the instances.
2348   * @param l A list of the shapes, providing that
2349   * the objects in the lists are non editable the data will be
2350   * kept intact.
2351   */
2352  public void setShapes(FastVector l) {
2353    m_plot.setShapes(l);
2354  }
2355
2356  /**
2357   * Tells the panel to use a new set of instances.
2358   * @param inst a set of Instances
2359   */
2360  public void setInstances(Instances inst) {
2361    if (inst.numAttributes() > 0 && inst.numInstances() > 0) {
2362      newColorAttribute(inst.numAttributes()-1, inst);
2363    }
2364
2365    PlotData2D temp = new PlotData2D(inst);
2366    temp.setPlotName(inst.relationName());
2367   
2368    try {
2369      setMasterPlot(temp);
2370    } catch (Exception ex) {
2371      System.err.println(ex);
2372      ex.printStackTrace();
2373    } 
2374  }
2375
2376  /**
2377   * initializes the comboboxes based on the data
2378   *
2379   * @param inst        the data to base the combobox-setup on
2380   */
2381  public void setUpComboBoxes(Instances inst) {
2382    setProperties(inst.relationName());
2383    int prefX = -1;
2384    int prefY = -1;
2385    if (inst.numAttributes() > 1) {
2386      prefY = 1;
2387    }
2388    int prefC = -1;
2389    String [] XNames = new String [inst.numAttributes()];
2390    String [] YNames = new String [inst.numAttributes()];
2391    String [] CNames = new String [inst.numAttributes()];
2392    for (int i = 0; i < XNames.length; i++) {
2393      String type = "";
2394      switch (inst.attribute(i).type()) {
2395      case Attribute.NOMINAL:
2396        type = " (Nom)";
2397        break;
2398      case Attribute.NUMERIC:
2399        type = " (Num)";
2400        break;
2401      case Attribute.STRING:
2402        type = " (Str)";
2403        break;
2404      case Attribute.DATE:
2405        type = " (Dat)";
2406        break;
2407      case Attribute.RELATIONAL:
2408        type = " (Rel)";
2409        break;
2410      default:
2411        type = " (???)";
2412      }
2413      XNames[i] = "X: "+ inst.attribute(i).name()+type;
2414      YNames[i] = "Y: "+ inst.attribute(i).name()+type;
2415      CNames[i] = "Colour: "+ inst.attribute(i).name()+type;
2416      if (m_preferredXDimension != null) {
2417        if (m_preferredXDimension.compareTo(inst.attribute(i).name()) == 0) {
2418          prefX = i;
2419          //System.err.println("Found preferred X dimension");
2420        }
2421      }
2422      if (m_preferredYDimension != null) {
2423        if (m_preferredYDimension.compareTo(inst.attribute(i).name()) == 0) {
2424          prefY = i;
2425          //System.err.println("Found preferred Y dimension");
2426        }
2427      }
2428      if (m_preferredColourDimension != null) {
2429        if (m_preferredColourDimension.
2430            compareTo(inst.attribute(i).name()) == 0) {
2431          prefC = i;
2432          //System.err.println("Found preferred Colour dimension");
2433        }
2434      }
2435    }
2436    m_XCombo.setModel(new DefaultComboBoxModel(XNames));
2437    m_YCombo.setModel(new DefaultComboBoxModel(YNames));
2438
2439    m_ColourCombo.setModel(new DefaultComboBoxModel(CNames));
2440    //m_ShapeCombo.setModel(new DefaultComboBoxModel(SNames));
2441    //m_ShapeCombo.setEnabled(true);
2442    m_XCombo.setEnabled(true);
2443    m_YCombo.setEnabled(true);
2444   
2445    if (m_splitListener == null) {
2446      m_ColourCombo.setEnabled(true);
2447      m_ColourCombo.setSelectedIndex(inst.numAttributes()-1);
2448    }
2449    m_plotSurround.setBorder((BorderFactory.createTitledBorder("Plot: "
2450                              +inst.relationName())));
2451    try {
2452      if (prefX != -1) {
2453        setXIndex(prefX);
2454      }
2455      if (prefY != -1) {
2456        setYIndex(prefY);
2457      }
2458      if (prefC != -1) {
2459        m_ColourCombo.setSelectedIndex(prefC);
2460      }
2461    } catch (Exception ex) {
2462      System.err.println("Problem setting preferred Visualization dimensions");
2463    }
2464  }
2465
2466  /**
2467   * Removes all the plots.
2468   */
2469  public void removeAllPlots() {
2470    m_plot.removeAllPlots();
2471  }
2472 
2473  /**
2474   * Set the master plot for the visualize panel
2475   * @param newPlot the new master plot
2476   * @exception Exception if the master plot could not be set
2477   */
2478  public void setMasterPlot(PlotData2D newPlot) throws Exception {
2479    m_plot.setMasterPlot(newPlot);
2480    setUpComboBoxes(newPlot.m_plotInstances);
2481    m_saveBut.setEnabled(true);
2482    repaint();
2483  }
2484
2485  /**
2486   * Set a new plot to the visualize panel
2487   * @param newPlot the new plot to add
2488   * @exception Exception if the plot could not be added
2489   */
2490  public void addPlot(PlotData2D newPlot) throws Exception {
2491    m_plot.addPlot(newPlot);
2492    if (m_plot.m_plot2D.getMasterPlot() != null) {
2493      setUpComboBoxes(newPlot.m_plotInstances);
2494    }
2495    m_saveBut.setEnabled(true);
2496    repaint();
2497  }
2498 
2499  /**
2500   * Returns the underlying plot panel.
2501   *
2502   * @return            the plot panel
2503   */
2504  public PlotPanel getPlotPanel() {
2505    return m_plot;
2506  }
2507
2508  /**
2509   * Main method for testing this class
2510   *
2511   * @param args        the commandline parameters
2512   */
2513  public static void main(String [] args) {
2514    try {
2515      if (args.length < 1) {
2516        System.err.println("Usage : weka.gui.visualize.VisualizePanel "
2517                           +"<dataset> [<dataset> <dataset>...]");
2518        System.exit(1);
2519      }
2520
2521      weka.core.logging.Logger.log(weka.core.logging.Logger.Level.INFO, "Logging started");
2522      final javax.swing.JFrame jf = 
2523        new javax.swing.JFrame("Weka Explorer: Visualize");
2524      jf.setSize(500,400);
2525      jf.getContentPane().setLayout(new BorderLayout());
2526      final VisualizePanel sp = new VisualizePanel();
2527     
2528      jf.getContentPane().add(sp, BorderLayout.CENTER);
2529      jf.addWindowListener(new java.awt.event.WindowAdapter() {
2530        public void windowClosing(java.awt.event.WindowEvent e) {
2531          jf.dispose();
2532          System.exit(0);
2533        }
2534      });
2535
2536      jf.setVisible(true);
2537      if (args.length >= 1) {
2538        for (int j = 0; j < args.length; j++) {
2539          System.err.println("Loading instances from " + args[j]);
2540          java.io.Reader r = new java.io.BufferedReader(
2541                             new java.io.FileReader(args[j]));
2542          Instances i = new Instances(r);
2543          i.setClassIndex(i.numAttributes()-1);
2544          PlotData2D pd1 = new PlotData2D(i);
2545         
2546          if (j == 0) {
2547            pd1.setPlotName("Master plot");
2548            sp.setMasterPlot(pd1);
2549          } else {
2550            pd1.setPlotName("Plot "+(j+1));
2551            pd1.m_useCustomColour = true;
2552            pd1.m_customColour = (j % 2 == 0) ? Color.red : Color.blue; 
2553            sp.addPlot(pd1);
2554          }
2555        }
2556      }
2557    } catch (Exception ex) {
2558      ex.printStackTrace();
2559      System.err.println(ex.getMessage());
2560    }
2561  }
2562}
Note: See TracBrowser for help on using the repository browser.