userinterface.graph.Graph3D.java Source code

Java tutorial

Introduction

Here is the source code for userinterface.graph.Graph3D.java

Source

//==============================================================================
//   
//   Copyright (c) 2016
//   Authors:
//   * Muhammad Omer Saeed <muhammad.omar555@gmail.com> (University of Bonn)
//   
//------------------------------------------------------------------------------
//   
//   This file is part of PRISM.
//   
//   PRISM 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 2 of the License, or
//   (at your option) any later version.
//   
//   PRISM 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 PRISM; if not, write to the Free Software Foundation,
//   Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//   
//==============================================================================

package userinterface.graph;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.itextpdf.awt.DefaultFontMapper;
import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;
import com.orsoncharts.Chart3D;
import com.orsoncharts.Chart3DFactory;
import com.orsoncharts.Chart3DPanel;
import com.orsoncharts.Range;
import com.orsoncharts.data.function.Function3D;
import com.orsoncharts.data.xyz.XYZDataItem;
import com.orsoncharts.data.xyz.XYZSeries;
import com.orsoncharts.data.xyz.XYZSeriesCollection;
import com.orsoncharts.graphics3d.swing.DisplayPanel3D;
import com.orsoncharts.plot.XYZPlot;
import com.orsoncharts.renderer.RainbowScale;
import com.orsoncharts.renderer.xyz.ScatterXYZRenderer;
import com.orsoncharts.renderer.xyz.SurfaceRenderer;
import com.orsoncharts.util.Orientation;

import prism.DefinedConstant;
import settings.ChoiceSetting;
import settings.ColorSetting;
import settings.DoubleSetting;
import settings.FontColorPair;
import settings.FontColorSetting;
import settings.MultipleLineStringSetting;
import settings.Setting;
import settings.SettingDisplay;
import settings.SettingException;
import settings.SettingOwner;
import userinterface.GUIPrism;
import userinterface.properties.GUIGraphHandler;

/**
 * Can be used to plot a 3d graph. Currently a scatter and surface plots (via Bilinear interpolation) are supported in 3d
 * @author Muhammad Omer Saeed
 */
public class Graph3D extends JPanel implements SettingOwner, EntityResolver, Observer, Printable {

    private static final long serialVersionUID = 1L;

    public static final int SCATTER = 1;
    public static final int SURFACE = 2;

    protected Chart3DPanel panel;
    protected DisplayPanel3D dPanel;
    protected Chart3D chart;
    protected XYZPlot plot;
    protected GUIGraphHandler graphHandler;
    protected String xLabel, yLabel, zLabel;

    /** Display for settings. Required to implement SettingsOwner */
    private SettingDisplay display;

    /**Settings for the x,y and z axes*/
    private AxisSettings3D xAxisSetting, yAxisSetting, zAxisSetting;

    private DisplaySettings3D displaySettings;

    /** Settings of this graph. */
    protected MultipleLineStringSetting graphTitle;
    private FontColorSetting titleFont;
    protected ChoiceSetting legendOrientation;
    private DoubleSetting rotateIncrement;
    private DoubleSetting rollIncrement;
    protected ChoiceSetting chartType;

    /**Settings specifically for scatter plot*/
    private DoubleSetting pointSize;
    private ColorSetting dataColor;

    /**Settings specifically for surface plot*/
    private ChoiceSetting scaleMethod;
    private ColorSetting lowColor;
    private ColorSetting highColor;

    /** For scatter plot */
    private ScatterXYZRenderer rendererScatter;
    private XYZSeries seriesScatter;
    private XYZSeriesCollection seriesCollectionScatter;

    /**For surface plot*/
    protected SurfaceRenderer rendererSurface;
    private DataInterpolateFunction function;
    private DefinedConstant rangingConstantX, rangingConstantY;

    /**For showing series options in the graph options*/
    private SeriesSettingsList seriesList;
    private SeriesSettings seriesSettings;

    /**This will hold our data for the plot*/
    private ArrayList<XYZDataItem> dataCache;

    protected int plotType;

    /**
     * Creates an empty 3d Graph 
     */
    public Graph3D() {
        initSettings();
    }

