weka.gui.visualize.VisualizePanel.java Source code

Java tutorial

Introduction

Here is the source code for weka.gui.visualize.VisualizePanel.java

Source

/*
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 *    VisualizePanel.java
 *    Copyright (C) 1999-2012 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.gui.visualize;

import weka.core.Attribute;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.Settings;
import weka.gui.ExtensionFileFilter;
import weka.gui.Logger;
import weka.gui.WekaFileChooser;

import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Random;

/**
 * This panel allows the user to visualize a dataset (and if provided) a
 * classifier's/clusterer's predictions in two dimensions.
 * 
 * If the user selects a nominal attribute as the colouring attribute then each
 * point is drawn in a colour that corresponds to the discrete value of that
 * attribute for the instance. If the user selects a numeric attribute to colour
 * on, then the points are coloured using a spectrum ranging from blue to red
 * (low values to high).
 * 
 * When a classifier's predictions are supplied they are plotted in one of two
 * ways (depending on whether the class is nominal or numeric).<br>
 * For nominal class: an error made by a classifier is plotted as a square in
 * the colour corresponding to the class it predicted.<br>
 * For numeric class: predictions are plotted as varying sized x's, where the
 * size of the x is related to the magnitude of the error.
 * 
 * @author Mark Hall (mhall@cs.waikato.ac.nz)
 * @author Malcolm Ware (mfw4@cs.waikato.ac.nz)
 * @version $Revision$
 */
public class VisualizePanel extends PrintablePanel {

    /** for serialization */
    private static final long serialVersionUID = 240108358588153943L;

    /** Inner class to handle plotting */
    protected class PlotPanel extends PrintablePanel implements Plot2DCompanion {

        /** for serialization */
        private static final long serialVersionUID = -4823674171136494204L;

        /** The actual generic plotting panel */
        protected Plot2D m_plot2D = new Plot2D();

        /** The instances from the master plot */
        protected Instances m_plotInstances = null;

        /** The master plot */
        protected PlotData2D m_originalPlot = null;

        /**
         * Indexes of the attributes to go on the x and y axis and the attribute to
         * use for colouring and the current shape for drawing
         */
        protected int m_xIndex = 0;
        protected int m_yIndex = 0;
        protected int m_cIndex = 0;
        protected int m_sIndex = 0;

        /** the offsets of the axes once label metrics are calculated */
        /*
         * private int m_XaxisStart=0; NOT USED private int m_YaxisStart=0; private
         * int m_XaxisEnd=0; private int m_YaxisEnd=0;
         */

        /** True if the user is currently dragging a box. */
        private boolean m_createShape;

        /** contains all the shapes that have been drawn for these attribs */
        private ArrayList<ArrayList<Double>> m_shapes;

        /** contains the points of the shape currently being drawn. */
        private ArrayList<Double> m_shapePoints;

        /** contains the position of the mouse (used for rubberbanding). */
        private final Dimension m_newMousePos;

        /** Constructor */
        public PlotPanel() {
            this.setBackground(m_plot2D.getBackground());
            this.setLayout(new BorderLayout());
            this.add(m_plot2D, BorderLayout.CENTER);
            m_plot2D.setPlotCompanion(this);

            m_createShape = false;
            m_shapes = null;// //
            m_shapePoints = null;
            m_newMousePos = new Dimension();

            this.addMouseListener(new MouseAdapter() {
                // /////
                @Override
                public void mousePressed(MouseEvent e) {
                    if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK) {
                        //
                        if (m_sIndex == 0) {
                            // do nothing it will get dealt to in the clicked method
                        } else if (m_sIndex == 1) {
                            m_createShape = true;
                            m_shapePoints = new ArrayList<Double>(5);
                            m_shapePoints.add(new Double(m_sIndex));
                            m_shapePoints.add(new Double(e.getX()));
                            m_shapePoints.add(new Double(e.getY()));
                            m_shapePoints.add(new Double(e.getX()));
                            m_shapePoints.add(new Double(e.getY()));
                            // Graphics g = PlotPanel.this.getGraphics();
                            Graphics g = m_plot2D.getGraphics();
                            g.setColor(Color.black);
                            g.setXORMode(Color.white);
                            g.drawRect(m_shapePoints.get(1).intValue(), m_shapePoints.get(2).intValue(),
                                    m_shapePoints.get(3).intValue() - m_shapePoints.get(1).intValue(),
                                    m_shapePoints.get(4).intValue() - m_shapePoints.get(2).intValue());
                            g.dispose();
                        }
                        // System.out.println("clicked");
                    }
                    // System.out.println("clicked");
                }

                // ////
                @Override
                public void mouseClicked(MouseEvent e) {

                    if ((m_sIndex == 2 || m_sIndex == 3) && (m_createShape
                            || (e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK)) {
                        if (m_createShape) {
                            // then it has been started already.

                            Graphics g = m_plot2D.getGraphics();
                            g.setColor(Color.black);
                            g.setXORMode(Color.white);
                            if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK
                                    && !e.isAltDown()) {
                                m_shapePoints.add(new Double(m_plot2D.convertToAttribX(e.getX())));

                                m_shapePoints.add(new Double(m_plot2D.convertToAttribY(e.getY())));

                                m_newMousePos.width = e.getX();
                                m_newMousePos.height = e.getY();
                                g.drawLine(
                                        (int) Math.ceil(m_plot2D.convertToPanelX(
                                                m_shapePoints.get(m_shapePoints.size() - 2).doubleValue())),

                                        (int) Math.ceil(m_plot2D.convertToPanelY(
                                                m_shapePoints.get(m_shapePoints.size() - 1).doubleValue())),
                                        m_newMousePos.width, m_newMousePos.height);

                            } else if (m_sIndex == 3) {
                                // then extend the lines to infinity
                                // (100000 or so should be enough).
                                // the area is selected by where the user right clicks
                                // the mouse button

                                m_createShape = false;
                                if (m_shapePoints.size() >= 5) {
                                    double cx = Math.ceil(m_plot2D.convertToPanelX(
                                            m_shapePoints.get(m_shapePoints.size() - 4).doubleValue()));

                                    double cx2 = Math
                                            .ceil(m_plot2D.convertToPanelX(
                                                    m_shapePoints.get(m_shapePoints.size() - 2).doubleValue()))
                                            - cx;

                                    cx2 *= 50000;

                                    double cy = Math.ceil(m_plot2D.convertToPanelY(
                                            m_shapePoints.get(m_shapePoints.size() - 3).doubleValue()));
                                    double cy2 = Math
                                            .ceil(m_plot2D.convertToPanelY(
                                                    m_shapePoints.get(m_shapePoints.size() - 1).doubleValue()))
                                            - cy;
                                    cy2 *= 50000;

                                    double cxa = Math
                                            .ceil(m_plot2D.convertToPanelX(m_shapePoints.get(3).doubleValue()));
                                    double cxa2 = Math.ceil(
                                            m_plot2D.convertToPanelX(m_shapePoints.get(1).doubleValue())) - cxa;
                                    cxa2 *= 50000;

                                    double cya = Math
                                            .ceil(m_plot2D.convertToPanelY(m_shapePoints.get(4).doubleValue()));
                                    double cya2 = Math.ceil(
                                            m_plot2D.convertToPanelY(m_shapePoints.get(2).doubleValue())) - cya;

                                    cya2 *= 50000;

                                    m_shapePoints.set(1, new Double(m_plot2D.convertToAttribX(cxa2 + cxa)));

                                    m_shapePoints.set(m_shapePoints.size() - 1,
                                            new Double(m_plot2D.convertToAttribY(cy2 + cy)));

                                    m_shapePoints.set(m_shapePoints.size() - 2,
                                            new Double(m_plot2D.convertToAttribX(cx2 + cx)));

                                    m_shapePoints.set(2, new Double(m_plot2D.convertToAttribY(cya2 + cya)));

                                    // determine how infinity line should be built

                                    cy = Double.POSITIVE_INFINITY;
                                    cy2 = Double.NEGATIVE_INFINITY;
                                    if (m_shapePoints.get(1).doubleValue() > m_shapePoints.get(3).doubleValue()) {
                                        if (m_shapePoints.get(2).doubleValue() == m_shapePoints.get(4)
                                                .doubleValue()) {
                                            cy = m_shapePoints.get(2).doubleValue();
                                        }
                                    }
                                    if (m_shapePoints.get(m_shapePoints.size() - 2).doubleValue() > m_shapePoints
                                            .get(m_shapePoints.size() - 4).doubleValue()) {
                                        if (m_shapePoints.get(m_shapePoints.size() - 3)
                                                .doubleValue() == m_shapePoints.get(m_shapePoints.size() - 1)
                                                        .doubleValue()) {
                                            cy2 = m_shapePoints.get(m_shapePoints.size() - 1).doubleValue();
                                        }
                                    }
                                    m_shapePoints.add(new Double(cy));
                                    m_shapePoints.add(new Double(cy2));

                                    if (!inPolyline(m_shapePoints, m_plot2D.convertToAttribX(e.getX()),
                                            m_plot2D.convertToAttribY(e.getY()))) {
                                        Double tmp = m_shapePoints.get(m_shapePoints.size() - 2);
                                        m_shapePoints.set(m_shapePoints.size() - 2,
                                                m_shapePoints.get(m_shapePoints.size() - 1));
                                        m_shapePoints.set(m_shapePoints.size() - 1, tmp);
                                    }

                                    if (m_shapes == null) {
                                        m_shapes = new ArrayList<ArrayList<Double>>(4);
                                    }
                                    m_shapes.add(m_shapePoints);

                                    m_submit.setText("Submit");
                                    m_submit.setActionCommand("Submit");

                                    m_submit.setEnabled(true);
                                }

                                m_shapePoints = null;
                                PlotPanel.this.repaint();

                            } else {
                                // then close the shape
                                m_createShape = false;
                                if (m_shapePoints.size() >= 7) {
                                    m_shapePoints.add(m_shapePoints.get(1));
                                    m_shapePoints.add(m_shapePoints.get(2));
                                    if (m_shapes == null) {
                                        m_shapes = new ArrayList<ArrayList<Double>>(4);
                                    }
                                    m_shapes.add(m_shapePoints);

                                    m_submit.setText("Submit");
                                    m_submit.setActionCommand("Submit");

                                    m_submit.setEnabled(true);
                                }
                                m_shapePoints = null;
                                PlotPanel.this.repaint();
                            }
                            g.dispose();
                            // repaint();
                        } else if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK) {
                            // then this is the first point
                            m_createShape = true;
                            m_shapePoints = new ArrayList<Double>(17);
                            m_shapePoints.add(new Double(m_sIndex));
                            m_shapePoints.add(new Double(m_plot2D.convertToAttribX(e.getX()))); // the
                                                                                                // new
                                                                                                // point
                            m_shapePoints.add(new Double(m_plot2D.convertToAttribY(e.getY())));
                            m_newMousePos.width = e.getX(); // the temp mouse point
                            m_newMousePos.height = e.getY();

                            Graphics g = m_plot2D.getGraphics();
                            g.setColor(Color.black);
                            g.setXORMode(Color.white);
                            g.drawLine(
                                    (int) Math.ceil(m_plot2D.convertToPanelX(m_shapePoints.get(1).doubleValue())),
                                    (int) Math.ceil(m_plot2D.convertToPanelY(m_shapePoints.get(2).doubleValue())),
                                    m_newMousePos.width, m_newMousePos.height);
                            g.dispose();
                        }
                    } else {
                        if ((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) {

                            m_plot2D.searchPoints(e.getX(), e.getY(), false);
                        } else {
                            m_plot2D.searchPoints(e.getX(), e.getY(), true);
                        }
                    }
                }

                // ///////
                @Override
                public void mouseReleased(MouseEvent e) {

                    if (m_createShape) {
                        if (m_shapePoints.get(0).intValue() == 1) {
                            m_createShape = false;
                            Graphics g = m_plot2D.getGraphics();
                            g.setColor(Color.black);
                            g.setXORMode(Color.white);
                            g.drawRect(m_shapePoints.get(1).intValue(), m_shapePoints.get(2).intValue(),
                                    m_shapePoints.get(3).intValue() - m_shapePoints.get(1).intValue(),
                                    m_shapePoints.get(4).intValue() - m_shapePoints.get(2).intValue());

                            g.dispose();
                            if (checkPoints(m_shapePoints.get(1).doubleValue(), m_shapePoints.get(2).doubleValue())
                                    && checkPoints(m_shapePoints.get(3).doubleValue(),
                                            m_shapePoints.get(4).doubleValue())) {
                                // then the points all land on the screen
                                // now do special check for the rectangle
                                if (m_shapePoints.get(1).doubleValue() < m_shapePoints.get(3).doubleValue()
                                        && m_shapePoints.get(2).doubleValue() < m_shapePoints.get(4)
                                                .doubleValue()) {
                                    // then the rectangle is valid
                                    if (m_shapes == null) {
                                        m_shapes = new ArrayList<ArrayList<Double>>(2);
                                    }
                                    m_shapePoints.set(1, new Double(
                                            m_plot2D.convertToAttribX(m_shapePoints.get(1).doubleValue())));
                                    m_shapePoints.set(2, new Double(
                                            m_plot2D.convertToAttribY(m_shapePoints.get(2).doubleValue())));
                                    m_shapePoints.set(3, new Double(
                                            m_plot2D.convertToAttribX(m_shapePoints.get(3).doubleValue())));
                                    m_shapePoints.set(4, new Double(
                                            m_plot2D.convertToAttribY(m_shapePoints.get(4).doubleValue())));

                                    m_shapes.add(m_shapePoints);

                                    m_submit.setText("Submit");
                                    m_submit.setActionCommand("Submit");

                                    m_submit.setEnabled(true);

                                    PlotPanel.this.repaint();
                                }
                            }
                            m_shapePoints = null;
                        }
                    }
                }
            });

