Java tutorial
/* * The MIT License * * Copyright 2015 Hilmar. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package genlib.output.gui; import genlib.abstractrepresentation.GenObject; import genlib.extended.distributions.BasicTypeDistributions.MinMaxDouble; import genlib.extended.distributions.BasicTypeDistributions.MinMaxLong; import genlib.utils.Exceptions.GeneticRuntimeException; import genlib.utils.MergeOperator; import genlib.utils.Utils; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.GridLayout; import java.awt.Toolkit; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.WindowConstants; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.category.LineAndShapeRenderer; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultIntervalCategoryDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.ui.ApplicationFrame; import org.jfree.ui.RefineryUtilities; /** * This is an interface to the JFreeChart-Library to display graphs within GenLib * * @author Hilmar */ public class Graph2D extends ApplicationFrame { /** * Open a windows with the specified graph inside * * @param title the window-title */ private Graph2D(String title) { super(title); //we don't want the whole program to close setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); } @Override public void windowClosing(java.awt.event.WindowEvent event) { } /** * open a window with one plot * * @param plot the plot * @param xAxisName the name of the x-axis * @param yAxisName the name of the y-axis * @return the window-object */ public static Graph2D open(Plot plot, String xAxisName, String yAxisName) { if (plot instanceof Plot2DDiscreteX) return open(PlotCollection.createDiscretePointsChart(plot.title, xAxisName, yAxisName, (Plot2DDiscreteX) plot)); else return open(PlotCollection.createContinuousPointsChart(plot.title, xAxisName, yAxisName, (Plot2DContinuousX) plot)); } /** * open a window with one or more plot-collections * * @param plotCollections the plot-collections, can have null's inside,then there is empty space on the window * @return the window-object * @throws IllegalArgumentException if 0 plots are specified */ public static Graph2D open(PlotCollection... plotCollections) { if (plotCollections == null || plotCollections.length == 0) throw new IllegalArgumentException("there have to be at least 1 plot-collection."); //calculate the count of rows and cols int rows = 1; int cols = 1; while (rows * cols < plotCollections.length) { if (cols == rows) cols++; else rows++; } //we have to fill the rest with null PlotCollection[] completePlotCollections = new PlotCollection[rows * cols]; System.arraycopy(plotCollections, 0, completePlotCollections, 0, plotCollections.length); return open(rows, cols, 10, (plotCollections.length == 1 ? 0.5 : 0.75), completePlotCollections); } /** * open a window with one or more plot-collections * * @param rows count of rows * @param cols count of columns * @param gap size of gaps between Plots (in pixel) * @param percentSizeOfScreen the size of the complete window in per-cent of the windows * @param plots the plot-collections, can have null's inside,then there is empty space on the window * @return the window-object * @throws IllegalArgumentException if rows or cols lower than 1, gap lower than 0, percentSizeOfScreen lower than 0.001 or higher than 1 or number of plots not the same as rows*cols */ public static Graph2D open(int rows, int cols, int gap, double percentSizeOfScreen, PlotCollection... plots) { if (rows <= 0) throw new IllegalArgumentException("invalid rows-count: '" + rows + "'."); if (cols <= 0) throw new IllegalArgumentException("invalid cols-count: '" + cols + "'."); if (gap < 0) throw new IllegalArgumentException("invalid gap: '" + gap + "'."); if (percentSizeOfScreen < 0.001 || percentSizeOfScreen > 1) throw new IllegalArgumentException("invalid percentSizeOfScreen: '" + percentSizeOfScreen + "'."); if (plots == null || plots.length != rows * cols) throw new IllegalArgumentException("length of plots is not rows*cols."); //the window-title is a concatenation of all plots //but first, we have to ignore all null-plots String windowTitle = ""; List<PlotCollection> plotsNotNull = new ArrayList(); for (PlotCollection plot : plots) if (plot != null) plotsNotNull.add(plot); if (plotsNotNull.isEmpty()) windowTitle = "no plots"; else for (int i = 0; i < plotsNotNull.size(); i++) windowTitle += (i > 0 ? ", " : "") + plotsNotNull.get(i).title; //create the window Graph2D ret = new Graph2D(windowTitle); //and create the row-col-layout JPanel grid = new JPanel(); grid.setLayout(new GridLayout(rows, cols, gap, gap)); for (PlotCollection plot : plots) if (plot == null) grid.add(new JLabel("")); else grid.add(plot.getGUIElement()); //some other attributes of the window grid.setBackground(Color.BLACK); ret.setContentPane(grid); ret.pack(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); ret.setSize(new Dimension((int) (screenSize.getWidth() * percentSizeOfScreen), (int) (screenSize.getHeight() * percentSizeOfScreen))); RefineryUtilities.centerFrameOnScreen(ret); ret.setVisible(true); return ret; } /** * a PlotCollection stores one or more plots and displays * them in one graph with one x-axis and one y-axis */ public static class PlotCollection extends GenObject { /** * the graph-type */ protected enum Type { JustLines, JustPoints, LineAndPoints, BarCharts }; /** * the graph-type */ protected final Type type; /** * the title */ protected final String title; /** * the description of the x-axis and y-axis */ protected final String xAxis, yAxis; /** * just needed for discrete chart: If the plots don't * have the exact same discrete keys, we need a standard- * value for this case */ protected final double valueForMissingData; /** * all the discrete plots */ protected final Plot2DDiscreteX[] plots2DDiscreteX; /** * all the non-discrete plots */ protected final Plot2DContinuousX[] plots2DContinuousX; /** * the x-axis and y-axis can be converted to integer-values only */ protected final boolean xAxisDiscreteInts, yAxisDiscreteInts; /** * the constructor, just available for creation-methods * * @param _type the graph-type * @param _title the title * @param _xAxis description x-axis * @param _yAxis description y-axis * @param _valueForMissingData just needed for discrete chart: If the plots don't have the exact same discrete keys, we need a standard-value for this case * @param scaleY scales all plots to this min and max value, can be null for no scaling * @param _xAxisDiscreteInts the x-axis can be converted to integer-values only * @param _yAxisDiscreteInts the y-axis can be converted to integer-values only * @param _plots2DDiscreteX all the discrete plots * @param _plots2DContinuousX all the non-discrete plots * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ private PlotCollection(Type _type, String _title, String _xAxis, String _yAxis, double _valueForMissingData, MinMaxDouble scaleY, boolean _xAxisDiscreteInts, boolean _yAxisDiscreteInts, Plot2DDiscreteX[] _plots2DDiscreteX, Plot2DContinuousX[] _plots2DContinuousX) { type = _type; title = _title; xAxis = _xAxis; yAxis = _yAxis; valueForMissingData = _valueForMissingData; plots2DDiscreteX = _plots2DDiscreteX; plots2DContinuousX = _plots2DContinuousX; xAxisDiscreteInts = _xAxisDiscreteInts; yAxisDiscreteInts = _yAxisDiscreteInts; if (title == null) throw new NullPointerException("title cannot be null."); if (xAxis == null) throw new NullPointerException("xAxis-name cannot be null."); if (yAxis == null) throw new NullPointerException("yAxis-name cannot be null."); if (plots2DDiscreteX != null) { if (plots2DDiscreteX.length == 0) throw new IllegalArgumentException("there has to be at least 1 plot."); //every title is just allowed one time Set<String> titlesInUse = new HashSet(); for (int i = 0; i < plots2DDiscreteX.length; i++) { String title = plots2DDiscreteX[i].getTitle(); if (titlesInUse.contains(title)) for (int j = 2; true; j++) if (!titlesInUse.contains(title + " (" + j + ")")) { plots2DDiscreteX[i] = new Plot2DDiscreteX(plots2DDiscreteX[i], title + " (" + j + ")"); break; } titlesInUse.add(plots2DDiscreteX[i].getTitle()); if (scaleY != null) plots2DDiscreteX[i].scaleYAxis(scaleY.getMin(), scaleY.getMax()); plots2DDiscreteX[i].finish(); } } if (plots2DContinuousX != null) { if (plots2DContinuousX.length == 0) throw new IllegalArgumentException("there has to be at least 1 plot."); //every title is just allowed one time Set<String> titlesInUse = new HashSet(); for (int i = 0; i < plots2DContinuousX.length; i++) { String title = plots2DContinuousX[i].getTitle(); if (titlesInUse.contains(title)) for (int j = 2; true; j++) if (!titlesInUse.contains(title + " (" + j + ")")) { plots2DContinuousX[i] = new Plot2DContinuousX(plots2DContinuousX[i], title + " (" + j + ")"); break; } titlesInUse.add(plots2DContinuousX[i].getTitle()); if (scaleY != null) plots2DContinuousX[i].scaleYAxis(scaleY.getMin(), scaleY.getMax()); plots2DContinuousX[i].finish(); } } } /** * the creation-method for a non-discrete line-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createContinuousLineChart(String title, String xAxis, String yAxis, Plot2DContinuousX... plots) { return new PlotCollection(Type.JustLines, title, xAxis, yAxis, -1.0, null, false, false, null, plots); } /** * the creation-method for a non-discrete points-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createContinuousPointsChart(String title, String xAxis, String yAxis, Plot2DContinuousX... plots) { return new PlotCollection(Type.JustPoints, title, xAxis, yAxis, -1.0, null, false, false, null, plots); } /** * the creation-method for a non-discrete line-and-points-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createContinuousLineAndPointsChart(String title, String xAxis, String yAxis, Plot2DContinuousX... plots) { return new PlotCollection(Type.LineAndPoints, title, xAxis, yAxis, -1.0, null, false, false, null, plots); } /** * the creation-method for a discrete line-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createDiscreteLineChart(String title, String xAxis, String yAxis, Plot2DDiscreteX... plots) { return new PlotCollection(Type.JustLines, title, xAxis, yAxis, 0.0, null, false, false, plots, null); } /** * the creation-method for a discrete points-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createDiscretePointsChart(String title, String xAxis, String yAxis, Plot2DDiscreteX... plots) { return new PlotCollection(Type.JustPoints, title, xAxis, yAxis, 0.0, null, false, false, plots, null); } /** * the creation-method for a discrete line-and-points-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createDiscreteLineAndPointsChart(String title, String xAxis, String yAxis, Plot2DDiscreteX... plots) { return new PlotCollection(Type.LineAndPoints, title, xAxis, yAxis, 0.0, null, false, false, plots, null); } /** * the creation-method for a discrete bar-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createBarChart(String title, String xAxis, String yAxis, Plot2DDiscreteX... plots) { return new PlotCollection(Type.BarCharts, title, xAxis, yAxis, 0.0, null, false, false, plots, null); } /** * the creation-method for a non-discrete line-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param scaleY scales all plots to this min and max value, can be null for no scaling * @param xAxisDiscreteInts the x-axis can be converted to integer-values only * @param yAxisDiscreteInts the y-axis can be converted to integer-values only * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createContinuousLineChart(String title, String xAxis, String yAxis, MinMaxDouble scaleY, boolean xAxisDiscreteInts, boolean yAxisDiscreteInts, Plot2DContinuousX... plots) { return new PlotCollection(Type.JustLines, title, xAxis, yAxis, -1.0, scaleY, xAxisDiscreteInts, yAxisDiscreteInts, null, plots); } /** * the creation-method for a non-discrete points-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param scaleY scales all plots to this min and max value, can be null for no scaling * @param xAxisDiscreteInts the x-axis can be converted to integer-values only * @param yAxisDiscreteInts the y-axis can be converted to integer-values only * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createContinuousPointsChart(String title, String xAxis, String yAxis, MinMaxDouble scaleY, boolean xAxisDiscreteInts, boolean yAxisDiscreteInts, Plot2DContinuousX... plots) { return new PlotCollection(Type.JustPoints, title, xAxis, yAxis, -1.0, scaleY, xAxisDiscreteInts, yAxisDiscreteInts, null, plots); } /** * the creation-method for a non-discrete line-and-points-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param scaleY scales all plots to this min and max value, can be null for no scaling * @param xAxisDiscreteInts the x-axis can be converted to integer-values only * @param yAxisDiscreteInts the y-axis can be converted to integer-values only * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createContinuousLineAndPointsChart(String title, String xAxis, String yAxis, MinMaxDouble scaleY, boolean xAxisDiscreteInts, boolean yAxisDiscreteInts, Plot2DContinuousX... plots) { return new PlotCollection(Type.LineAndPoints, title, xAxis, yAxis, -1.0, scaleY, xAxisDiscreteInts, yAxisDiscreteInts, null, plots); } /** * the creation-method for a discrete line-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param scaleY scales all plots to this min and max value, can be null for no scaling * @param valueForMissingData If the plots don't have the exact same discrete keys, we need a standard-value for this case * @param xAxisDiscreteInts the x-axis can be converted to integer-values only * @param yAxisDiscreteInts the y-axis can be converted to integer-values only * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createDiscreteLineChart(String title, String xAxis, String yAxis, MinMaxDouble scaleY, double valueForMissingData, boolean xAxisDiscreteInts, boolean yAxisDiscreteInts, Plot2DDiscreteX... plots) { return new PlotCollection(Type.JustLines, title, xAxis, yAxis, valueForMissingData, scaleY, xAxisDiscreteInts, yAxisDiscreteInts, plots, null); } /** * the creation-method for a discrete points-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param scaleY scales all plots to this min and max value, can be null for no scaling * @param valueForMissingData If the plots don't have the exact same discrete keys, we need a standard-value for this case * @param xAxisDiscreteInts the x-axis can be converted to integer-values only * @param yAxisDiscreteInts the y-axis can be converted to integer-values only * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createDiscretePointsChart(String title, String xAxis, String yAxis, MinMaxDouble scaleY, double valueForMissingData, boolean xAxisDiscreteInts, boolean yAxisDiscreteInts, Plot2DDiscreteX... plots) { return new PlotCollection(Type.JustPoints, title, xAxis, yAxis, valueForMissingData, scaleY, xAxisDiscreteInts, yAxisDiscreteInts, plots, null); } /** * the creation-method for a discrete line-and-points-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param scaleY scales all plots to this min and max value, can be null for no scaling * @param valueForMissingData If the plots don't have the exact same discrete keys, we need a standard-value for this case * @param xAxisDiscreteInts the x-axis can be converted to integer-values only * @param yAxisDiscreteInts the y-axis can be converted to integer-values only * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createDiscreteLineAndPointsChart(String title, String xAxis, String yAxis, MinMaxDouble scaleY, double valueForMissingData, boolean xAxisDiscreteInts, boolean yAxisDiscreteInts, Plot2DDiscreteX... plots) { return new PlotCollection(Type.LineAndPoints, title, xAxis, yAxis, valueForMissingData, scaleY, xAxisDiscreteInts, yAxisDiscreteInts, plots, null); } /** * the creation-method for a discrete bar-chart * * @param title the title * @param xAxis description x-axis * @param yAxis description y-axis * @param scaleY scales all plots to this min and max value, can be null for no scaling * @param valueForMissingData If the plots don't have the exact same discrete keys, we need a standard-value for this case * @param xAxisDiscreteInts the x-axis can be converted to integer-values only * @param yAxisDiscreteInts the y-axis can be converted to integer-values only * @param plots the plots * @return the requested plot-collection * @throws IllegalArgumentException if we have 0 plots * @throws NullPointerException if title, x-axis-name or y-axis-name is null */ public static PlotCollection createBarChart(String title, String xAxis, String yAxis, MinMaxDouble scaleY, double valueForMissingData, boolean xAxisDiscreteInts, boolean yAxisDiscreteInts, Plot2DDiscreteX... plots) { return new PlotCollection(Type.BarCharts, title, xAxis, yAxis, valueForMissingData, scaleY, xAxisDiscreteInts, yAxisDiscreteInts, plots, null); } /** * returns, if this plot-collection has lines (the type of graph) * * @return true, if it has lines */ protected boolean hasLines() { return type == Type.JustLines || type == Type.LineAndPoints; } /** * returns, if this plot-collection has points (the type of graph) * * @return true, if it has points */ protected boolean hasPoints() { return type == Type.JustPoints || type == Type.LineAndPoints; } /** * this method constructs the GUI-element of the graph. We use * the JFreeChart-Library for this. * * @return the GUI-object */ protected ChartPanel getGUIElement() { JFreeChart chart = null; //even if the continuous and discrete graphs have many similiarities, //the differences are too large - so we have two complete case-handlings if (plots2DContinuousX != null) { XYSeriesCollection seriesCollection = new XYSeriesCollection(); for (Plot2DContinuousX plot : plots2DContinuousX) seriesCollection.addSeries(plot.getGraphData()); //the line / points / line-and-points graphs are in general the same graph with a different option set chart = ChartFactory.createXYLineChart(title, xAxis, yAxis, seriesCollection, PlotOrientation.VERTICAL, true, true, false); XYPlot plot = chart.getXYPlot(); plot.setBackgroundPaint(new Color(230, 230, 230)); plot.setDomainGridlinePaint(Color.white); plot.setRangeGridlinePaint(Color.white); //set the lines / points visible or not XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); for (int i = 0; i < plots2DContinuousX.length; i++) { renderer.setSeriesLinesVisible(i, hasLines()); renderer.setSeriesShapesVisible(i, hasPoints()); } plot.setRenderer(renderer); //in the non-discrete case we just set the two axis' to integer-ticks if (xAxisDiscreteInts) ((NumberAxis) plot.getDomainAxis()).setStandardTickUnits(NumberAxis.createIntegerTickUnits()); if (yAxisDiscreteInts) ((NumberAxis) plot.getRangeAxis()).setStandardTickUnits(NumberAxis.createIntegerTickUnits()); } else { Comparable[] plotTitles = new Comparable[plots2DDiscreteX.length]; List<Comparable> keys = new ArrayList(); //calculate the discrete keys of all plots for (int i = 0; i < plots2DDiscreteX.length; i++) { Plot2DDiscreteX plot = plots2DDiscreteX[i]; plotTitles[i] = plot.title; for (Plot2DDiscreteX.Pt pt : plot.pts) { Comparable key = pt.x; //in the case of a x-axis with integers, we have to convert the keys too if (xAxisDiscreteInts && key instanceof Number) key = ((Number) key).longValue(); if (!keys.contains(key)) keys.add(key); } } Comparable[] plotKeys = keys.toArray(new Comparable[keys.size()]); //you can imagine the start- and end-values as the start and the end of bars in a bar-chart Number[][] startValues = new Number[plots2DDiscreteX.length][plotKeys.length]; Number[][] endValues = new Number[plots2DDiscreteX.length][plotKeys.length]; for (int i = 0; i < plots2DDiscreteX.length; i++) { Arrays.fill(startValues[i], 0); Arrays.fill(endValues[i], valueForMissingData); for (Plot2DDiscreteX.Pt pt : plots2DDiscreteX[i].pts) { Comparable key = pt.x; if (xAxisDiscreteInts && key instanceof Number) key = ((Number) key).longValue(); endValues[i][keys.indexOf(key)] = pt.y; } } //the data-representation for JFreeChart-Library String[] formattedPlotKeys = new String[plotKeys.length]; for (int i = 0; i < plotKeys.length; i++) { if (plotKeys[i] instanceof Double) formattedPlotKeys[i] = Utils.doubleToCompactStr((Double) plotKeys[i]); else if (plotKeys[i] instanceof Float) formattedPlotKeys[i] = Utils.doubleToCompactStr((Float) plotKeys[i]); else formattedPlotKeys[i] = plotKeys[i].toString(); } CategoryDataset dataSet = new DefaultIntervalCategoryDataset(plotTitles, formattedPlotKeys, startValues, endValues); //the bar-chart is a different chart-type if (type == Type.BarCharts) chart = ChartFactory.createBarChart(title, xAxis, yAxis, dataSet, PlotOrientation.VERTICAL, true, true, false); else chart = ChartFactory.createLineChart(title, xAxis, yAxis, dataSet, PlotOrientation.VERTICAL, true, true, false); CategoryPlot plot = chart.getCategoryPlot(); plot.setBackgroundPaint(new Color(230, 230, 230)); plot.setDomainGridlinePaint(Color.white); plot.setRangeGridlinePaint(Color.white); //set the lines / points (but just if we don't have a bar-chart) if (type != Type.BarCharts) { LineAndShapeRenderer renderer = new LineAndShapeRenderer(); for (int i = 0; i < plots2DDiscreteX.length; i++) { renderer.setSeriesLinesVisible(i, hasLines()); renderer.setSeriesShapesVisible(i, hasPoints()); } plot.setRenderer(renderer); } //the xAxisDiscreteInts did we already handle with the integer-keys if (yAxisDiscreteInts) ((NumberAxis) plot.getRangeAxis()).setStandardTickUnits(NumberAxis.createIntegerTickUnits()); //cause of a bug in the JFreeChart-Libray (at least I think it is a bug ..) //the range of the y-axis has to be specified manually double highestY = Double.NEGATIVE_INFINITY, lowestY = Double.POSITIVE_INFINITY; for (int i = 0; i < plots2DDiscreteX.length; i++) for (Plot2DDiscreteX.Pt pt : plots2DDiscreteX[i].pts) { if (pt.y > highestY) highestY = pt.y; if (pt.y < lowestY) lowestY = pt.y; } ((NumberAxis) plot.getRangeAxis()).setRange(new Range(lowestY, highestY)); } ChartPanel chartPanel = new ChartPanel(chart); chartPanel.setPreferredSize(new Dimension(640, 400)); return chartPanel; } @Override public List<Attribute> getAttributes() { return Utils.createList( new Attribute(new AttributeType(AttributeType.Type.MainAttribute), "type", type), new Attribute(new AttributeType(AttributeType.Type.Descriptor), "title", title), new Attribute(new AttributeType(AttributeType.Type.NormalAttribute), "xAxis", xAxis), new Attribute(new AttributeType(AttributeType.Type.NormalAttribute), "yAxis", yAxis), new Attribute( new AttributeType((plots2DDiscreteX != null ? AttributeType.Type.NormalAttribute : AttributeType.Type.TemporaryOrUnimportant)), "valueForMissingData", valueForMissingData), new Attribute( new AttributeType((plots2DDiscreteX != null ? AttributeType.Type.NormalAttribute : AttributeType.Type.TemporaryOrUnimportant)), "plots2DDiscreteX", plots2DDiscreteX), new Attribute( new AttributeType((plots2DContinuousX != null ? AttributeType.Type.NormalAttribute : AttributeType.Type.TemporaryOrUnimportant)), "plots2DContinuousX", plots2DContinuousX), new Attribute(new AttributeType(AttributeType.Type.NormalAttribute), "xAxisDiscreteInts", xAxisDiscreteInts), new Attribute(new AttributeType(AttributeType.Type.NormalAttribute), "yAxisDiscreteInts", yAxisDiscreteInts)); } } /** * this class contains a single plot */ public static abstract class Plot extends GenObject { /** * the title of the plots */ protected final String title; /** * it is not allowed to add anymore values, after the * finish()-method is called once */ protected boolean inUse = false; /** * the constructor * * @param _title the title */ protected Plot(String _title) { title = _title; } /** * after finishing, the plot-values should not be changed anymore */ protected void finish() { inUse = true; } /** * gets the title. * * @return the title */ public String getTitle() { return title; } /** * This method modifies the y-values of the points, so they * will be in the range [0.0, 1.0] * * @throws GeneticRuntimeException if these plot is already in use */ public void normalize() { scaleYAxis(0, 1); } /** * This method modifies the y-values of the points, so they * will be in the range [min, max] * * @param min the smallest value on the y-axis * @param max the largest value on the y-axis * @throws GeneticRuntimeException if these plot is already in use * @throws IllegalArgumentException if min or max are NaN or infinte or if max smaller than min */ public abstract void scaleYAxis(double min, double max); /** * get the minimum and the maximum values on the x-axis. * If there are points with a double-value, there index * will count as x-value. * * @return the min-max-informations */ public abstract MinMaxDouble getMinMaxXAxis(); /** * get the minimum and the maximum values on the y-axis. * * @return the min-max-informations */ public abstract MinMaxDouble getMinMaxYAxis(); @Override public List<Attribute> getAttributes() { return Utils.createList(new Attribute(new AttributeType(AttributeType.Type.Descriptor), "title", title), new Attribute(new AttributeType(AttributeType.Type.TemporaryOrUnimportant), "inUse", inUse)); } } /** * a discrete plot */ public static class Plot2DDiscreteX extends Plot { /** * the discrete points in insert-order */ protected List<Pt> pts = new ArrayList(); /** * the constructor * * @param _title the title */ public Plot2DDiscreteX(String _title) { super(_title); } /** * the constructor * * @param _title the title * @param ptMap an initializing map of points */ public Plot2DDiscreteX(String _title, Map<Comparable, Double> ptMap) { super(_title); List<Comparable> xValues = new ArrayList(); xValues.addAll(ptMap.keySet()); //we sort the values, because there is no given order in the input-map Collections.sort(xValues); for (Comparable x : xValues) add(x, ptMap.get(x)); } /** * creates of copy of the specified plot. The new plot * won't be in use, will have a deepcopy of the points and * can have a new title. * * @param from the origin plot * @param newTitle the new title */ public Plot2DDiscreteX(Plot2DDiscreteX from, String newTitle) { super(newTitle); pts.addAll(from.pts); } /** * adds a new point * * @param _x x * @param _y y * @throws GeneticRuntimeException if this method is called, after the plot is finished */ public void add(Comparable _x, double _y) { if (inUse) throw new GeneticRuntimeException( "this plot is already in use - you can't add any points anymore."); pts.add(new Pt(_x, _y)); } /** * A discretized graph can have per discretized group still many different * values. The standard behavior is, that a random value will represent the group. * With this method, you can choose, which value should represent the group * * @param mergeOp an operator, that merges a set of double-values (the values on the y-axis) */ public void mergePointsInSameDiscretizedGroup(MergeOperator mergeOp) { List<Pt> newPts = new ArrayList(); //in every iteration-step, find the group with same x-values as the first point in pts while (pts.size() > 0) { List<Double> valuesInSameGroup = new ArrayList(Arrays.asList(pts.get(0).y)); for (int i = 1; i < pts.size(); i++) { if (pts.get(i).x.equals(pts.get(0).x)) { valuesInSameGroup.add(pts.remove(i).y); i--; } } //do the merge newPts.add(new Pt(pts.remove(0).x, mergeOp.merge(valuesInSameGroup))); } pts = newPts; } /** * converts this plot in a continuous plot. If the * x-values are not numbers, they will get numerated: * 0, 1, 2, ... * * @return this plot in continuous form */ public Plot2DContinuousX convertToContinuousPlot() { double[] points = new double[pts.size() * 2]; for (int i = 0; i < pts.size(); i++) { if (pts.get(i).x instanceof Number) points[i * 2] = ((Number) pts.get(i).x).doubleValue(); else points[i * 2] = i; points[i * 2 + 1] = pts.get(i).y; } return new Plot2DContinuousX(title, points); } @Override public void scaleYAxis(double min, double max) { if (inUse) throw new GeneticRuntimeException("this plot is already in use - you can't modify points anymore."); if (!Double.isFinite(min)) throw new IllegalArgumentException("min has to be finite (not NaN and not infinte)"); if (!Double.isFinite(max)) throw new IllegalArgumentException("max has to be finite (not NaN and not infinte)"); if (max < min) throw new IllegalArgumentException("max has to be >= min"); MinMaxDouble minMaxY = getMinMaxYAxis(); for (int i = 0; i < pts.size(); i++) pts.set(i, new Pt(pts.get(i).x, ((pts.get(i).y - minMaxY.getMin()) / minMaxY.getDistance()) * (max - min) + min)); } @Override public MinMaxDouble getMinMaxXAxis() { if (pts.isEmpty()) return new MinMaxDouble(0, 0); double min = Double.POSITIVE_INFINITY, max = Double.NEGATIVE_INFINITY; for (int i = 0; i < pts.size(); i++) { Pt pt = pts.get(i); //if pt.x is a number, take the value as x-axis-value if (pt.x instanceof Number) { if (((Number) pt.x).doubleValue() < min) min = ((Number) pt.x).doubleValue(); if (((Number) pt.x).doubleValue() > max) max = ((Number) pt.x).doubleValue(); //if pt.x is NOT a number, take the index as x-axis-value } else { if (i < min) min = i; if (i > max) max = i; } } return new MinMaxDouble(min, max); } @Override public MinMaxDouble getMinMaxYAxis() { if (pts.isEmpty()) return new MinMaxDouble(0, 0); double min = Double.POSITIVE_INFINITY, max = Double.NEGATIVE_INFINITY; for (Pt pt : pts) { if (pt.y < min) min = pt.y; if (pt.y > max) max = pt.y; } return new MinMaxDouble(min, max); } @Override public List<Attribute> getAttributes() { return Utils.extendList(super.getAttributes(), new Attribute(new AttributeType(AttributeType.Type.MainAttribute), "pts", pts)); } /** * a container-class for a discrete point */ protected class Pt extends GenObject { /** * the x */ protected final Comparable x; /** * the y */ protected final double y; /** * the constructor * * @param _x the x * @param _y the y */ public Pt(Comparable _x, double _y) { x = _x; y = _y; } @Override public List<Attribute> getAttributes() { return Utils.createList(new Attribute(new AttributeType(AttributeType.Type.MainAttribute), "x", x), new Attribute(new AttributeType(AttributeType.Type.MainAttribute), "y", y)); } } } /** * a non-discrete plot */ public static class Plot2DContinuousX extends Plot { /** * the points in insert-order */ protected List<Pt> pts = new ArrayList(); /** * the constructor * * @param _title the title * @param pts an initializing set of points, the first double is a x-value, the second a y-value, the third the x-value of the second point and so on.. * @throws IllegalArgumentException if length of point-array not dividable by 2 */ public Plot2DContinuousX(String _title, double... pts) { super(_title); if (pts == null || pts.length % 2 == 1) throw new IllegalArgumentException("invalid length of pts: has to be dividable by 2."); for (int i = 0; i < pts.length; i += 2) add(pts[i], pts[i + 1]); } /** * the constructor * * @param _title the title * @param ptMap an initializing map of points */ public Plot2DContinuousX(String _title, Map<Double, Double> ptMap) { super(_title); List<Double> xValues = new ArrayList(); xValues.addAll(ptMap.keySet()); //we sort the values, because there is no given order in the input-map Collections.sort(xValues); for (Double x : xValues) add(x, ptMap.get(x)); } /** * creates of copy of the specified plot. The new plot * won't be in use, will have a deepcopy of the points and * can have a new title. * * @param from the origin plot * @param newTitle the new title */ public Plot2DContinuousX(Plot2DContinuousX from, String newTitle) { super(newTitle); pts.addAll(from.pts); } /** * adds a new point * * @param _x x * @param _y y * @throws GeneticRuntimeException if this method is called, after the plot is finished */ public void add(double _x, double _y) { if (inUse) throw new GeneticRuntimeException( "this plot is already in use - you can't add any points anymore."); pts.add(new Pt(_x, _y)); } /** * converts this plot in a discrete plot. * * @return this plot in discrete form */ public Plot2DDiscreteX convertToDiscretePlot() { Map<Comparable, Double> points = new HashMap(); for (Pt pt : pts) points.put(pt.x, pt.y); return new Plot2DDiscreteX(title, points); } /** * convert the data in a JFreeChart-Data-Format * * @return the data as XYSeries */ protected XYSeries getGraphData() { XYSeries series = new XYSeries(title); for (Pt pt : pts) series.add(pt.x, pt.y); return series; } @Override public void scaleYAxis(double min, double max) { if (inUse) throw new GeneticRuntimeException("this plot is already in use - you can't modify points anymore."); if (!Double.isFinite(min)) throw new IllegalArgumentException("min has to be finite (not NaN and not infinte)"); if (!Double.isFinite(max)) throw new IllegalArgumentException("max has to be finite (not NaN and not infinte)"); if (max < min) throw new IllegalArgumentException("max has to be >= min"); MinMaxDouble minMaxY = getMinMaxYAxis(); for (int i = 0; i < pts.size(); i++) pts.set(i, new Pt(pts.get(i).x, ((pts.get(i).y - minMaxY.getMin()) / minMaxY.getDistance()) * (max - min) + min)); } @Override public MinMaxDouble getMinMaxXAxis() { if (pts.isEmpty()) return new MinMaxDouble(0, 0); double min = Double.POSITIVE_INFINITY, max = Double.NEGATIVE_INFINITY; for (Pt pt : pts) { if (pt.x < min) min = pt.x; if (pt.x > max) max = pt.x; } return new MinMaxDouble(min, max); } @Override public MinMaxDouble getMinMaxYAxis() { if (pts.isEmpty()) return new MinMaxDouble(0, 0); double min = Double.POSITIVE_INFINITY, max = Double.NEGATIVE_INFINITY; for (Pt pt : pts) { if (pt.y < min) min = pt.y; if (pt.y > max) max = pt.y; } return new MinMaxDouble(min, max); } @Override public List<Attribute> getAttributes() { return Utils.extendList(super.getAttributes(), new Attribute(new AttributeType(AttributeType.Type.MainAttribute), "pts", pts)); } /** * a container-class for a non-discrete point */ protected class Pt extends GenObject { /** * the x */ protected final double x; /** * the y */ protected final double y; /** * the constructor * * @param _x the x * @param _y the y */ public Pt(double _x, double _y) { x = _x; y = _y; } @Override public List<Attribute> getAttributes() { return Utils.createList(new Attribute(new AttributeType(AttributeType.Type.MainAttribute), "x", x), new Attribute(new AttributeType(AttributeType.Type.MainAttribute), "y", y)); } } } }