    /**
     * Initialize all the settings and set the properties
     */
    public void initSettings() {

        xAxisSetting = new AxisSettings3D("x axis settings", AxisSettings3D.XAXIS, this);
        yAxisSetting = new AxisSettings3D("y axis settings", AxisSettings3D.YAXIS, this);
        zAxisSetting = new AxisSettings3D("z axis settings", AxisSettings3D.ZAXIS, this);

        displaySettings = new DisplaySettings3D(this);
        displaySettings.addObserver(this);

        graphTitle = new MultipleLineStringSetting("title", "", "the main title heading for the chart", this,
                false);

        titleFont = new FontColorSetting("title font",
                new FontColorPair(new Font(Font.SANS_SERIF, Font.PLAIN, 11), Color.BLACK),
                "the font of the chart's title", this, false);

        legendOrientation = new ChoiceSetting("legend orientation", new String[] { "Horizontal", "Vertical" },
                "Horizontal", "change the orientation of the legend", this, false);
        legendOrientation.setEnabled(false);

        rotateIncrement = new DoubleSetting("Rotate increment", 1.0, "rotate increment value for the plot", this,
                false);
        rollIncrement = new DoubleSetting("roll increment", 1.0, "roll increment for the plot", this, false);

        chartType = new ChoiceSetting("Plot type", new String[] { "Scatter plot", "Surface plot" }, "Scatter plot",
                "select the plot type to visualize the data", this, false);

        scaleMethod = new ChoiceSetting("scale method",
                new String[] { "Rainbow scale", "Gradient scale", "GBR scale" }, "Rainbow scale",
                "change the scale method for the chart", this, false);

        lowColor = new ColorSetting("low color", Color.WHITE, "low color for the gradient scale", this, false);
        lowColor.setEnabled(false);

        highColor = new ColorSetting("high color", Color.BLACK, "high color of the gradient scale", this, false);
        highColor.setEnabled(false);

        pointSize = new DoubleSetting("Size of data points", 0.2, "change the size of data points in 3d space",
                this, false);
        dataColor = new ColorSetting("data color", Color.RED, "the color of the data points in 3d space", this,
                false);

        dataCache = new ArrayList<XYZDataItem>();

        seriesList = new SeriesSettingsList(this);
    }

    /**
     * Initializes the scatter plot
     */
    public void initScatterPlot() {

        seriesCollectionScatter = new XYZSeriesCollection();
        setLayout(new BorderLayout());
        plotType = SCATTER;
    }

    /**
     * Add new data to the current data cache
     * @param item the new data that has to be added
     */
    public void addPointToDataCache(XYZDataItem item) {
        dataCache.add(item);
    }

    /**
     * Plots the scatter plot with the given series name
     * @param seriesName the name of the series to be plotted
     */
    public void plotScatter(String seriesName) {

        seriesScatter = new XYZSeries(seriesName);

        for (XYZDataItem item : dataCache) {
            // copy data cache to our series
            seriesScatter.add(item);
        }

        seriesSettings = new SeriesSettings(this, new SeriesKey());

        seriesCollectionScatter.add(seriesScatter);
        chart = Chart3DFactory.createScatterChart("", "", seriesCollectionScatter, xLabel, zLabel, yLabel);
        plot = (XYZPlot) chart.getPlot();
        rendererScatter = (ScatterXYZRenderer) plot.getRenderer();
        panel = new Chart3DPanel(chart);
        panel.setComponentPopupMenu(graphHandler.getGraphMenu());
        dPanel = new DisplayPanel3D(panel, true, false);
        panel.addMouseListener(graphHandler);

        // make sure there is nothing inside this panel before we add our scatter plot
        {
            removeAll();
            revalidate();
            repaint();
        }

        add(dPanel, BorderLayout.CENTER);

        /*update all the settings*/
        updateGraph();
        displaySettings.updateDisplay();
        seriesList.updateSeriesList();
    }

    /**
     * Intializes the surface plot
     */
    public void initSurfacePlot() {

        function = new DataInterpolateFunction();
        setLayout(new BorderLayout());
        seriesSettings = null;
        plotType = SURFACE;
    }

