package umontreal.iro.lecuyer.charts;

import   org.jfree.chart.JFreeChart;
import   org.jfree.chart.ChartPanel;
import   org.jfree.chart.ChartFactory;
import   org.jfree.chart.axis.NumberAxis;
import   org.jfree.chart.plot.XYPlot;
import   org.jfree.chart.plot.PlotOrientation;

import   java.util.Locale;
import   java.util.Formatter;
import   java.util.ArrayList;
import   javax.swing.JFrame;

/**
 * Provides tools to plot many datasets on the same chart.
 * This class is mainly used to draw plots with different styles.
 * Class {@link umontreal.iro.lecuyer.charts.XYChart XYChart} and
 * its subclasses are to be preferred to draw simple charts with one style.
 * Datasets are stored in an ArrayList. The first dataset is called
 * as the primary dataset.
 * 
 */
public class MultipleDatasetChart  {

   protected ArrayList datasetList;
   protected Axis XAxis;
   protected Axis YAxis;
   protected JFreeChart chart;
   protected boolean latexDocFlag = true;

   protected boolean autoRange = true;
   protected double[] manualRange;

   protected boolean grid = false;
   protected double xstepGrid;
   protected double ystepGrid;



   /**
    * Initializes a new MultipleDatasetChart.
    * 
    */
   public MultipleDatasetChart()  {
      super();

      // create the chart...
      chart = ChartFactory.createXYLineChart(
         null,                     // chart title
         null,                     // x axis label
         null,                     // y axis label
         null, // data
         PlotOrientation.VERTICAL,
         true,                     // include legend
         true,                     // tool tips
         false                     // urls
      );

      datasetList = new ArrayList();
      // Initialize axis variables
      XAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getDomainAxis(),
                 Axis.ORIENTATION_HORIZONTAL);
      YAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getRangeAxis(),
                 Axis.ORIENTATION_VERTICAL);
   }


   /**
    * Initializes a new MultipleDatasetChart instance.
    *    title sets a title, XLabel is a short description of
    *    the x-axis, and YLabel is a short description of the y-axis.
    * 
    * @param title chart title.
    * 
    *    @param XLabel Label on x-axis.
    * 
    *    @param YLabel Label on y-axis.
    * 
    */
   public MultipleDatasetChart (String title, String XLabel, String YLabel)  {
      // create the chart...
      chart = ChartFactory.createXYLineChart(
         title,                    // chart title
         XLabel,                   // x axis label
         YLabel,                   // y axis label
         null,                     // data
         PlotOrientation.VERTICAL,
         true,                     // include legend
         true,                     // tool tips
         false                     // urls
      );

      datasetList = new ArrayList();
      //Initialize axis variables
      XAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getDomainAxis(),
              Axis.ORIENTATION_HORIZONTAL);
      YAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getRangeAxis(),
              Axis.ORIENTATION_VERTICAL);
   }


   /**
    * Returns the JFreeChart variable associated with this chart.
    * 
    * @return the associated JFreeChart variable.
    * 
    */
   public JFreeChart getJFreeChart()  {
      return chart;
   }


   /**
    * Returns the chart's domain axis (x-axis) object.
    * 
    * @return chart's domain axis (x-axis) object.
    * 
    */
   public Axis getXAxis()  {
      return XAxis;
   }


   /**
    * Returns the chart's range axis (y-axis) object.
    * 
    * @return chart's range axis (y-axis) object.
    * 
    */
   public Axis getYAxis()  {
      return YAxis;
   }


   /**
    * Gets the current chart title.
    * 
    * @return Chart title.
    * 
    */
   public String getTitle()  {
      return chart.getTitle().getText();
   }


   /**
    * Sets a title to the chart. This title will appear on the chart displayed by method {@link #view view}.
    * 
    * @param title chart title.
    * 
    * 
    */
   public void setTitle (String title)  {
      chart.setTitle(title);
   }


   /**
    * Sets chart range to automatic values.
    * 
    */
   public void setAutoRange()  {
      autoRange = true;
      double[][] temp = new double[2][datasetList.size()];
      for(int i = 0; ix-axis and y-axis bounds, with format: axisRange = [xmin, xmax, ymin, ymax].
    * 
    * @param axisRange new axis ranges.
    * 
    * 
    */
   public void setManualRange (double[] axisRange)  {
      if(axisRange.length != 4)
         throw new IllegalArgumentException("axisRange must share the format: [xmin, xmax, ymin, ymax]");
      autoRange = false;
      XAxis.getAxis().setLowerBound(axisRange[0]);
      XAxis.getAxis().setUpperBound(axisRange[1]);
      YAxis.getAxis().setLowerBound(axisRange[2]);
      YAxis.getAxis().setUpperBound(axisRange[3]);
   }


   /**
    * Adds a new dataset to the chart at the end of the list and returns its position.
    * 
    * @param dataset dataset to add.
    * 
    *    @return the dataset position in the list.
    * 
    */
   public int add (SSJXYSeriesCollection dataset)  {
      ((XYPlot)chart.getPlot()).setDataset(datasetList.size(),
                  dataset.getSeriesCollection());
      ((XYPlot)chart.getPlot()).setRenderer(datasetList.size(),
                  dataset.getRenderer());
      datasetList.add(dataset);
      if(datasetList.size() == 1) {
         XAxis.setLabelsAuto();
         YAxis.setLabelsAuto();
      }
      return datasetList.size()-1;
   }


   /**
    * Gets the primary dataset.
    * 
    * @return dataset.
    * 
    */
   public SSJXYSeriesCollection get()  {
      return datasetList.get(0);
   }


   /**
    * Sets the primary dataset for the plot, replacing the existing dataset if there is one.
    * 
    * @param dataset the new primary dataset.
    * 
    * 
    */
   public void set (SSJXYSeriesCollection dataset)  {
      ((XYPlot)chart.getPlot()).setDataset(dataset.getSeriesCollection());
      ((XYPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
      datasetList.set(0, dataset);
   }


   /**
    * Gets the element at the specified position in the dataset list.
    * 
    * @param datasetNum position in the dataset list.
    * 
    *    @return dataset.
    * 
    */
   public SSJXYSeriesCollection get (int datasetNum)  {
      return datasetList.get(datasetNum);
   }


   /**
    * Replaces the element at the specified position in the dataset list with the specified element.
    * 
    * @param datasetNum position in the dataset list.
    * 
    *    @param dataset dataset list.
    * 
    * 
    */
   public void set (int datasetNum, SSJXYSeriesCollection dataset)  {
      ((XYPlot)chart.getPlot()).setDataset(datasetNum, dataset.getSeriesCollection());
      ((XYPlot)chart.getPlot()).setRenderer(datasetNum, dataset.getRenderer());
      datasetList.add(datasetNum, dataset);
   }


   /**
    * Returns the dataset list.
    * 
    * @return dataset list.
    * 
    */
   public ArrayList getList()  {
      return datasetList;
   }


   /**
    * Displays chart on the screen using Swing.
    *    This method creates an application containing a chart panel displaying
    *    the chart. The created frame is positioned on-screen, and displayed before
    *    it is returned. The width and the height
    *    of the chart are measured in pixels.
    * 
    * @param width frame width in pixels.
    * 
    *    @param height frame height in pixels.
    * 
    * 
    */
   public JFrame view (int width, int height)  {
      JFrame myFrame;
      if (chart.getTitle() != null)
         myFrame = new JFrame("MultipleDatasetChart from SSJ: " +
                               chart.getTitle().getText());
      else
         myFrame = new JFrame("MultipleDatasetChart from SSJ");
      ChartPanel chartPanel = new ChartPanel(chart);
      chartPanel.setPreferredSize(new java.awt.Dimension(width, height));
      myFrame.setContentPane(chartPanel);
      myFrame.pack();
      myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
      myFrame.setLocationRelativeTo (null);
      myFrame.setVisible(true);
      return myFrame;
   }


   /**
    * Puts grid on the background. It is important to note that the grid is
    * always placed in such a way that it contains the axes. Thus, the grid does
    *    not always have an intersection at the corner points; this occurs
    *    only if the corner points are multiples of the stepping. xstep
    *    and ystep sets the stepping in each direction.
    * 
    * @param xstep sets the stepping in the x-direction.
    * 
    *    @param ystep sets the stepping in the y-direction.
    * 
    * 
    */
   public void enableGrid (double xstep, double ystep)  {
      this.grid = true;
      this.xstepGrid = xstep;
      this.ystepGrid = ystep;
   }


   /**
    * Disables the background grid.
    * 
    */
   public void disableGrid ()  {
      this.grid = false;
   }


   /**
    * Same as in {@link XYChart}.
    * 
    * @param width Chart's width in centimeters.
    * 
    *    @param height Chart's height in centimeters.
    * 
    * 
    */
   public String toLatex (double width, double height)  {
      double xunit, yunit;
      double[] save = new double[4];

      if(datasetList.size() == 0)
         throw new IllegalArgumentException("Empty chart");

      //Calcul des parametres d'echelle et de decalage
      double XScale = computeXScale(XAxis.getTwinAxisPosition());
      double YScale = computeYScale(YAxis.getTwinAxisPosition());

      xunit = width / ( (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition()) * XScale) - (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition()) * XScale) );
      //taille d'une unite en x et en cm dans l'objet "tikzpicture"
      yunit = height / ( (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition()) * YScale) - (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition()) * YScale) );
      //taille d'une unite en y et en cm dans l'objet "tikzpicture"

      Formatter formatter = new Formatter(Locale.US);

      /*Entete du document*/
      if (latexDocFlag) {
         formatter.format("\\documentclass[12pt]{article}%n%n");
         formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
      }
      if(chart.getTitle() != null)
         formatter.format("%% PGF/TikZ picture from SSJ : %s%n", chart.getTitle().getText());
      else
         formatter.format("%% PGF/TikZ picture from SSJ %n");
      formatter.format("%% XScale = %s,  YScale = %s,  XShift = %s,  YShift = %s%n", XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition());
      formatter.format("%% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale%n");
      formatter.format("%%        and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
      if (chart.getTitle() != null)
         formatter.format("\\begin{figure}%n");
      formatter.format("\\begin{center}%n");
      formatter.format("\\begin{tikzpicture}[x=%scm, y=%scm]%n", xunit, yunit);
      formatter.format("\\footnotesize%n");
      if(grid)
         formatter.format("\\draw[color=lightgray] (%s, %s) grid[xstep = %s, ystep=%s] (%s, %s);%n",
            (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
            (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
            xstepGrid*XScale, ystepGrid*YScale,
            (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
            (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale );

      formatter.format("%s", XAxis.toLatex(XScale) );
      formatter.format("%s", YAxis.toLatex(YScale) );

      for(int i = 0; i < datasetList.size(); i++)
         formatter.format("%s", datasetList.get(i).toLatex(XScale, YScale,
                                                            XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition(),
                                                            XAxis.getAxis().getLowerBound(), XAxis.getAxis().getUpperBound(),
                                                            YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));

      formatter.format("\\end{tikzpicture}%n");
      formatter.format("\\end{center}%n");
      if (chart.getTitle() != null) {
         formatter.format("\\caption{");
         formatter.format(chart.getTitle().getText());
         formatter.format("}%n\\end{figure}%n");
      }
      if (latexDocFlag)
      formatter.format("\\end{document}%n");
      return formatter.toString();
   }


   /**
    * Same as in {@link XYChart}.
    * 
    */
   public void setLatexDocFlag (boolean flag)  {
      latexDocFlag = flag;
   }



   protected double computeXScale (double position) {
      double[] bounds = new double[2];
      bounds[0] = XAxis.getAxis().getLowerBound();
      bounds[1] = XAxis.getAxis().getUpperBound();

      if(positionbounds[1])
         bounds[1] = position;
      bounds[0] -= position;
      bounds[1] -= position;
      return computeScale(bounds);
   }

   protected double computeYScale (double position) {
      double[] bounds = new double[2];
      bounds[0] = YAxis.getAxis().getLowerBound();
      bounds[1] = YAxis.getAxis().getUpperBound();

      if(positionbounds[1])
         bounds[1] = position;
      bounds[0] -= position;
      bounds[1] -= position;
      return computeScale(bounds);
   }


   protected double computeScale (double[] bounds) {
      int tenPowerRatio=0;
      //echelle < 1 si les valeurs sont grandes
      while(bounds[1] > 1000 || bounds[0] < -1000) {
         bounds[1] /= 10;
         bounds[0] /= 10;
         tenPowerRatio++;
      }
      //echelle > 1 si les valeurs sont petites
      while(bounds[1]<100 && bounds[0] > -100) {
         bounds[1] *= 10;
         bounds[0] *= 10;
         tenPowerRatio--;
      }
      return 1/Math.pow(10, tenPowerRatio);
   }

   private static double max (double[] t) {
      double aux = t[0];
      for (int i=1 ; i < t.length ; i++)
         if (t[i] > aux)
            aux = t[i];
      return aux ;
   }

   private static double min (double[] t) {
      double aux = t[0];
      for (int i=1 ; i < t.length ; i++)
         if (t[i] < aux)
            aux = t[i];
      return aux ;
   }
}

 

Ads help maintain this website.