            this.addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    // check if the user is dragging a box
                    if (m_createShape) {
                        if (m_shapePoints.get(0).intValue() == 1) {
                            Graphics g = m_plot2D.getGraphics();
                            g.setColor(Color.black);
                            g.setXORMode(Color.white);
                            g.drawRect(m_shapePoints.get(1).intValue(), m_shapePoints.get(2).intValue(),
                                    m_shapePoints.get(3).intValue() - m_shapePoints.get(1).intValue(),
                                    m_shapePoints.get(4).intValue() - m_shapePoints.get(2).intValue());

                            m_shapePoints.set(3, new Double(e.getX()));
                            m_shapePoints.set(4, new Double(e.getY()));

                            g.drawRect(m_shapePoints.get(1).intValue(), m_shapePoints.get(2).intValue(),
                                    m_shapePoints.get(3).intValue() - m_shapePoints.get(1).intValue(),
                                    m_shapePoints.get(4).intValue() - m_shapePoints.get(2).intValue());
                            g.dispose();
                        }
                    }
                }

                @Override
                public void mouseMoved(MouseEvent e) {
                    if (m_createShape) {
                        if (m_shapePoints.get(0).intValue() == 2 || m_shapePoints.get(0).intValue() == 3) {
                            Graphics g = m_plot2D.getGraphics();
                            g.setColor(Color.black);
                            g.setXORMode(Color.white);
                            g.drawLine(
                                    (int) Math.ceil(m_plot2D.convertToPanelX(
                                            m_shapePoints.get(m_shapePoints.size() - 2).doubleValue())),
                                    (int) Math.ceil(m_plot2D.convertToPanelY(
                                            m_shapePoints.get(m_shapePoints.size() - 1).doubleValue())),
                                    m_newMousePos.width, m_newMousePos.height);

                            m_newMousePos.width = e.getX();
                            m_newMousePos.height = e.getY();

                            g.drawLine(
                                    (int) Math.ceil(m_plot2D.convertToPanelX(
                                            m_shapePoints.get(m_shapePoints.size() - 2).doubleValue())),
                                    (int) Math.ceil(m_plot2D.convertToPanelY(
                                            m_shapePoints.get(m_shapePoints.size() - 1).doubleValue())),
                                    m_newMousePos.width, m_newMousePos.height);
                            g.dispose();
                        }
                    }
                }
            });

            m_submit.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {

                    if (e.getActionCommand().equals("Submit")) {
                        if (m_splitListener != null && m_shapes != null) {
                            // then send the split to the listener
                            Instances sub_set1 = new Instances(m_plot2D.getMasterPlot().m_plotInstances, 500);
                            Instances sub_set2 = new Instances(m_plot2D.getMasterPlot().m_plotInstances, 500);

                            if (m_plot2D.getMasterPlot().m_plotInstances != null) {

                                for (int noa = 0; noa < m_plot2D.getMasterPlot().m_plotInstances
                                        .numInstances(); noa++) {
                                    if (!m_plot2D.getMasterPlot().m_plotInstances.instance(noa).isMissing(m_xIndex)
                                            && !m_plot2D.getMasterPlot().m_plotInstances.instance(noa)
                                                    .isMissing(m_yIndex)) {

                                        if (inSplit(m_plot2D.getMasterPlot().m_plotInstances.instance(noa))) {
                                            sub_set1.add(m_plot2D.getMasterPlot().m_plotInstances.instance(noa));
                                        } else {
                                            sub_set2.add(m_plot2D.getMasterPlot().m_plotInstances.instance(noa));
                                        }
                                    }
                                }
                                ArrayList<ArrayList<Double>> tmp = m_shapes;
                                cancelShapes();
                                m_splitListener.userDataEvent(
                                        new VisualizePanelEvent(tmp, sub_set1, sub_set2, m_xIndex, m_yIndex));
                            }
                        } else if (m_shapes != null && m_plot2D.getMasterPlot().m_plotInstances != null) {
                            Instances sub_set1 = new Instances(m_plot2D.getMasterPlot().m_plotInstances, 500);
                            int count = 0;
                            for (int noa = 0; noa < m_plot2D.getMasterPlot().m_plotInstances
                                    .numInstances(); noa++) {
                                if (inSplit(m_plot2D.getMasterPlot().m_plotInstances.instance(noa))) {
                                    sub_set1.add(m_plot2D.getMasterPlot().m_plotInstances.instance(noa));
                                    count++;
                                }

                            }

                            int[] nSizes = null;
                            int[] nTypes = null;
                            boolean[] connect = null;
                            int x = m_xIndex;
                            int y = m_yIndex;

                            if (m_originalPlot == null) {
                                // this sets these instances as the instances
                                // to go back to.
                                m_originalPlot = m_plot2D.getMasterPlot();
                            }

                            if (count > 0) {
                                nTypes = new int[count];
                                nSizes = new int[count];
                                connect = new boolean[count];
                                count = 0;
                                for (int noa = 0; noa < m_plot2D.getMasterPlot().m_plotInstances
                                        .numInstances(); noa++) {
                                    if (inSplit(m_plot2D.getMasterPlot().m_plotInstances.instance(noa))) {

                                        nTypes[count] = m_plot2D.getMasterPlot().m_shapeType[noa];
                                        nSizes[count] = m_plot2D.getMasterPlot().m_shapeSize[noa];
                                        connect[count] = m_plot2D.getMasterPlot().m_connectPoints[noa];
                                        count++;
                                    }
                                }
                            }
                            cancelShapes();

                            PlotData2D newPlot = new PlotData2D(sub_set1);

                            try {
                                newPlot.setShapeSize(nSizes);
                                newPlot.setShapeType(nTypes);
                                newPlot.setConnectPoints(connect);

                                m_plot2D.removeAllPlots();

                                VisualizePanel.this.addPlot(newPlot);
                            } catch (Exception ex) {
                                System.err.println(ex);
                                ex.printStackTrace();
                            }

                            try {
                                VisualizePanel.this.setXIndex(x);
                                VisualizePanel.this.setYIndex(y);
                            } catch (Exception er) {
                                System.out.println("Error : " + er);
                                // System.out.println("Part of user input so had to" +
                                // " catch here");
                            }
                        }
                    } else if (e.getActionCommand().equals("Reset")) {
                        int x = m_xIndex;
                        int y = m_yIndex;

                        m_plot2D.removeAllPlots();
                        try {
                            VisualizePanel.this.addPlot(m_originalPlot);
                        } catch (Exception ex) {
                            System.err.println(ex);
                            ex.printStackTrace();
                        }

                        try {
                            VisualizePanel.this.setXIndex(x);
                            VisualizePanel.this.setYIndex(y);
                        } catch (Exception er) {
                            System.out.println("Error : " + er);
                        }
                    }
                }
            });

            m_cancel.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    cancelShapes();
                    PlotPanel.this.repaint();
                }
            });
            // //////////
        }

        /**
         * Apply settings
         * 
         * @param settings the settings to apply
         * @param ownerID the ID of the owner perspective, panel etc. This key is
         *          used when looking up our settings
         */
        protected void applySettings(Settings settings, String ownerID) {
            m_plot2D.applySettings(settings, ownerID);
            setBackground(m_plot2D.getBackground());
            repaint();
        }

        /**
         * Removes all the plots.
         */
        public void removeAllPlots() {
            m_plot2D.removeAllPlots();
            m_legendPanel.setPlotList(m_plot2D.getPlots());
        }

        /**
         * @return The FastVector containing all the shapes.
         */
        public ArrayList<ArrayList<Double>> getShapes() {

            return m_shapes;
        }

        /**
         * Sets the list of shapes to empty and also cancels the current shape being
         * drawn (if applicable).
         */
        public void cancelShapes() {

            if (m_splitListener == null) {
                m_submit.setText("Reset");
                m_submit.setActionCommand("Reset");

                if (m_originalPlot == null || m_originalPlot.m_plotInstances == m_plotInstances) {
                    m_submit.setEnabled(false);
                } else {
                    m_submit.setEnabled(true);
                }
            } else {
                m_submit.setEnabled(false);
            }

            m_createShape = false;
            m_shapePoints = null;
            m_shapes = null;
            this.repaint();
        }

        /**
         * This can be used to set the shapes that should appear.
         * 
         * @param v The list of shapes.
         */
        public void setShapes(ArrayList<ArrayList<Double>> v) {
            // note that this method should be fine for doubles,
            // but anything else that uses something other than doubles
            // (or uneditable objects) could have unsafe copies.
            if (v != null) {
                ArrayList<Double> temp;
                m_shapes = new ArrayList<ArrayList<Double>>(v.size());
                for (int noa = 0; noa < v.size(); noa++) {
                    temp = new ArrayList<Double>(v.get(noa).size());
                    m_shapes.add(temp);
                    for (int nob = 0; nob < v.get(noa).size(); nob++) {

                        temp.add(v.get(noa).get(nob));

                    }
                }
            } else {
                m_shapes = null;
            }
            this.repaint();
        }

        /**
         * This will check the values of the screen points passed and make sure that
         * they land on the screen
         * 
         * @param x1 The x coord.
         * @param y1 The y coord.
         * @return true if the point would land on the screen
         */
        private boolean checkPoints(double x1, double y1) {
            if (x1 < 0 || x1 > this.getSize().width || y1 < 0 || y1 > this.getSize().height) {
                return false;
            }
            return true;
        }

        /**
         * This will check if an instance is inside or outside of the current
         * shapes.
         * 
         * @param i The instance to check.
         * @return True if 'i' falls inside the shapes, false otherwise.
         */
        public boolean inSplit(Instance i) {
            // this will check if the instance lies inside the shapes or not

            if (m_shapes != null) {
                ArrayList<Double> stmp;
                double x1, y1, x2, y2;
                for (int noa = 0; noa < m_shapes.size(); noa++) {
                    stmp = m_shapes.get(noa);
                    if (stmp.get(0).intValue() == 1) {
                        // then rectangle
                        x1 = stmp.get(1).doubleValue();
                        y1 = stmp.get(2).doubleValue();
                        x2 = stmp.get(3).doubleValue();
                        y2 = stmp.get(4).doubleValue();
                        if (i.value(m_xIndex) >= x1 && i.value(m_xIndex) <= x2 && i.value(m_yIndex) <= y1
                                && i.value(m_yIndex) >= y2) {
                            // then is inside split so return true;
                            return true;
                        }
                    } else if (stmp.get(0).intValue() == 2) {
                        // then polygon
                        if (inPoly(stmp, i.value(m_xIndex), i.value(m_yIndex))) {
                            return true;
                        }
                    } else if (stmp.get(0).intValue() == 3) {
                        // then polyline
                        if (inPolyline(stmp, i.value(m_xIndex), i.value(m_yIndex))) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        /**
         * Checks to see if the coordinate passed is inside the ployline passed,
         * Note that this is done using attribute values and not there respective
         * screen values.
         * 
         * @param ob The polyline.
         * @param x The x coord.
         * @param y The y coord.
         * @return True if it falls inside the polyline, false otherwise.
         */
        private boolean inPolyline(ArrayList<Double> ob, double x, double y) {
            // this works similar to the inPoly below except that
            // the first and last lines are treated as extending infinite in one
            // direction and
            // then infinitly in the x dirction their is a line that will
            // normaly be infinite but
            // can be finite in one or both directions

            int countx = 0;
            double vecx, vecy;
            double change;
            double x1, y1, x2, y2;

            for (int noa = 1; noa < ob.size() - 4; noa += 2) {
                y1 = ob.get(noa + 1).doubleValue();
                y2 = ob.get(noa + 3).doubleValue();
                x1 = ob.get(noa).doubleValue();
                x2 = ob.get(noa + 2).doubleValue();

                // System.err.println(y1 + " " + y2 + " " + x1 + " " + x2);
                vecy = y2 - y1;
                vecx = x2 - x1;
                if (noa == 1 && noa == ob.size() - 6) {
                    // then do special test first and last edge
                    if (vecy != 0) {
                        change = (y - y1) / vecy;
                        if (vecx * change + x1 >= x) {
                            // then intersection
                            countx++;
                        }
                    }
                } else if (noa == 1) {
                    if ((y < y2 && vecy > 0) || (y > y2 && vecy < 0)) {
                        // now just determine intersection or not
                        change = (y - y1) / vecy;
                        if (vecx * change + x1 >= x) {
                            // then intersection on horiz
                            countx++;
                        }
                    }
                } else if (noa == ob.size() - 6) {
                    // then do special test on last edge
                    if ((y <= y1 && vecy < 0) || (y >= y1 && vecy > 0)) {
                        change = (y - y1) / vecy;
                        if (vecx * change + x1 >= x) {
                            countx++;
                        }
                    }
                } else if ((y1 <= y && y < y2) || (y2 < y && y <= y1)) {
                    // then continue tests.
                    if (vecy == 0) {
                        // then lines are parallel stop tests in
                        // ofcourse it should never make it this far
                    } else {
                        change = (y - y1) / vecy;
                        if (vecx * change + x1 >= x) {
                            // then intersects on horiz
                            countx++;
                        }
                    }
                }
            }

            // now check for intersection with the infinity line
            y1 = ob.get(ob.size() - 2).doubleValue();
            y2 = ob.get(ob.size() - 1).doubleValue();

            if (y1 > y2) {
                // then normal line
                if (y1 >= y && y > y2) {
                    countx++;
                }
            } else {
                // then the line segment is inverted
                if (y1 >= y || y > y2) {
                    countx++;
                }
            }

            if ((countx % 2) == 1) {
                return true;
            } else {
                return false;
            }
        }

        /**
         * This checks to see if The coordinate passed is inside the polygon that
         * was passed.
         * 
         * @param ob The polygon.
         * @param x The x coord.
         * @param y The y coord.
         * @return True if the coordinate is in the polygon, false otherwise.
         */
        private boolean inPoly(ArrayList<Double> ob, double x, double y) {
            // brief on how this works
            // it draws a line horizontally from the point to the right (infinitly)
            // it then sees how many lines of the polygon intersect this,
            // if it is even then the point is
            // outside the polygon if it's odd then it's inside the polygon
            int count = 0;
            double vecx, vecy;
            double change;
            double x1, y1, x2, y2;
            for (int noa = 1; noa < ob.size() - 2; noa += 2) {
                y1 = ob.get(noa + 1).doubleValue();
                y2 = ob.get(noa + 3).doubleValue();
                if ((y1 <= y && y < y2) || (y2 < y && y <= y1)) {
                    // then continue tests.
                    vecy = y2 - y1;
                    if (vecy == 0) {
                        // then lines are parallel stop tests for this line
                    } else {
                        x1 = ob.get(noa).doubleValue();
                        x2 = ob.get(noa + 2).doubleValue();
                        vecx = x2 - x1;
                        change = (y - y1) / vecy;
                        if (vecx * change + x1 >= x) {
                            // then add to count as an intersected line
                            count++;
                        }
                    }
                }
            }
            if ((count % 2) == 1) {
                // then lies inside polygon
                // System.out.println("in");
                return true;
            } else {
                // System.out.println("out");
                return false;
            }
            // System.out.println("WHAT?!?!?!?!!?!??!?!");
            // return false;
        }

        /**
         * Set level of jitter and repaint the plot using the new jitter value
         * 
         * @param j the level of jitter
         */
        public void setJitter(int j) {
            m_plot2D.setJitter(j);
        }

        /**
         * Set the index of the attribute to go on the x axis
         * 
         * @param x the index of the attribute to use on the x axis
         */
        public void setXindex(int x) {

            // this just ensures that the shapes get disposed of
            // if the attribs change
            if (x != m_xIndex) {
                cancelShapes();
            }
            m_xIndex = x;
            m_plot2D.setXindex(x);
            if (m_showAttBars) {
                m_attrib.setX(x);
            }
            // this.repaint();
        }

        /**
         * Set the index of the attribute to go on the y axis
         * 
         * @param y the index of the attribute to use on the y axis
         */
        public void setYindex(int y) {

            // this just ensures that the shapes get disposed of
            // if the attribs change
            if (y != m_yIndex) {
                cancelShapes();
            }
            m_yIndex = y;
            m_plot2D.setYindex(y);
            if (m_showAttBars) {
                m_attrib.setY(y);
            }
            // this.repaint();
        }

        /**
         * Set the index of the attribute to use for colouring
         * 
         * @param c the index of the attribute to use for colouring
         */
        public void setCindex(int c) {
            m_cIndex = c;
            m_plot2D.setCindex(c);
            if (m_showAttBars) {
                m_attrib.setCindex(c, m_plot2D.getMaxC(), m_plot2D.getMinC());
            }
            m_classPanel.setCindex(c);
            this.repaint();
        }

        /**
         * Set the index of the attribute to use for the shape.
         * 
         * @param s the index of the attribute to use for the shape
         */
        public void setSindex(int s) {
            if (s != m_sIndex) {
                m_shapePoints = null;
                m_createShape = false;
            }
            m_sIndex = s;
            this.repaint();
        }

        /**
         * Clears all existing plots and sets a new master plot
         * 
         * @param newPlot the new master plot
         * @exception Exception if plot could not be added
         */
        public void setMasterPlot(PlotData2D newPlot) throws Exception {
            m_plot2D.removeAllPlots();
            this.addPlot(newPlot);
        }

        /**
         * Adds a plot. If there are no plots so far this plot becomes the master
         * plot and, if it has a custom colour defined then the class panel is
         * removed.
         * 
         * @param newPlot the plot to add.
         * @exception Exception if plot could not be added
         */
        public void addPlot(PlotData2D newPlot) throws Exception {
            if (m_plot2D.getPlots().size() == 0) {
                m_plot2D.addPlot(newPlot);
                if (m_plotSurround.getComponentCount() > 1 && m_plotSurround.getComponent(1) == m_attrib
                        && m_showAttBars) {
                    try {
                        m_attrib.setInstances(newPlot.m_plotInstances);
                        m_attrib.setCindex(0);
                        m_attrib.setX(0);
                        m_attrib.setY(0);
                    } catch (Exception ex) {
                        // more attributes than the panel can handle?
                        // Due to hard coded constraints in GridBagLayout
                        m_plotSurround.remove(m_attrib);
                        System.err.println("Warning : data contains more attributes "
                                + "than can be displayed as attribute bars.");
                        if (m_Log != null) {
                            m_Log.logMessage("Warning : data contains more attributes "
                                    + "than can be displayed as attribute bars.");
                        }
                    }
                } else if (m_showAttBars) {
                    try {
                        m_attrib.setInstances(newPlot.m_plotInstances);
                        m_attrib.setCindex(0);
                        m_attrib.setX(0);
                        m_attrib.setY(0);
                        GridBagConstraints constraints = new GridBagConstraints();
                        constraints.fill = GridBagConstraints.BOTH;
                        constraints.insets = new Insets(0, 0, 0, 0);
                        constraints.gridx = 4;
                        constraints.gridy = 0;
                        constraints.weightx = 1;
                        constraints.gridwidth = 1;
                        constraints.gridheight = 1;
                        constraints.weighty = 5;
                        m_plotSurround.add(m_attrib, constraints);
                    } catch (Exception ex) {
                        System.err.println("Warning : data contains more attributes "
                                + "than can be displayed as attribute bars.");
                        if (m_Log != null) {
                            m_Log.logMessage("Warning : data contains more attributes "
                                    + "than can be displayed as attribute bars.");
                        }
                    }
                }
                m_classPanel.setInstances(newPlot.m_plotInstances);

                plotReset(newPlot.m_plotInstances, newPlot.getCindex());
                if (newPlot.m_useCustomColour && m_showClassPanel) {
                    VisualizePanel.this.remove(m_classSurround);
                    switchToLegend();
                    m_legendPanel.setPlotList(m_plot2D.getPlots());
                    m_ColourCombo.setEnabled(false);
                }
            } else {
                if (!newPlot.m_useCustomColour && m_showClassPanel) {
                    VisualizePanel.this.add(m_classSurround, BorderLayout.SOUTH);
                    m_ColourCombo.setEnabled(true);
                }
                if (m_plot2D.getPlots().size() == 1) {
                    switchToLegend();
                }
                m_plot2D.addPlot(newPlot);
                m_legendPanel.setPlotList(m_plot2D.getPlots());
            }
        }

        /**
         * Remove the attibute panel and replace it with the legend panel
         */
        protected void switchToLegend() {

            if (m_plotSurround.getComponentCount() > 1 && m_plotSurround.getComponent(1) == m_attrib) {
                m_plotSurround.remove(m_attrib);
            }

            if (m_plotSurround.getComponentCount() > 1 && m_plotSurround.getComponent(1) == m_legendPanel) {
                return;
            }

            GridBagConstraints constraints = new GridBagConstraints();
            constraints.fill = GridBagConstraints.BOTH;
            constraints.insets = new Insets(0, 0, 0, 0);
            constraints.gridx = 4;
            constraints.gridy = 0;
            constraints.weightx = 1;
            constraints.gridwidth = 1;
            constraints.gridheight = 1;
            constraints.weighty = 5;
            m_plotSurround.add(m_legendPanel, constraints);
            setSindex(0);
            m_ShapeCombo.setEnabled(false);
        }

        protected void switchToBars() {
            if (m_plotSurround.getComponentCount() > 1 && m_plotSurround.getComponent(1) == m_legendPanel) {
                m_plotSurround.remove(m_legendPanel);
            }

            if (m_plotSurround.getComponentCount() > 1 && m_plotSurround.getComponent(1) == m_attrib) {
                return;
            }

            if (m_showAttBars) {
                try {
                    m_attrib.setInstances(m_plot2D.getMasterPlot().m_plotInstances);
                    m_attrib.setCindex(0);
                    m_attrib.setX(0);
                    m_attrib.setY(0);
                    GridBagConstraints constraints = new GridBagConstraints();
                    constraints.fill = GridBagConstraints.BOTH;
                    constraints.insets = new Insets(0, 0, 0, 0);
                    constraints.gridx = 4;
                    constraints.gridy = 0;
                    constraints.weightx = 1;
                    constraints.gridwidth = 1;
                    constraints.gridheight = 1;
                    constraints.weighty = 5;
                    m_plotSurround.add(m_attrib, constraints);
                } catch (Exception ex) {
                    System.err.println("Warning : data contains more attributes "
                            + "than can be displayed as attribute bars.");
                    if (m_Log != null) {
                        m_Log.logMessage("Warning : data contains more attributes "
                                + "than can be displayed as attribute bars.");
                    }
                }
            }
        }

        /**
         * Reset the visualize panel's buttons and the plot panels instances
         * 
         * @param inst the data
         * @param cIndex the color index
         */
        private void plotReset(Instances inst, int cIndex) {
            if (m_splitListener == null) {
                m_submit.setText("Reset");
                m_submit.setActionCommand("Reset");
                // if (m_origInstances == null || m_origInstances == inst) {
                if (m_originalPlot == null || m_originalPlot.m_plotInstances == inst) {
                    m_submit.setEnabled(false);
                } else {
                    m_submit.setEnabled(true);
                }
            } else {
                m_submit.setEnabled(false);
            }

            m_plotInstances = inst;
            if (m_splitListener != null) {
                m_plotInstances.randomize(new Random());
            }
            m_xIndex = 0;
            m_yIndex = 0;
            m_cIndex = cIndex;
            cancelShapes();
        }

        /**
         * Set a list of colours to use for plotting points
         * 
         * @param cols a list of java.awt.Colors
         */
        public void setColours(ArrayList<Color> cols) {
            m_plot2D.setColours(cols);
            m_colorList = cols;
        }

        /**
         * This will draw the shapes created onto the panel. For best visual, this
         * should be the first thing to be drawn (and it currently is).
         * 
         * @param gx The graphics context.
         */
        private void drawShapes(Graphics gx) {
            // FastVector tmp = m_plot.getShapes();

            if (m_shapes != null) {
                ArrayList<Double> stmp;
                int x1, y1, x2, y2;
                for (int noa = 0; noa < m_shapes.size(); noa++) {
                    stmp = m_shapes.get(noa);
                    if (stmp.get(0).intValue() == 1) {
                        // then rectangle
                        x1 = (int) m_plot2D.convertToPanelX(stmp.get(1).doubleValue());
                        y1 = (int) m_plot2D.convertToPanelY(stmp.get(2).doubleValue());
                        x2 = (int) m_plot2D.convertToPanelX(stmp.get(3).doubleValue());
                        y2 = (int) m_plot2D.convertToPanelY(stmp.get(4).doubleValue());

                        gx.setColor(Color.gray);
                        gx.fillRect(x1, y1, x2 - x1, y2 - y1);
                        gx.setColor(Color.black);
                        gx.drawRect(x1, y1, x2 - x1, y2 - y1);

                    } else if (stmp.get(0).intValue() == 2) {
                        // then polygon
                        int[] ar1, ar2;
                        ar1 = getXCoords(stmp);
                        ar2 = getYCoords(stmp);
                        gx.setColor(Color.gray);
                        gx.fillPolygon(ar1, ar2, (stmp.size() - 1) / 2);
                        gx.setColor(Color.black);
                        gx.drawPolyline(ar1, ar2, (stmp.size() - 1) / 2);
                    } else if (stmp.get(0).intValue() == 3) {
                        // then polyline
                        int[] ar1, ar2;
                        ArrayList<Double> tmp = makePolygon(stmp);
                        ar1 = getXCoords(tmp);
                        ar2 = getYCoords(tmp);

                        gx.setColor(Color.gray);
                        gx.fillPolygon(ar1, ar2, (tmp.size() - 1) / 2);
                        gx.setColor(Color.black);
                        gx.drawPolyline(ar1, ar2, (tmp.size() - 1) / 2);
                    }
                }
            }

            if (m_shapePoints != null) {
                // then the current image needs to be refreshed
                if (m_shapePoints.get(0).intValue() == 2 || m_shapePoints.get(0).intValue() == 3) {
                    gx.setColor(Color.black);
                    gx.setXORMode(Color.white);
                    int[] ar1, ar2;
                    ar1 = getXCoords(m_shapePoints);
                    ar2 = getYCoords(m_shapePoints);
                    gx.drawPolyline(ar1, ar2, (m_shapePoints.size() - 1) / 2);
                    m_newMousePos.width = (int) Math.ceil(
                            m_plot2D.convertToPanelX(m_shapePoints.get(m_shapePoints.size() - 2).doubleValue()));

                    m_newMousePos.height = (int) Math.ceil(
                            m_plot2D.convertToPanelY(m_shapePoints.get(m_shapePoints.size() - 1).doubleValue()));

                    gx.drawLine(
                            (int) Math.ceil(m_plot2D
                                    .convertToPanelX(m_shapePoints.get(m_shapePoints.size() - 2).doubleValue())),
                            (int) Math.ceil(m_plot2D
                                    .convertToPanelY(m_shapePoints.get(m_shapePoints.size() - 1).doubleValue())),
                            m_newMousePos.width, m_newMousePos.height);
                    gx.setPaintMode();
                }
            }
        }

        /**
         * This is called for polylines to see where there two lines that extend to
         * infinity cut the border of the view.
         * 
         * @param x1 an x point along the line
         * @param y1 the accompanying y point.
         * @param x2 The x coord of the end point of the line.
         * @param y2 The y coord of the end point of the line.
         * @param x 0 or the width of the border line if it has one.
         * @param y 0 or the height of the border line if it has one.
         * @param offset The offset for the border line (either for x or y dependant
         *          on which one doesn't change).
         * @return double array that contains the coordinate for the point that the
         *         polyline cuts the border (which ever side that may be).
         */
        private double[] lineIntersect(double x1, double y1, double x2, double y2, double x, double y,
                double offset) {
            // the first 4 params are thestart and end points of a line
            // the next param is either 0 for no change in x or change in x,
            // the next param is the same for y
            // the final 1 is the offset for either x or y (which ever has no change)
            double xval;
            double yval;
            double xn = -100, yn = -100;
            double change;
            if (x == 0) {
                if ((x1 <= offset && offset < x2) || (x1 >= offset && offset > x2)) {
                    // then continue
                    xval = x1 - x2;
                    change = (offset - x2) / xval;
                    yn = (y1 - y2) * change + y2;
                    if (0 <= yn && yn <= y) {
                        // then good
                        xn = offset;
                    } else {
                        // no intersect
                        xn = -100;
                    }
                }
            } else if (y == 0) {
                if ((y1 <= offset && offset < y2) || (y1 >= offset && offset > y2)) {
                    // the continue
                    yval = (y1 - y2);
                    change = (offset - y2) / yval;
                    xn = (x1 - x2) * change + x2;
                    if (0 <= xn && xn <= x) {
                        // then good
                        yn = offset;
                    } else {
                        xn = -100;
                    }
                }
            }
            double[] ret = new double[2];
            ret[0] = xn;
            ret[1] = yn;
            return ret;
        }

        /**
         * This will convert a polyline to a polygon for drawing purposes So that I
         * can simply use the polygon drawing function.
         * 
         * @param v The polyline to convert.
         * @return A FastVector containing the polygon.
         */
        private ArrayList<Double> makePolygon(ArrayList<Double> v) {
            ArrayList<Double> building = new ArrayList<Double>(v.size() + 10);
            double x1, y1, x2, y2;
            int edge1 = 0, edge2 = 0;
            for (int noa = 0; noa < v.size() - 2; noa++) {
                building.add(new Double(v.get(noa).doubleValue()));
            }

            // now clip the lines
            double[] new_coords;
            // note lineIntersect , expects the values to have been converted to
            // screen coords
            // note the first point passed is the one that gets shifted.
            x1 = m_plot2D.convertToPanelX(v.get(1).doubleValue());
            y1 = m_plot2D.convertToPanelY(v.get(2).doubleValue());
            x2 = m_plot2D.convertToPanelX(v.get(3).doubleValue());
            y2 = m_plot2D.convertToPanelY(v.get(4).doubleValue());

            if (x1 < 0) {
                // test left
                new_coords = lineIntersect(x1, y1, x2, y2, 0, this.getHeight(), 0);
                edge1 = 0;
                if (new_coords[0] < 0) {
                    // then not left
                    if (y1 < 0) {
                        // test top
                        new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
                        edge1 = 1;
                    } else {
                        // test bottom
                        new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, this.getHeight());
                        edge1 = 3;
                    }
                }
            } else if (x1 > this.getWidth()) {
                // test right
                new_coords = lineIntersect(x1, y1, x2, y2, 0, this.getHeight(), this.getWidth());
                edge1 = 2;
                if (new_coords[0] < 0) {
                    // then not right
                    if (y1 < 0) {
                        // test top
                        new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
                        edge1 = 1;
                    } else {
                        // test bottom
                        new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, this.getHeight());
                        edge1 = 3;
                    }
                }
            } else if (y1 < 0) {
                // test top
                new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
                edge1 = 1;
            } else {
                // test bottom
                new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, this.getHeight());
                edge1 = 3;
            }

            building.set(1, new Double(m_plot2D.convertToAttribX(new_coords[0])));
            building.set(2, new Double(m_plot2D.convertToAttribY(new_coords[1])));

            x1 = m_plot2D.convertToPanelX(v.get(v.size() - 4).doubleValue());
            y1 = m_plot2D.convertToPanelY(v.get(v.size() - 3).doubleValue());
            x2 = m_plot2D.convertToPanelX(v.get(v.size() - 6).doubleValue());
            y2 = m_plot2D.convertToPanelY(v.get(v.size() - 5).doubleValue());

            if (x1 < 0) {
                // test left
                new_coords = lineIntersect(x1, y1, x2, y2, 0, this.getHeight(), 0);
                edge2 = 0;
                if (new_coords[0] < 0) {
                    // then not left
                    if (y1 < 0) {
                        // test top
                        new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
                        edge2 = 1;
                    } else {
                        // test bottom
                        new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, this.getHeight());
                        edge2 = 3;
                    }
                }
            } else if (x1 > this.getWidth()) {
                // test right
                new_coords = lineIntersect(x1, y1, x2, y2, 0, this.getHeight(), this.getWidth());
                edge2 = 2;
                if (new_coords[0] < 0) {
                    // then not right
                    if (y1 < 0) {
                        // test top
                        new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
                        edge2 = 1;
                    } else {
                        // test bottom
                        new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, this.getHeight());
                        edge2 = 3;
                    }
                }
            } else if (y1 < 0) {
                // test top
                new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
                edge2 = 1;
            } else {
                // test bottom
                new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, this.getHeight());
                edge2 = 3;
            }

            building.set(building.size() - 2, new Double(m_plot2D.convertToAttribX(new_coords[0])));
            building.set(building.size() - 1, new Double(m_plot2D.convertToAttribY(new_coords[1])));

            // trust me this complicated piece of code will
            // determine what points on the boundary of the view to add to the polygon
            int xp, yp;

            xp = this.getWidth() * ((edge2 & 1) ^ ((edge2 & 2) / 2));
            yp = this.getHeight() * ((edge2 & 2) / 2);
            // System.out.println(((-1 + 4) % 4) + " hoi");

            if (inPolyline(v, m_plot2D.convertToAttribX(xp), m_plot2D.convertToAttribY(yp))) {
                // then add points in a clockwise direction
                building.add(new Double(m_plot2D.convertToAttribX(xp)));
                building.add(new Double(m_plot2D.convertToAttribY(yp)));
                for (int noa = (edge2 + 1) % 4; noa != edge1; noa = (noa + 1) % 4) {
                    xp = this.getWidth() * ((noa & 1) ^ ((noa & 2) / 2));
                    yp = this.getHeight() * ((noa & 2) / 2);
                    building.add(new Double(m_plot2D.convertToAttribX(xp)));
                    building.add(new Double(m_plot2D.convertToAttribY(yp)));
                }
            } else {
                xp = this.getWidth() * ((edge2 & 2) / 2);
                yp = this.getHeight() * (1 & ~((edge2 & 1) ^ ((edge2 & 2) / 2)));
                if (inPolyline(v, m_plot2D.convertToAttribX(xp), m_plot2D.convertToAttribY(yp))) {
                    // then add points in anticlockwise direction
                    building.add(new Double(m_plot2D.convertToAttribX(xp)));
                    building.add(new Double(m_plot2D.convertToAttribY(yp)));
                    for (int noa = (edge2 + 3) % 4; noa != edge1; noa = (noa + 3) % 4) {
                        xp = this.getWidth() * ((noa & 2) / 2);
                        yp = this.getHeight() * (1 & ~((noa & 1) ^ ((noa & 2) / 2)));
                        building.add(new Double(m_plot2D.convertToAttribX(xp)));
                        building.add(new Double(m_plot2D.convertToAttribY(yp)));
                    }
                }
            }
            return building;
        }

        /**
         * This will extract from a polygon shape its x coodrdinates so that an
         * awt.Polygon can be created.
         * 
         * @param v The polygon shape.
         * @return an int array containing the screen x coords for the polygon.
         */
        private int[] getXCoords(ArrayList<Double> v) {
            int cach = (v.size() - 1) / 2;
            int[] ar = new int[cach];
            for (int noa = 0; noa < cach; noa++) {
                ar[noa] = (int) m_plot2D.convertToPanelX(v.get(noa * 2 + 1).doubleValue());
            }
            return ar;
        }

        /**
         * This will extract from a polygon shape its y coordinates so that an
         * awt.Polygon can be created.
         * 
         * @param v The polygon shape.
         * @return an int array containing the screen y coords for the polygon.
         */
        private int[] getYCoords(ArrayList<Double> v) {
            int cach = (v.size() - 1) / 2;
            int[] ar = new int[cach];
            for (int noa = 0; noa < cach; noa++) {
                ar[noa] = (int) m_plot2D.convertToPanelY(v.get(noa * 2 + 2).doubleValue());
            }
            return ar;
        }

        /**
         * Renders the polygons if necessary
         * 
         * @param gx the graphics context
         */
        @Override
        public void prePlot(Graphics gx) {
            super.paintComponent(gx);
            if (m_plotInstances != null) {
                drawShapes(gx); // will be in paintComponent of ShapePlot2D
            }
        }
    }

    /** default colours for colouring discrete class */
    protected Color[] m_DefaultColors = { Color.blue, Color.red, Color.green, Color.cyan, Color.pink,
            new Color(255, 0, 255), Color.orange, new Color(255, 0, 0), new Color(0, 255, 0), Color.white };

    /** Lets the user select the attribute for the x axis */
    protected JComboBox m_XCombo = new JComboBox();

    /** Lets the user select the attribute for the y axis */
    protected JComboBox m_YCombo = new JComboBox();

    /** Lets the user select the attribute to use for colouring */
    protected JComboBox m_ColourCombo = new JComboBox();

    /**
     * Lets the user select the shape they want to create for instance selection.
     */
    protected JComboBox m_ShapeCombo = new JComboBox();

    /** Button for the user to enter the splits. */
    protected JButton m_submit = new JButton("Submit");

    /** Button for the user to remove all splits. */
    protected JButton m_cancel = new JButton("Clear");

    /** Button for the user to open the visualized set of instances */
    protected JButton m_openBut = new JButton("Open");

    /** Button for the user to save the visualized set of instances */
    protected JButton m_saveBut = new JButton("Save");

    /** Stop the combos from growing out of control */
    private final Dimension COMBO_SIZE = new Dimension(250, m_saveBut.getPreferredSize().height);

    /** file chooser for saving instances */
    protected WekaFileChooser m_FileChooser = new WekaFileChooser(new File(System.getProperty("user.dir")));

    /** Filter to ensure only arff files are selected */
    protected FileFilter m_ArffFilter = new ExtensionFileFilter(Instances.FILE_EXTENSION, "Arff data files");

    /** Label for the jitter slider */
    protected JLabel m_JitterLab = new JLabel("Jitter", SwingConstants.RIGHT);

    /** The jitter slider */
    protected JSlider m_Jitter = new JSlider(0, 50, 0);

    /** The panel that displays the plot */
    protected PlotPanel m_plot = new PlotPanel();

    /**
     * The panel that displays the attributes , using color to represent another
     * attribute.
     */
    protected AttributePanel m_attrib = new AttributePanel(m_plot.m_plot2D.getBackground());

    /** The panel that displays legend info if there is more than one plot */
    protected LegendPanel m_legendPanel = new LegendPanel();

    /** Panel that surrounds the plot panel with a titled border */
    protected JPanel m_plotSurround = new JPanel();

    /** Panel that surrounds the class panel with a titled border */
    protected JPanel m_classSurround = new JPanel();

    /**
     * An optional listener that we will inform when ComboBox selections change
     */
    protected ActionListener listener = null;

    /**
     * An optional listener that we will inform when the user creates a split to
     * seperate instances.
     */
    protected VisualizePanelListener m_splitListener = null;

    /**
     * The name of the plot (not currently displayed, but can be used in the
     * containing Frame or Panel)
     */
    protected String m_plotName = "";

    /** The panel that displays the legend for the colouring attribute */
    protected ClassPanel m_classPanel = new ClassPanel(m_plot.m_plot2D.getBackground());

    /** The list of the colors used */
    protected ArrayList<Color> m_colorList;

    /**
     * These hold the names of preferred columns to visualize on---if the user has
     * defined them in the Visualize.props file
     */
    protected String m_preferredXDimension = null;
    protected String m_preferredYDimension = null;
    protected String m_preferredColourDimension = null;

    /** Show the attribute bar panel */
    protected boolean m_showAttBars = true;

    /** Show the class panel **/
    protected boolean m_showClassPanel = true;

    /** the logger */
    protected Logger m_Log;

    /**
     * Sets the Logger to receive informational messages
     * 
     * @param newLog the Logger that will now get info messages
     */
    public void setLog(Logger newLog) {
        m_Log = newLog;
    }

    /**
     * Set whether the attribute bars should be shown or not. If turned off via
     * this method then any setting in the properties file (if exists) is ignored.
     * 
     * @param sab false if attribute bars are not to be displayed.
     */
    public void setShowAttBars(boolean sab) {
        if (!sab && m_showAttBars) {
            m_plotSurround.remove(m_attrib);
        } else if (sab && !m_showAttBars) {
            GridBagConstraints constraints = new GridBagConstraints();
            constraints.insets = new Insets(0, 0, 0, 0);
            constraints.gridx = 4;
            constraints.gridy = 0;
            constraints.weightx = 1;
            constraints.gridwidth = 1;
            constraints.gridheight = 1;
            constraints.weighty = 5;
            m_plotSurround.add(m_attrib, constraints);
        }
        m_showAttBars = sab;
        repaint();
    }

    /**
     * Gets whether or not attribute bars are being displayed.
     * 
     * @return true if attribute bars are being displayed.
     */
    public boolean getShowAttBars() {
        return m_showAttBars;
    }

    /**
     * Set whether the class panel should be shown or not.
     * 
     * @param scp false if class panel is not to be displayed
     */
    public void setShowClassPanel(boolean scp) {
        if (!scp && m_showClassPanel) {
            remove(m_classSurround);
        } else if (scp && !m_showClassPanel) {
            add(m_classSurround, BorderLayout.SOUTH);
        }
        m_showClassPanel = scp;
        repaint();
    }

    /**
     * Gets whether or not the class panel is being displayed.
     * 
     * @return true if the class panel is being displayed.
     */
    public boolean getShowClassPanel() {
        return m_showClassPanel;
    }

    /**
     * This constructor allows a VisualizePanelListener to be set.
     * 
     * @param ls the listener to use
     */
    public VisualizePanel(VisualizePanelListener ls) {
        this();
        m_splitListener = ls;
    }

    /**
     * Set the properties for the VisualizePanel
     * 
     * @param relationName the name of the relation, can be null
     */
    private void setProperties(String relationName) {
        if (VisualizeUtils.VISUALIZE_PROPERTIES != null) {
            String thisClass = this.getClass().getName();
            if (relationName == null) {

                String showAttBars = thisClass + ".displayAttributeBars";

                String val = VisualizeUtils.VISUALIZE_PROPERTIES.getProperty(showAttBars);
                if (val == null) {
                    // System.err.println("Displaying attribute bars ");
                    // m_showAttBars = true;
                } else {
                    // only check if this hasn't been turned off programatically
                    if (m_showAttBars) {
                        if (val.compareTo("true") == 0 || val.compareTo("on") == 0) {
                            // System.err.println("Displaying attribute bars ");
                            m_showAttBars = true;
                        } else {
                            m_showAttBars = false;
                        }
                    }
                }
            } else {
                /*
                 * System.err.println("Looking for preferred visualization dimensions for "
                 * +relationName);
                 */
                String xcolKey = thisClass + "." + relationName + ".XDimension";
                String ycolKey = thisClass + "." + relationName + ".YDimension";
                String ccolKey = thisClass + "." + relationName + ".ColourDimension";

                m_preferredXDimension = VisualizeUtils.VISUALIZE_PROPERTIES.getProperty(xcolKey);
                /*
                 * if (m_preferredXDimension == null) {
                 * System.err.println("No preferred X dimension found in "
                 * +VisualizeUtils.PROPERTY_FILE +" for "+xcolKey); } else {
                 * System.err.println("Setting preferred X dimension to "
                 * +m_preferredXDimension); }
                 */
                m_preferredYDimension = VisualizeUtils.VISUALIZE_PROPERTIES.getProperty(ycolKey);
                /*
                 * if (m_preferredYDimension == null) {
                 * System.err.println("No preferred Y dimension found in "
                 * +VisualizeUtils.PROPERTY_FILE +" for "+ycolKey); } else {
                 * System.err.println("Setting preferred dimension Y to "
                 * +m_preferredYDimension); }
                 */
                m_preferredColourDimension = VisualizeUtils.VISUALIZE_PROPERTIES.getProperty(ccolKey);
                /*
                 * if (m_preferredColourDimension == null) {
                 * System.err.println("No preferred Colour dimension found in "
                 * +VisualizeUtils.PROPERTY_FILE +" for "+ycolKey); } else {
                 * System.err.println("Setting preferred Colour dimension to "
                 * +m_preferredColourDimension); }
                 */
            }
        }
    }

    /**
     * Apply settings
     *
     * @param settings the settings to apply
     * @param ownerID the ID of the owner perspective, panel etc. to use when
     *          looking up settings
     */
    public void applySettings(Settings settings, String ownerID) {
        m_plot.applySettings(settings, ownerID);
        m_attrib.applySettings(settings, ownerID);
        repaint();
    }

    /**
     * Constructor
     */
    public VisualizePanel() {
        super();
        setProperties(null);
        m_FileChooser.setFileFilter(m_ArffFilter);
        m_FileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);

        m_XCombo.setToolTipText("Select the attribute for the x axis");
        m_YCombo.setToolTipText("Select the attribute for the y axis");
        m_ColourCombo.setToolTipText("Select the attribute to colour on");
        m_ShapeCombo.setToolTipText("Select the shape to use for data selection");

        m_XCombo.setPreferredSize(COMBO_SIZE);
        m_YCombo.setPreferredSize(COMBO_SIZE);
        m_ColourCombo.setPreferredSize(COMBO_SIZE);
        m_ShapeCombo.setPreferredSize(COMBO_SIZE);

        m_XCombo.setMaximumSize(COMBO_SIZE);
        m_YCombo.setMaximumSize(COMBO_SIZE);
        m_ColourCombo.setMaximumSize(COMBO_SIZE);
        m_ShapeCombo.setMaximumSize(COMBO_SIZE);

        m_XCombo.setMinimumSize(COMBO_SIZE);
        m_YCombo.setMinimumSize(COMBO_SIZE);
        m_ColourCombo.setMinimumSize(COMBO_SIZE);
        m_ShapeCombo.setMinimumSize(COMBO_SIZE);
        // ////////
        m_XCombo.setEnabled(false);
        m_YCombo.setEnabled(false);
        m_ColourCombo.setEnabled(false);
        m_ShapeCombo.setEnabled(false);

        // tell the class panel and the legend panel that we want to know when
        // colours change
        m_classPanel.addRepaintNotify(this);
        m_legendPanel.addRepaintNotify(this);

        // Check the default colours against the background colour of the
        // plot panel. If any are equal to the background colour then
        // change them (so they are visible :-)
        for (int i = 0; i < m_DefaultColors.length; i++) {
            Color c = m_DefaultColors[i];
            if (c.equals(m_plot.m_plot2D.getBackground())) {
                int red = c.getRed();
                int blue = c.getBlue();
                int green = c.getGreen();
                red += (red < 128) ? (255 - red) / 2 : -(red / 2);
                blue += (blue < 128) ? (blue - red) / 2 : -(blue / 2);
                green += (green < 128) ? (255 - green) / 2 : -(green / 2);
                m_DefaultColors[i] = new Color(red, green, blue);
            }
        }
        m_classPanel.setDefaultColourList(m_DefaultColors);
        m_attrib.setDefaultColourList(m_DefaultColors);

        m_colorList = new ArrayList<Color>(10);
        for (int noa = m_colorList.size(); noa < 10; noa++) {
            Color pc = m_DefaultColors[noa % 10];
            int ija = noa / 10;
            ija *= 2;
            for (int j = 0; j < ija; j++) {
                pc = pc.darker();
            }

            m_colorList.add(pc);
        }
        m_plot.setColours(m_colorList);
        m_classPanel.setColours(m_colorList);
        m_attrib.setColours(m_colorList);
        m_attrib.addAttributePanelListener(new AttributePanelListener() {
            @Override
            public void attributeSelectionChange(AttributePanelEvent e) {
                if (e.m_xChange) {
                    m_XCombo.setSelectedIndex(e.m_indexVal);
                } else {
                    m_YCombo.setSelectedIndex(e.m_indexVal);
                }
            }
        });

        m_XCombo.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int selected = m_XCombo.getSelectedIndex();
                if (selected < 0) {
                    selected = 0;
                }
                m_plot.setXindex(selected);

                // try sending on the event if anyone is listening
                if (listener != null) {
                    listener.actionPerformed(e);
                }
            }
        });

        m_YCombo.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int selected = m_YCombo.getSelectedIndex();
                if (selected < 0) {
                    selected = 0;
                }
                m_plot.setYindex(selected);

                // try sending on the event if anyone is listening
                if (listener != null) {
                    listener.actionPerformed(e);
                }
            }
        });

        m_ColourCombo.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int selected = m_ColourCombo.getSelectedIndex();
                if (selected < 0) {
                    selected = 0;
                }
                m_plot.setCindex(selected);

                if (listener != null) {
                    listener.actionPerformed(e);
                }
            }
        });

        // /////
        m_ShapeCombo.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int selected = m_ShapeCombo.getSelectedIndex();
                if (selected < 0) {
                    selected = 0;
                }
                m_plot.setSindex(selected);
                // try sending on the event if anyone is listening
                if (listener != null) {
                    listener.actionPerformed(e);
                }
            }
        });

        // /////////////////////////////////////

        m_Jitter.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                m_plot.setJitter(m_Jitter.getValue());
            }
        });

        m_openBut.setToolTipText("Loads previously saved instances from a file");
        m_openBut.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                openVisibleInstances();
            }
        });

        m_saveBut.setEnabled(false);
        m_saveBut.setToolTipText("Save the visible instances to a file");
        m_saveBut.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                saveVisibleInstances();
            }
        });

        JPanel combos = new JPanel();
        GridBagLayout gb = new GridBagLayout();
        GridBagConstraints constraints = new GridBagConstraints();

        m_XCombo.setLightWeightPopupEnabled(false);
        m_YCombo.setLightWeightPopupEnabled(false);
        m_ColourCombo.setLightWeightPopupEnabled(false);
        m_ShapeCombo.setLightWeightPopupEnabled(false);
        combos.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5));

        combos.setLayout(gb);
        constraints.gridx = 0;
        constraints.gridy = 0;
        constraints.weightx = 5;
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.gridwidth = 2;
        constraints.gridheight = 1;
        constraints.insets = new Insets(0, 2, 0, 2);
        combos.add(m_XCombo, constraints);
        constraints.gridx = 2;
        constraints.gridy = 0;
        constraints.weightx = 5;
        constraints.gridwidth = 2;
        constraints.gridheight = 1;
        combos.add(m_YCombo, constraints);
        constraints.gridx = 0;
        constraints.gridy = 1;
        constraints.weightx = 5;
        constraints.gridwidth = 2;
        constraints.gridheight = 1;
        combos.add(m_ColourCombo, constraints);
        //
        constraints.gridx = 2;
        constraints.gridy = 1;
        constraints.weightx = 5;
        constraints.gridwidth = 2;
        constraints.gridheight = 1;
        combos.add(m_ShapeCombo, constraints);

        JPanel mbts = new JPanel();
        mbts.setLayout(new GridLayout(1, 4));
        mbts.add(m_submit);
        mbts.add(m_cancel);
        mbts.add(m_openBut);
        mbts.add(m_saveBut);

        constraints.gridx = 0;
        constraints.gridy = 2;
        constraints.weightx = 5;
        constraints.gridwidth = 2;
        constraints.gridheight = 1;
        combos.add(mbts, constraints);

        // //////////////////////////////
        constraints.gridx = 2;
        constraints.gridy = 2;
        constraints.weightx = 5;
        constraints.gridwidth = 1;
        constraints.gridheight = 1;
        constraints.insets = new Insets(10, 0, 0, 5);
        combos.add(m_JitterLab, constraints);
        constraints.gridx = 3;
        constraints.gridy = 2;
        constraints.weightx = 5;
        constraints.insets = new Insets(10, 0, 0, 0);
        combos.add(m_Jitter, constraints);

        m_classSurround = new JPanel();
        m_classSurround.setBorder(BorderFactory.createTitledBorder("Class colour"));
        m_classSurround.setLayout(new BorderLayout());

        m_classPanel.setBorder(BorderFactory.createEmptyBorder(15, 10, 10, 10));
        m_classSurround.add(m_classPanel, BorderLayout.CENTER);

        GridBagLayout gb2 = new GridBagLayout();
        m_plotSurround.setBorder(BorderFactory.createTitledBorder("Plot"));
        m_plotSurround.setLayout(gb2);

        constraints.fill = GridBagConstraints.BOTH;
        constraints.insets = new Insets(0, 0, 0, 10);
        constraints.gridx = 0;
        constraints.gridy = 0;
        constraints.weightx = 3;
        constraints.gridwidth = 4;
        constraints.gridheight = 1;
        constraints.weighty = 5;
        m_plotSurround.add(m_plot, constraints);

        if (m_showAttBars) {
            constraints.insets = new Insets(0, 0, 0, 0);
            constraints.gridx = 4;
            constraints.gridy = 0;
            constraints.weightx = 1;
            constraints.gridwidth = 1;
            constraints.gridheight = 1;
            constraints.weighty = 5;
            m_plotSurround.add(m_attrib, constraints);
        }

        setLayout(new BorderLayout());
        add(combos, BorderLayout.NORTH);
        add(m_plotSurround, BorderLayout.CENTER);
        add(m_classSurround, BorderLayout.SOUTH);

        String[] SNames = new String[4];
        SNames[0] = "Select Instance";
        SNames[1] = "Rectangle";
        SNames[2] = "Polygon";
        SNames[3] = "Polyline";

        m_ShapeCombo.setModel(new DefaultComboBoxModel(SNames));
        m_ShapeCombo.setEnabled(true);
    }

    /**
     * displays the previously saved instances
     * 
     * @param insts the instances to display
     * @throws Exception if display is not possible
     */
    protected void openVisibleInstances(Instances insts) throws Exception {
        PlotData2D tempd = new PlotData2D(insts);
        tempd.setPlotName(insts.relationName());
        tempd.addInstanceNumberAttribute();
        m_plot.m_plot2D.removeAllPlots();
        addPlot(tempd);

        // modify title
        Component parent = getParent();
        while (parent != null) {
            if (parent instanceof JFrame) {
                ((JFrame) parent)
                        .setTitle("Weka Classifier Visualize: " + insts.relationName() + " (display only)");
                break;
            } else {
                parent = parent.getParent();
            }
        }
    }

    /**
     * Loads previously saved instances from a file
     */
    protected void openVisibleInstances() {
        try {
            int returnVal = m_FileChooser.showOpenDialog(this);
            if (returnVal == JFileChooser.APPROVE_OPTION) {
                File sFile = m_FileChooser.getSelectedFile();
                if (!sFile.getName().toLowerCase().endsWith(Instances.FILE_EXTENSION)) {
                    sFile = new File(sFile.getParent(), sFile.getName() + Instances.FILE_EXTENSION);
                }
                File selected = sFile;
                Instances insts = new Instances(new BufferedReader(new FileReader(selected)));
                openVisibleInstances(insts);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            m_plot.m_plot2D.removeAllPlots();
            JOptionPane.showMessageDialog(this, ex.getMessage(), "Error loading file...",
                    JOptionPane.ERROR_MESSAGE);
        }
    }

    /**
     * Save the currently visible set of instances to a file
     */
    private void saveVisibleInstances() {
        ArrayList<PlotData2D> plots = m_plot.m_plot2D.getPlots();
        if (plots != null) {
            PlotData2D master = plots.get(0);
            Instances saveInsts = new Instances(master.getPlotInstances());
            for (int i = 1; i < plots.size(); i++) {
                PlotData2D temp = plots.get(i);
                Instances addInsts = temp.getPlotInstances();
                for (int j = 0; j < addInsts.numInstances(); j++) {
                    saveInsts.add(addInsts.instance(j));
                }
            }
            try {
                int returnVal = m_FileChooser.showSaveDialog(this);
                if (returnVal == JFileChooser.APPROVE_OPTION) {
                    File sFile = m_FileChooser.getSelectedFile();
                    if (!sFile.getName().toLowerCase().endsWith(Instances.FILE_EXTENSION)) {
                        sFile = new File(sFile.getParent(), sFile.getName() + Instances.FILE_EXTENSION);
                    }
                    File selected = sFile;
                    Writer w = new BufferedWriter(new FileWriter(selected));
                    w.write(saveInsts.toString());
                    w.close();
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * Set the index for colouring.
     *
     * @param index the index of the attribute to use for colouring
     * @param enableCombo false if the colouring combo box should be disabled
     */
    public void setColourIndex(int index, boolean enableCombo) {
        if (index >= 0) {
            m_ColourCombo.setSelectedIndex(index);
        } else {
            m_ColourCombo.setSelectedIndex(0);
        }
        m_ColourCombo.setEnabled(enableCombo);
    }

    /**
     * Sets the index used for colouring. If this method is called then the
     * supplied index is used and the combo box for selecting colouring attribute
     * is disabled.
     * 
     * @param index the index of the attribute to use for colouring
     */
    public void setColourIndex(int index) {
        setColourIndex(index, false);
    }

    /**
     * Set the index of the attribute for the x axis
     * 
     * @param index the index for the x axis
     * @exception Exception if index is out of range.
     */
    public void setXIndex(int index) throws Exception {
        if (index >= 0 && index < m_XCombo.getItemCount()) {
            m_XCombo.setSelectedIndex(index);
        } else {
            throw new Exception("x index is out of range!");
        }
    }

    /**
     * Get the index of the attribute on the x axis
     * 
     * @return the index of the attribute on the x axis
     */
    public int getXIndex() {
        return m_XCombo.getSelectedIndex();
    }

    /**
     * Set the index of the attribute for the y axis
     * 
     * @param index the index for the y axis
     * @exception Exception if index is out of range.
     */
    public void setYIndex(int index) throws Exception {
        if (index >= 0 && index < m_YCombo.getItemCount()) {
            m_YCombo.setSelectedIndex(index);
        } else {
            throw new Exception("y index is out of range!");
        }
    }

    /**
     * Get the index of the attribute on the y axis
     * 
     * @return the index of the attribute on the x axis
     */
    public int getYIndex() {
        return m_YCombo.getSelectedIndex();
    }

    /**
     * Get the index of the attribute selected for coloring
     * 
     * @return the index of the attribute on the x axis
     */
    public int getCIndex() {
        return m_ColourCombo.getSelectedIndex();
    }

    /**
     * Get the index of the shape selected for creating splits.
     * 
     * @return The index of the shape.
     */
    public int getSIndex() {
        return m_ShapeCombo.getSelectedIndex();
    }

    /**
     * Set the shape for creating splits.
     * 
     * @param index The index of the shape.
     * @exception Exception if index is out of range.
     */
    public void setSIndex(int index) throws Exception {
        if (index >= 0 && index < m_ShapeCombo.getItemCount()) {
            m_ShapeCombo.setSelectedIndex(index);
        } else {
            throw new Exception("s index is out of range!");
        }
    }

    /**
     * Add a listener for this visualize panel
     * 
     * @param act an ActionListener
     */
    public void addActionListener(ActionListener act) {
        listener = act;
    }

    /**
     * Set a name for this plot
     * 
     * @param plotName the name for the plot
     */
    @Override
    public void setName(String plotName) {
        m_plotName = plotName;
    }

    /**
     * Returns the name associated with this plot. "" is returned if no name is
     * set.
     * 
     * @return the name of the plot
     */
    @Override
    public String getName() {
        return m_plotName;
    }

    /**
     * Get the master plot's instances
     * 
     * @return the master plot's instances
     */
    public Instances getInstances() {
        return m_plot.m_plotInstances;
    }

    /**
     * Sets the Colors in use for a different attrib if it is not a nominal attrib
     * and or does not have more possible values then this will do nothing.
     * otherwise it will add default colors to see that there is a color for the
     * attrib to begin with.
     * 
     * @param a The index of the attribute to color.
     * @param i The instances object that contains the attribute.
     */
    protected void newColorAttribute(int a, Instances i) {
        if (i.attribute(a).isNominal()) {
            for (int noa = m_colorList.size(); noa < i.attribute(a).numValues(); noa++) {
                Color pc = m_DefaultColors[noa % 10];
                int ija = noa / 10;
                ija *= 2;
                for (int j = 0; j < ija; j++) {
                    pc = pc.brighter();
                }

                m_colorList.add(pc);
            }
            m_plot.setColours(m_colorList);
            m_attrib.setColours(m_colorList);
            m_classPanel.setColours(m_colorList);
        }
    }

    /**
     * This will set the shapes for the instances.
     * 
     * @param l A list of the shapes, providing that the objects in the lists are
     *          non editable the data will be kept intact.
     */
    public void setShapes(ArrayList<ArrayList<Double>> l) {
        m_plot.setShapes(l);
    }

    /**
     * Tells the panel to use a new set of instances.
     * 
     * @param inst a set of Instances
     */
    public void setInstances(Instances inst) {
        if (inst.numAttributes() > 0 && inst.numInstances() > 0) {
            newColorAttribute(inst.numAttributes() - 1, inst);
        }

        PlotData2D temp = new PlotData2D(inst);
        temp.setPlotName(inst.relationName());

        try {
            setMasterPlot(temp);
        } catch (Exception ex) {
            System.err.println(ex);
            ex.printStackTrace();
        }
    }

    /**
     * initializes the comboboxes based on the data
     * 
     * @param inst the data to base the combobox-setup on
     */
    public void setUpComboBoxes(Instances inst) {
        setProperties(inst.relationName());
        int prefX = -1;
        int prefY = -1;
        if (inst.numAttributes() > 1) {
            prefY = 1;
        }
        int prefC = -1;
        String[] XNames = new String[inst.numAttributes()];
        String[] YNames = new String[inst.numAttributes()];
        String[] CNames = new String[inst.numAttributes()];
        for (int i = 0; i < XNames.length; i++) {
            String type = " (" + Attribute.typeToStringShort(inst.attribute(i)) + ")";
            XNames[i] = "X: " + inst.attribute(i).name() + type;
            YNames[i] = "Y: " + inst.attribute(i).name() + type;
            CNames[i] = "Colour: " + inst.attribute(i).name() + type;
            if (m_preferredXDimension != null) {
                if (m_preferredXDimension.compareTo(inst.attribute(i).name()) == 0) {
                    prefX = i;
                    // System.err.println("Found preferred X dimension");
                }
            }
            if (m_preferredYDimension != null) {
                if (m_preferredYDimension.compareTo(inst.attribute(i).name()) == 0) {
                    prefY = i;
                    // System.err.println("Found preferred Y dimension");
                }
            }
            if (m_preferredColourDimension != null) {
                if (m_preferredColourDimension.compareTo(inst.attribute(i).name()) == 0) {
                    prefC = i;
                    // System.err.println("Found preferred Colour dimension");
                }
            }
        }
        m_XCombo.setModel(new DefaultComboBoxModel(XNames));
        m_YCombo.setModel(new DefaultComboBoxModel(YNames));

        m_ColourCombo.setModel(new DefaultComboBoxModel(CNames));
        // m_ShapeCombo.setModel(new DefaultComboBoxModel(SNames));
        // m_ShapeCombo.setEnabled(true);
        m_XCombo.setEnabled(true);
        m_YCombo.setEnabled(true);

        if (m_splitListener == null) {
            m_ColourCombo.setEnabled(true);
            m_ColourCombo.setSelectedIndex(inst.numAttributes() - 1);
        }
        m_plotSurround.setBorder((BorderFactory.createTitledBorder("Plot: " + inst.relationName())));
        try {
            if (prefX != -1) {
                setXIndex(prefX);
            }
            if (prefY != -1) {
                setYIndex(prefY);
            }
            if (prefC != -1) {
                m_ColourCombo.setSelectedIndex(prefC);
            }
        } catch (Exception ex) {
            System.err.println("Problem setting preferred Visualization dimensions");
        }
    }

    /**
     * Removes all the plots.
     */
    public void removeAllPlots() {
        m_plot.removeAllPlots();
    }

    /**
     * Set the master plot for the visualize panel
     * 
     * @param newPlot the new master plot
     * @exception Exception if the master plot could not be set
     */
    public void setMasterPlot(PlotData2D newPlot) throws Exception {
        m_plot.setMasterPlot(newPlot);
        setUpComboBoxes(newPlot.m_plotInstances);
        m_saveBut.setEnabled(true);
        repaint();
    }

    /**
     * Set a new plot to the visualize panel
     * 
     * @param newPlot the new plot to add
     * @exception Exception if the plot could not be added
     */
    public void addPlot(PlotData2D newPlot) throws Exception {
        m_plot.addPlot(newPlot);
        if (m_plot.m_plot2D.getMasterPlot() != null) {
            setUpComboBoxes(newPlot.m_plotInstances);
        }
        m_saveBut.setEnabled(true);
        repaint();
    }

    /**
     * Returns the underlying plot panel.
     * 
     * @return the plot panel
     */
    public PlotPanel getPlotPanel() {
        return m_plot;
    }

    /**
     * Main method for testing this class
     * 
     * @param args the commandline parameters
     */
    public static void main(String[] args) {
        try {
            if (args.length < 1) {
                System.err.println(
                        "Usage : weka.gui.visualize.VisualizePanel " + "<dataset> [<dataset> <dataset>...]");
                System.exit(1);
            }

            weka.core.logging.Logger.log(weka.core.logging.Logger.Level.INFO, "Logging started");
            final javax.swing.JFrame jf = new javax.swing.JFrame("Weka Explorer: Visualize");
            jf.setSize(500, 400);
            jf.getContentPane().setLayout(new BorderLayout());
            final VisualizePanel sp = new VisualizePanel();

            jf.getContentPane().add(sp, BorderLayout.CENTER);
            jf.addWindowListener(new java.awt.event.WindowAdapter() {
                @Override
                public void windowClosing(java.awt.event.WindowEvent e) {
                    jf.dispose();
                    System.exit(0);
                }
            });

            jf.setVisible(true);
            if (args.length >= 1) {
                for (int j = 0; j < args.length; j++) {
                    System.err.println("Loading instances from " + args[j]);
                    java.io.Reader r = new java.io.BufferedReader(new java.io.FileReader(args[j]));
                    Instances i = new Instances(r);
                    i.setClassIndex(i.numAttributes() - 1);
                    PlotData2D pd1 = new PlotData2D(i);

                    if (j == 0) {
                        pd1.setPlotName("Master plot");
                        sp.setMasterPlot(pd1);
                    } else {
                        pd1.setPlotName("Plot " + (j + 1));
                        pd1.m_useCustomColour = true;
                        pd1.m_customColour = (j % 2 == 0) ? Color.red : Color.blue;
                        sp.addPlot(pd1);
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            System.err.println(ex.getMessage());
        }
    }
}