    /**
     * Set the ranging constants for this graph. They will be used to set the range etc
     * 
     * @param rX
     * @param rY
     */
    public void setRangingConstants(DefinedConstant rX, DefinedConstant rY) {

        this.rangingConstantX = rX;
        this.rangingConstantY = rY;
    }

    /**
     * Plots the surface plot in the current graph panel
     */
    public void plotSurface() {

        chart = Chart3DFactory.createSurfaceChart("", "", function, xLabel, zLabel, yLabel);
        plot = (XYZPlot) chart.getPlot();
        plot.getXAxis().setRange(new Double(rangingConstantX.getLow().toString()),
                new Double(rangingConstantX.getHigh().toString()));
        plot.getZAxis().setRange(new Double(rangingConstantY.getLow().toString()),
                new Double(rangingConstantY.getHigh().toString()));
        rendererSurface = (SurfaceRenderer) plot.getRenderer();
        panel = new Chart3DPanel(chart);
        panel.setComponentPopupMenu(graphHandler.getGraphMenu());
        dPanel = new DisplayPanel3D(panel, true, false);
        panel.addMouseListener(graphHandler);

        // make sure there is nothing inside this panel
        {
            removeAll();
            revalidate();
            repaint();
        }

        add(dPanel, BorderLayout.CENTER);

        /*update all the settings*/
        updateGraph();
        displaySettings.updateDisplay();
    }

    /**
     * Set the title of the graph
     * @param title the title to set
     */
    public void setTitle(String title) {

        try {

            graphTitle.setValue(title);
        } catch (SettingException e) {
            e.printStackTrace();
        }

        updateGraph();
    }

    /**
     * Set the labels of the different axes
     * 
     * @param xLabel The x label
     * @param yLabel The y label
     * @param zLabel The z label
     */
    public void setAxisLabels(String xLabel, String yLabel, String zLabel) {

        this.xLabel = xLabel;
        this.yLabel = yLabel;
        this.zLabel = zLabel;
    }

    /**
     * Get the current 3d chart
     * @return
     */
    public Chart3D getChart() {
        return chart;
    }

    /**
     * Get the display panel for this chart
     * @return
     */
    public DisplayPanel3D getDisplayPanel() {
        return this.dPanel;
    }

    /**
     * Get the plot of the current chart
     * @return
     */
    public XYZPlot getPlot() {
        return plot;
    }

    /**
     * Get the surface renderer of the current chart
     * @return
     */
    public SurfaceRenderer getSurfaceRenderer() {
        return rendererSurface;
    }

    /**
     * Get the list of all the series (only valid for the scatter plot case)
     * @return
     */
    public SeriesSettingsList getGraphSeriesList() {
        return this.seriesList;
    }

    /**
     * 
     * @return
     */
    public SeriesSettings getSeriesSettings() {
        return seriesSettings;
    }

    /**
     * Return the series if the current plot is scatter {@code null} otherwise
     * @return
     */
    public XYZSeries getScatterSeries() {
        return this.seriesScatter;
    }

    /**
     * Returns the current type of the plot. Can be scatter or surface
     * @return
     */
    public int getPlotType() {
        return this.plotType;
    }

    @Override
    public int compareTo(Object o) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void update(Observable o, Object arg) {
        // TODO Auto-generated method stub

    }

    @Override
    public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
        InputSource inputSource = null;

        // override the resolve method for the dtd
        if (systemId.endsWith("dtd")) {
            // get appropriate dtd from classpath
            InputStream inputStream = Graph.class.getClassLoader().getResourceAsStream("dtds/chartformat.dtd");
            if (inputStream != null)
                inputSource = new InputSource(inputStream);
        }
        return inputSource;
    }

    @Override
    public int getSettingOwnerID() {
        return prism.PropertyConstants.MODEL;
    }

    @Override
    public String getSettingOwnerName() {
        return "";
    }

    @Override
    public String getSettingOwnerClassName() {
        return "Model";
    }

    @Override
    public int getNumSettings() {

        if (plotType == SCATTER) {
            return 8;
        } else if (plotType == SURFACE) {
            return 9;
        } else {
            return 0;

        }
    }

    @Override
    public Setting getSetting(int index) {

        switch (index) {

        case 0:
            return this.graphTitle;
        case 1:
            return this.titleFont;
        case 2:
            return this.legendOrientation;
        case 3:
            return this.rotateIncrement;
        case 4:
            return this.rollIncrement;
        case 5:
            return this.chartType;
        case 6:
            return plotType == SCATTER ? this.pointSize : this.scaleMethod;
        case 7:
            return plotType == SCATTER ? this.dataColor : this.lowColor;
        case 8:
            return plotType == SCATTER ? null : this.highColor;
        default:
            return null;

        }
    }

    @Override
    public void notifySettingChanged(Setting setting) {
        updateGraph();
    }

    @Override
    public void setDisplay(SettingDisplay display) {
        this.display = display;
    }

    @Override
    public SettingDisplay getDisplay() {
        return display;
    }

    /**
     * Update all the settings of the graph when user changes something
     */
    public void updateGraph() {

        /*graph title*/
        if (graphTitle.getStringValue().equals(this.chart.getTitle().toString())) {
            chart.setTitle(graphTitle.getStringValue());
        }

        /*graph title font*/
        {
            chart.setTitle(graphTitle.getStringValue(), titleFont.getFontColorValue().f,
                    titleFont.getFontColorValue().c);
        }

        /*legend orientation*/
        if (legendOrientation.getStringValue().equals("Horizontal")
                && (chart.getLegendOrientation() != Orientation.HORIZONTAL)) {
            chart.setLegendOrientation(Orientation.HORIZONTAL);
        } else if (legendOrientation.getStringValue().equals("Vertical")
                && (chart.getLegendOrientation() != Orientation.VERTICAL)) {
            chart.setLegendOrientation(Orientation.VERTICAL);
        }

        /*rotate increment*/
        if (rotateIncrement.getDoubleValue() != panel.getRotateIncrement()) {
            panel.setRotateIncrement(rotateIncrement.getDoubleValue());
        }

        /*roll increment*/
        if (rollIncrement.getDoubleValue() != panel.getRollIncrement()) {
            panel.setRollIncrement(rollIncrement.getDoubleValue());
        }

        /*chart type*/

        if (chartType.getStringValue().equals("Scatter plot") && plotType == SURFACE) {
            initScatterPlot();
            plotScatter("Series");
            plotType = SCATTER;
            legendOrientation.setEnabled(false);
        }

        if (chartType.getStringValue().equals("Surface plot") && plotType == SCATTER) {

            initSurfacePlot();
            function.setData(dataCache);
            plotSurface();
            plotType = SURFACE;
            legendOrientation.setEnabled(true);
        }

        /*scale method*/
        if (scaleMethod.getStringValue().equals("Rainbow scale") && plotType == SURFACE
                && rendererSurface != null) {

            lowColor.setEnabled(false);
            highColor.setEnabled(false);
            rendererSurface.setColorScale(new RainbowScale(plot.getYAxis().getRange()));
        } else if (scaleMethod.getStringValue().equals("Gradient scale") && plotType == SURFACE
                && rendererSurface != null) {

            lowColor.setEnabled(true);
            highColor.setEnabled(true);
            rendererSurface.setColorScale(new GradientColorScale(plot.getYAxis().getRange(),
                    lowColor.getColorValue(), highColor.getColorValue()));
        } else if (scaleMethod.getStringValue().equals("GBR scale") && plotType == SURFACE
                && rendererSurface != null) {

            lowColor.setEnabled(false);
            highColor.setEnabled(false);
            rendererSurface.setColorScale(new GBRColorScale(plot.getYAxis().getRange()));
        }

        /*low color*/

        if (lowColor.isEnabled() && plotType == SURFACE) {

            if (!lowColor.getColorValue()
                    .equals(((GradientColorScale) rendererSurface.getColorScale()).getLowColor())
                    && rendererSurface != null)
                rendererSurface.setColorScale(new GradientColorScale(new Range(0.0, 1.0), lowColor.getColorValue(),
                        highColor.getColorValue()));
        }

        /*high color*/

        if (highColor.isEnabled() && plotType == SURFACE && rendererSurface != null) {

            if (!highColor.getColorValue()
                    .equals(((GradientColorScale) rendererSurface.getColorScale()).getHighColor()))
                rendererSurface.setColorScale(new GradientColorScale(new Range(0.0, 1.0), lowColor.getColorValue(),
                        highColor.getColorValue()));
        }

        /*data point size*/

        if (plotType == SCATTER && rendererScatter != null) {

            if (rendererScatter.getSize() != pointSize.getDoubleValue()) {
                rendererScatter.setSize(pointSize.getDoubleValue());
            }

        }

        /*data point color*/
        if (plotType == SCATTER && rendererScatter != null) {

            if (!rendererScatter.getColorSource().getColor(0, 0).equals(dataColor.getColorValue())) {

                rendererScatter.setColors(dataColor.getColorValue());
            }
        }
    }

    @Override
    public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
        return 0;
    }

    /**
     * Return the x axis setting
     * @return
     */
    public AxisSettings3D getxAxisSetting() {
        return xAxisSetting;
    }

    /**
     * Return the y axis setting
     * @return
     */
    public AxisSettings3D getyAxisSetting() {
        return yAxisSetting;
    }

    /**
     * Return the z axis setting
     * @return
     */
    public AxisSettings3D getzAxisSetting() {
        return zAxisSetting;
    }

    /**
     * Return the display settings
     * @return
     */
    public DisplaySettings3D getDisplaySettings() {
        return displaySettings;
    }

    /**
     * 
     * @param gh
     */
    public void addMouseListener(GUIGraphHandler gh) {
        this.graphHandler = gh;
    }

    /**
     * Get the chart panel for this graph
     * @return
     */
    public Chart3DPanel getChart3DPanel() {
        return this.panel;
    }

    /**
     * Exports the 3d graph as a pdf
     * 
     * @param file The pdf file to which the data should be written
     * @param panel The chart panel that has to be exported
     */
    public static void exportToPDF(File file, Chart3DPanel panel) {

        PdfWriter out = null;
        Document document = new com.itextpdf.text.Document(PageSize.A4.rotate());

        int width = 800, height = 500;

        try {

            out = PdfWriter.getInstance(document, new FileOutputStream(file));
            document.open();
            PdfContentByte contentByte = out.getDirectContent();
            PdfTemplate template = contentByte.createTemplate(width, height);
            @SuppressWarnings("deprecation")
            Graphics2D graphics2d = template.createGraphics(width, height, new DefaultFontMapper());
            Rectangle2D rectangle2d = new Rectangle2D.Double(0, 0, width, height);

            panel.getChart().draw(graphics2d, rectangle2d);

            graphics2d.dispose();
            contentByte.addTemplate(template, 0, 0);

        } catch (Exception e) {
            // in case any error occurs tell the user what the error is (sometimes useful if there is a problem of writing rights)
            JOptionPane.showMessageDialog(GUIPrism.getGUI(), e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
            e.printStackTrace();
            return;
        }

        document.close();
    }

    /**
     * Exports the plot to a GNU plot readable file
     * 
     * @param file The file to which the data should be written
     * @throws IOException 
     */
    public void exportToGnuplot(File file) throws IOException {

        PrintWriter out = new PrintWriter(new FileWriter(file));

        out.println("#=========================================");
        out.println("# Generated by PRISM Chart Package");
        out.println("#=========================================");
        out.println("# usage: gnuplot <filename>");
        out.println("# Written by Muhammad Omer Saeed <muhammad.omar555@gmail.com>");

        out.println();

        /*setting general plot properties*/

        out.println("set xrange[" + rangingConstantX.getLow() + ":" + rangingConstantX.getHigh() + "]");
        out.println("set yrange[" + rangingConstantY.getLow() + ":" + rangingConstantY.getHigh() + "]");
        out.println("set title '" + this.graphTitle.getStringValue() + "'");
        out.println("set xlabel " + "\"" + this.xLabel + "\"");
        out.println("set ylabel " + "\"" + this.yLabel + "\"");
        out.println("set zlabel " + "\"probability\"");

        if (plotType == SURFACE) {

            /*setting graph properties for surface plot*/
            out.println("set hidden3d");
            out.println("set dgrid3d 50,50 qnorm 2");
            out.println("set pm3d");
            out.println("set palette model HSV defined ( 0 0 1 1, 1 1 1 1 )");

            out.println("splot '-' with lines");
            out.println();

            out.println("#X      #Y      #Z");
            out.println();

            double rateX = (plot.getXAxis().getRange().getMax() - plot.getXAxis().getRange().getMin())
                    / rendererSurface.getXSamples();
            double rateY = (plot.getZAxis().getRange().getMax() - plot.getZAxis().getRange().getMin())
                    / rendererSurface.getZSamples();

            /*Sample the function that will interpolate between the values*/

            for (double x = plot.getXAxis().getRange().getMin(); x <= plot.getXAxis().getRange()
                    .getMax(); x += rateX) {
                for (double y = plot.getZAxis().getRange().getMin(); y <= plot.getZAxis().getRange()
                        .getMax(); y += rateY) {

                    double z = function.getValue(x, y);

                    out.println(x + "      " + y + "      " + z);

                }
            }

        } else if (plotType == SCATTER) {

            /*setting graph properties for scatter plot*/

            out.println("splot '-' using 1:2:3 with points palette pointsize 1 pointtype 5");
            out.println();

            /*Writing data to file*/

            for (XYZDataItem item : dataCache) {

                out.println(item.getX() + "    " + item.getY() + "    " + item.getZ());

            }

        }

        /*finishing up*/
        out.println("end plot 3d");
        out.println();

        out.println("pause -1");

        /*closing the file safely*/
        out.flush();
        out.close();
    }

    /**
     * Exports the plot to a MATLAB readable file
     * @param file The file to which the data should be written
     * @throws IOException
     */
    public void exportToMatlab(File file) throws IOException {

        PrintWriter out = new PrintWriter(new FileWriter(file));

        out.println("%=========================================");
        out.println("% Generated by PRISM Chart Package");
        out.println("%=========================================");
        out.println("% usage: run <filename>");
        out.println("% Written by Muhammad Omer Saeed <muhammad.omar555@gmail.com>");

        /*writing data*/

        out.println("data = [");
        out.println("%X    %Y      %Z");

        if (plotType == SURFACE) {

            double rateX = (plot.getXAxis().getRange().getMax() - plot.getXAxis().getRange().getMin()) / 100.0;
            double rateY = (plot.getZAxis().getRange().getMax() - plot.getZAxis().getRange().getMin()) / 100.0;

            /*Sample the function that will interpolate between the values*/

            for (double x = plot.getXAxis().getRange().getMin(); x <= plot.getXAxis().getRange()
                    .getMax(); x += rateX) {
                for (double y = plot.getZAxis().getRange().getMin(); y <= plot.getZAxis().getRange()
                        .getMax(); y += rateY) {

                    double z = function.getValue(x, y);

                    out.println(x + "    " + y + "    " + z + ";");
                }
            }

            out.println("];");

            out.println("X = reshape(data(:,1),  " + 100.0 + ", []);");
            out.println("Y = reshape(data(:,2),  " + 100.0 + ", []);");
            out.println("Z = reshape(data(:,3),  " + 100.0 + ", []);");

            /*setting properties for surface*/

            out.println("figure;");
            out.println("hold on;");
            out.println("colormap(hsv);");
            out.println("surf(X, Y, Z);");
            out.println("title('" + graphTitle.getStringValue() + "');");
            out.println("xlabel(' " + this.xLabel + " ');");
            out.println("ylabel(' " + this.yLabel + "');");
            out.println("zlabel('Probability');");
            out.println("colorbar;");
            out.println("hold off;");

        } else if (plotType == SCATTER) {

            /*Writing data to file*/

            for (XYZDataItem item : dataCache) {

                out.println(item.getX() + "    " + item.getY() + "    " + item.getZ());

            }

            out.println("];");

            /*setting properties for scatter*/
            out.println("figure;");
            out.println("hold on;");
            out.println("scatter3(data(:,1), data(:,2), data(:,3));");
            out.println("xlabel(' " + this.xLabel + " ');");
            out.println("ylabel(' " + this.yLabel + "');");
            out.println("zlabel('Probability');");
            out.println("hold off;");
        }

        /*finishing up*/
        out.flush();
        out.close();
    }

    /**
     * Creates a print job for this graph
     */
    public void createPrintJob() {
        //TODO Haven't found a clean way to do this yet
    }

    /**
     * Custom function class that takes discrete data and provides continuous data to the plotter for
     * creating a surface using the bi-linear interpolation
     *  
     * @author Muhammad Omer Saeed
     */
    private class DataInterpolateFunction implements Function3D {

        private static final long serialVersionUID = -4229820807542439735L;
        private double[][] data;
        private double xLow, xHigh, xStep, yLow, yHigh, yStep;
        int xNumSteps, yNumSteps;

        /**
         * Creates a new function with default values set
         */
        public DataInterpolateFunction() {

            this.xLow = (double) rangingConstantX.getLow();
            this.xHigh = (double) rangingConstantX.getHigh();
            this.xStep = (double) rangingConstantX.getStep();
            this.xNumSteps = rangingConstantX.getNumSteps();

            this.yLow = (double) rangingConstantY.getLow();
            this.yHigh = (double) rangingConstantY.getHigh();
            this.yStep = (double) rangingConstantY.getStep();
            this.yNumSteps = rangingConstantY.getNumSteps();
        }

        /**
         * Set the data that will be used for interpolation
         * @param cache
         */
        public void setData(ArrayList<XYZDataItem> cache) {

            data = new double[rangingConstantX.getNumSteps()][rangingConstantY.getNumSteps()];

            int index = 0;

            // data has to be reshaped to a grid for easy access
            for (int i = 0; i < rangingConstantX.getNumSteps(); i++) {
                for (int j = 0; j < rangingConstantY.getNumSteps(); j++) {

                    data[i][j] = cache.get(index++).getY();
                }
            }

        }

        /**
         * Print the reshaped data, used for debugging
         */
        public void printData() {

            int index = 0;

            for (int i = 0; i < rangingConstantX.getNumSteps(); i++) {
                for (int j = 0; j < rangingConstantY.getNumSteps(); j++) {

                    XYZDataItem item = dataCache.get(index++);
                    double x = item.getX();
                    double y = item.getZ();

                    System.out.println("(" + x + "," + y + ")" + " = " + data[i][j] + " ");
                }
            }
        }

        /**
         * Gets the interpolated value at position x and y
         */
        @Override
        public double getValue(double x, double y) {

            if (x < xLow || x > xHigh || y < yLow || y > yHigh) {
                return 0.0;
            }

            int xIndexLow = (int) ((x - xLow) / xStep) > xNumSteps - 1 ? xNumSteps - 1 : (int) ((x - xLow) / xStep);
            int xIndexHigh = xIndexLow + 1 > xNumSteps - 1 ? xIndexLow : xIndexLow + 1;
            int yIndexLow = (int) ((y - yLow) / yStep) > yNumSteps - 1 ? yNumSteps - 1 : (int) ((y - yLow) / yStep);
            int yIndexHigh = yIndexLow + 1 > yNumSteps - 1 ? yIndexLow : yIndexLow + 1;

            // if true then we just need to do linear interpolation in y
            if (xIndexLow == xIndexHigh) {
                return data[xIndexLow][yIndexLow]
                        + ((data[xIndexLow][yIndexHigh] - data[xIndexLow][yIndexLow]) / (yHigh - yLow))
                                * (y - yLow);

            }

            //if true we just need to do linear interpolation in x
            if (yIndexLow == yIndexHigh) {

                return data[xIndexLow][yIndexLow]
                        + ((data[xIndexHigh][yIndexLow] - data[xIndexLow][yIndexLow]) / (xHigh - xLow))
                                * (x - xLow);
            }

            // bilinear interpolation see (https://en.wikipedia.org/wiki/Bilinear_interpolation)
            double biLinearlyInterpolated = ((1.0) / ((xHigh - xLow) * (yHigh - yLow)))
                    * ((data[xIndexLow][yIndexLow] * (xHigh - x) * (yHigh - y))
                            + (data[xIndexHigh][yIndexLow] * (x - xLow) * (yHigh - y))
                            + (data[xIndexLow][yIndexHigh] * (xHigh - x) * (y - yLow))
                            + (data[xIndexHigh][yIndexHigh] * (x - xLow) * (y - yLow)));

            return biLinearlyInterpolated;

        }

    }
}