Package umontreal.iro.lecuyer.charts

Source Code of umontreal.iro.lecuyer.charts.XYListSeriesCollection$AffineFit


/*
* Class:        XYListSeriesCollection
* Description: 
* Environment:  Java
* Software:     SSJ
* Copyright (C) 2001  Pierre L'Ecuyer and Université de Montréal
* Organization: DIRO, Université de Montréal
* @author      
* @since

* SSJ is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License (GPL) as published by the
* Free Software Foundation, either version 3 of the License, or
* any later version.

* SSJ 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.

* A copy of the GNU General Public License is available at
   <a href="http://www.gnu.org/licenses">GPL licence site</a>.
*/

package umontreal.iro.lecuyer.charts;

import   umontreal.iro.lecuyer.functions.MathFunction;
import   umontreal.iro.lecuyer.functionfit.SmoothingCubicSpline;
import   umontreal.iro.lecuyer.util.RootFinder;

import   org.jfree.data.xy.*;
import   org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;

import   cern.colt.list.DoubleArrayList;

import   java.util.Locale;
import   java.util.Formatter;
import   java.awt.Color;

/**
* This class extends
* {@link umontreal.iro.lecuyer.charts.SSJXYSeriesCollection SSJXYSeriesCollection}.
* It stores data used in a <TT>XYLineChart</TT> or in other related charts.
* <TT>XYListSeriesCollection</TT> provides complementary tools to draw
*  simple curves; for example, one may
* add or remove plots series and modify plot style.
* This class is linked with the JFreeChart <TT>XYSeriesCollection</TT> class to
* store data plots,
* and linked with the JFreeChart <TT>XYLineAndShapeRenderer</TT> to render the plot.
* Each series must contain enough points to plot a nice curve.
* It is recommended to use about 30 points. However, some rapidly
* varying functions may require many more points. This class can be used to draw scatter plots.
*
*/
public class XYListSeriesCollection  extends SSJXYSeriesCollection  {
   protected String[] marksType;   // marks on points (+, x, *...)
   protected String[] dashPattern; // line dashing (solid, dotted, densely dotted, loosely dotted,
                                 //               dashed, densely dashed, loosely dashed, only marks)
   protected String[] plotStyle;   // plot style (lines, curves...)
   private boolean autoCompletion = false;


   /**
    * Creates a new <TT>XYListSeriesCollection</TT> instance with an empty dataset.
    *
    */
   public XYListSeriesCollection()  {
      renderer = new XYLineAndShapeRenderer(true, false);
     // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
      seriesCollection = new XYSeriesCollection();
   }


   /**
    * Creates a new <TT>XYListSeriesCollection</TT> instance with default
    *    parameters and given data series. The input parameter <TT>data</TT>
    *    represents a set of plotting data.
    *
    * <P>
    * For example, if one <SPAN CLASS="MATH"><I>n</I></SPAN>-row matrix <TT>data1</TT> is given as argument,
    *  then the first row <TT>data1</TT><SPAN CLASS="MATH">[0]</SPAN> represents the
    *  <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate vector, and every other row <TT>data1</TT>
    * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 1,&#8230;, <I>n</I> - 1</SPAN>, represents a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinate set for the points.
    *   Therefore matrix <TT>data1</TT><SPAN CLASS="MATH">[<I>i</I>][<I>j</I>]</SPAN>,
    * <SPAN CLASS="MATH"><I>i</I> = 0,&#8230;, <I>n</I> - 1</SPAN>,  corresponds
    *    to <SPAN CLASS="MATH"><I>n</I> - 1</SPAN> curves, all with the same <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
    *
    * <P>
    * However, one may want to plot several curves with different <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
    *   In that case, one should give the curves as matrices with two rows.
    * For examples, if the argument <TT>data</TT> is made of three 2-row matrices
    * <TT>data1</TT>, <TT>data2</TT> and <TT>data3</TT>, then they represents
    *  three different curves, <TT>data*</TT><SPAN CLASS="MATH">[0]</SPAN> being the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates,
    *  and  <TT>data*</TT><SPAN CLASS="MATH">[1]</SPAN> the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of the curves.
    *
    * <P>
    * However, we may also consider the sets of points above not as part of curves,
    * but rather as several list of points.
    *
    * @param data series of point sets.
    *
    *
    */
   public XYListSeriesCollection (double[][]... data)  {
      renderer = new XYLineAndShapeRenderer(true, false);
   //   ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
      seriesCollection = new XYSeriesCollection();

      XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
      for (int i = 0; i < data.length; i ++) {

         if (data[i].length < 2)
            throw new IllegalArgumentException (
               "Unable to render the plot. data["+ i +"] contains less than two rows");

         for (int j = 0; j < data[i].length-1; j++)
            if (data[i][j].length != data[i][j+1].length)
               throw new IllegalArgumentException(
                  "data["+ i +"][" + j + "] and data["+ i +"]["+ (j+1) +"] must share the same length");

         for (int j = 1; j < data[i].length; j++) {
            XYSeries serie = new XYSeries(" ");
            for (int k = 0; k < data[i][0].length; k++)
               serie.add(data[i][0][k], data[i][j][k]);
            tempSeriesCollection.addSeries(serie);
         }
      }

      // set default colors
      for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
         renderer.setSeriesPaint(i, getDefaultColor(i));

      // set default plot style
      plotStyle = new String[tempSeriesCollection.getSeriesCount()];
      marksType = new String[tempSeriesCollection.getSeriesCount()];
      dashPattern = new String[tempSeriesCollection.getSeriesCount()];
      for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
         marksType[i] = " ";
         plotStyle[i] = "smooth";
         dashPattern[i] = "solid";
      }
   }


   /**
    * Creates a new <TT>XYListSeriesCollection</TT> instance with default
    *    parameters and given points <TT>data</TT>.
    *  If <TT>data</TT> is a <SPAN CLASS="MATH"><I>n</I></SPAN>-row matrix,
    *  then the first row <TT>data</TT><SPAN CLASS="MATH">[0]</SPAN> represents the
    *  <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate vector, and every other row <TT>data</TT>
    * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 1,&#8230;, <I>n</I> - 1</SPAN>, represents a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinate set of points.
    *   Therefore, if the points represents curves to be plotted,
    *    <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>][&nbsp;]</SPAN>,
    * <SPAN CLASS="MATH"><I>i</I> = 0,&#8230;, <I>n</I> - 1</SPAN>,  corresponds
    *    to <SPAN CLASS="MATH"><I>n</I> - 1</SPAN> curves, all with the same <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
    *    Only the first <TT>numPoints</TT> of <TT>data</TT> will be considered
    * for each of the set of points.
    *
    * @param data series of point sets.
    *
    *    @param numPoints Number of points to plot
    *
    *
    */
   public XYListSeriesCollection (double[][] data, int numPoints)  {
      renderer = new XYLineAndShapeRenderer(true, false);
     // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
      seriesCollection = new XYSeriesCollection();

      XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
      if (data.length < 2)
         throw new IllegalArgumentException (
            "Unable to render the plot. data contains less than two rows");

      // n-1 curves: data[0] is x; data[i] is y for each curve
      for (int j = 1; j < data.length; j++) {
         XYSeries serie = new XYSeries(" ");
         for (int k = 0; k < numPoints; k++)
            serie.add(data[0][k], data[j][k]);
         tempSeriesCollection.addSeries(serie);
      }

      // set default colors
      for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
         renderer.setSeriesPaint(i, getDefaultColor(i));

      // set default plot style
      plotStyle = new String[tempSeriesCollection.getSeriesCount()];
      marksType = new String[tempSeriesCollection.getSeriesCount()];
      dashPattern = new String[tempSeriesCollection.getSeriesCount()];
      for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
         marksType[i] = " ";
         plotStyle[i] = "smooth";
         dashPattern[i] = "solid";
      }
   }


   /**
    * Creates a new <TT>XYListSeriesCollection</TT> instance with default parameters and given data.
    *    The input parameter represents a set of data plots, the constructor will count the occurrence number
    *    <SPAN CLASS="MATH"><I>Y</I></SPAN> of each value <SPAN CLASS="MATH"><I>X</I></SPAN> in the <TT>DoubleArrayList</TT>, and plot the point <SPAN CLASS="MATH">(<I>X</I>, <I>Y</I>)</SPAN>.
    *    Each {@link cern.colt.list.DoubleArrayList DoubleArrayList} variable corresponds to a curve on the chart.
    *
    * @param data series of point sets.
    *
    *
    */
   public XYListSeriesCollection (DoubleArrayList... data)  {
      renderer = new XYLineAndShapeRenderer(true, false);
     // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
      seriesCollection = new XYSeriesCollection ();
      XYSeriesCollection tempSeriesCollection = (XYSeriesCollection )seriesCollection;

      XYSeries serie;
      double[] elements;
      int count = 0;
      DoubleArrayList temp;
      for(int i = 0; i < data.length; i++) {
         serie = new XYSeries(" ");

         temp = data[i].copy()// deep copy
         temp.trimToSize();      // set capacity to the current size
         temp.quickSortFromTo(0, temp.size()-1);   // sort list in increasing order, simplify the next processings
         elements = temp.elements();

         int j = 0;
         int l = 0;
         while(j < elements.length) {
            while(j < elements.length && elements[j] == elements[l]) {
               j++;
               count++;
            }
            serie.add(elements[l], count);
            count = 0;
            l = j;
         }
         tempSeriesCollection.addSeries(serie);
      }

      // set default colors
      for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
         renderer.setSeriesPaint(i, getDefaultColor(i));
      }

      // set default plot style
      plotStyle = new String[tempSeriesCollection.getSeriesCount()];
      marksType = new String[tempSeriesCollection.getSeriesCount()];
      dashPattern = new String[tempSeriesCollection.getSeriesCount()];
      for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
         marksType[i] = " ";
         plotStyle[i] = "smooth";
         dashPattern[i] = "solid";
      }
   }


   /**
    * Creates a new <TT>XYListSeriesCollection</TT> instance with default parameters and given data series.
    *    The input parameter represents a set of plotting data.
    *    Each series of the given collection corresponds to a curve on the plot.
    *
    * @param data series of point sets.
    *
    */
   public XYListSeriesCollection (XYSeriesCollection data)  {
      renderer = new XYLineAndShapeRenderer(true, false);
    //  ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
      seriesCollection = data;
      for(int i = 0; i < data.getSeriesCount(); i++) {
         XYSeries serie = data.getSeries(i);
      }

      // set default colors
      for(int i = 0; i < data.getSeriesCount(); i++) {
         renderer.setSeriesPaint(i, getDefaultColor(i));
      }

      // set default plot style
      plotStyle = new String[data.getSeriesCount()];
      marksType = new String[data.getSeriesCount()];
      dashPattern = new String[data.getSeriesCount()];
      for(int i = 0; i < data.getSeriesCount(); i++) {
         marksType[i] = " ";
         plotStyle[i] = "smooth";
         dashPattern[i] = "solid";
      }
   }


   /**
    * Adds a data series into the series collection. Vector <TT>x</TT> represents
    *    the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates and vector <TT>y</TT> represents the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of
    *    the series.
    *
    * @param x <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
    *
    *    @param y <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
    *
    *    @return Integer that represent the new point set's position in the JFreeChart <TT>XYSeriesCollection</TT> object.
    *
    */
   public int add (double[] x, double[] y)  {
      if (x.length != y.length)
         throw new IllegalArgumentException("x and y must have the same length");
      return add (x, y, x.length);
   }


   /**
    * Adds a data series into the series collection. Vector <TT>x</TT> represents
    *    the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates and vector <TT>y</TT> represents the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of
    *    the series. Only <SPAN  CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>x</TT>
    *    and <TT>y</TT> will be added to the new series.
    *
    * @param x <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
    *
    *    @param y <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
    *
    *    @param numPoints Number of points to add
    *
    *    @return Integer that represent the new point set's position in the JFreeChart <TT>XYSeriesCollection</TT> object.
    *
    */
   public int add (double[] x, double[] y, int numPoints)  {
      XYSeries serie = new XYSeries(" ");
      XYSeriesCollection tempSeriesCollection = (XYSeriesCollection )seriesCollection;
      serie.setNotify(true);
      if ((x.length < numPoints) ||(y.length < numPoints))
         throw new IllegalArgumentException("numPoints > length of x or y");
      for (int i = 0; i < numPoints; i++)
         serie.add(x[i], y[i]);
      tempSeriesCollection.addSeries(serie);

      // color
      int j = tempSeriesCollection.getSeriesCount()-1;
      renderer.setSeriesPaint(j, getDefaultColor(j));

      int co = tempSeriesCollection.getSeriesCount();
      String[] newPlotStyle = new String[co];
      String[] newMarksType = new String[co];
      String[] newDashPattern = new String[co];
      for(j = 0; j < co - 1; j++) {
         newPlotStyle[j] = plotStyle[j];
         newMarksType[j] = marksType[j];
         newDashPattern[j] = dashPattern[j];
      }

      newPlotStyle[j] = "smooth";
      newMarksType[j] = " ";
      newDashPattern[j] = "solid";
      plotStyle = newPlotStyle;
      marksType = newMarksType;
      dashPattern = newDashPattern;

      return tempSeriesCollection.getSeriesCount()-1;
   }


   /**
    * Adds a data series into the series collection. The input format of
    *  <TT>data</TT> is described in constructor
    * <TT>XYListSeriesCollection(double[][] data)</TT>.
    *
    * @param data input data.
    *
    *    @return Integer that represent the number of point sets added to the current dataset.
    *
    */
   public int add (double[][] data)  {
      return add (data, data[0].length);
   }


   /**
    * Adds  data series into the series collection. The input format of
    *  <TT>data</TT> is described in constructor
    * <TT>XYListSeriesCollection(double[][] data)</TT>.
    *   Only <SPAN  CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>data</TT>
    *  (the first <TT>numPoints</TT> columns of the matrix)
    *   will be added to each new series.
    *
    * @param data input data.
    *
    *    @param numPoints Number of points to add for each new series
    *
    *    @return Integer that represent the number of point sets added to the current dataset.
    *
    */
   public int add (double[][] data, int numPoints)  {
      XYSeriesCollection tempSeriesCollection =
          (XYSeriesCollection) seriesCollection;
      int n = tempSeriesCollection.getSeriesCount();

      if (data.length < 2)
         throw new IllegalArgumentException(
            "Unable to render the plot. data contains less than two rows");

      for (int j = 0; j < data.length; j++)
         if (data[j].length < numPoints)
            throw new IllegalArgumentException(
               "data[" + j + "] has not enough points");

      for (int j = 1; j < data.length; j++) {
         XYSeries serie = new XYSeries(" ");
         serie.setNotify(true);
         for (int k = 0; k < numPoints; k++)
            serie.add(data[0][k], data[j][k]);
         tempSeriesCollection.addSeries(serie);
      }

      // color
      for(int j = n; j < tempSeriesCollection.getSeriesCount(); j++)
         renderer.setSeriesPaint(j, getDefaultColor(j));

      String[] newPlotStyle = new String[tempSeriesCollection.getSeriesCount()];
      String[] newMarksType = new String[tempSeriesCollection.getSeriesCount()];
      String[] newDashPattern = new String[tempSeriesCollection.getSeriesCount()];
      for (int j = 0; j < n; j++) {
         newPlotStyle[j] = plotStyle[j];
         newMarksType[j] = marksType[j];
         newDashPattern[j] = dashPattern[j];
      }

      for(int j = n; j < tempSeriesCollection.getSeriesCount(); j++) {
         newPlotStyle[j] = "smooth";
         newMarksType[j] = " ";
         newDashPattern[j] = "solid";
      }
      plotStyle = newPlotStyle;
      marksType = newMarksType;
      dashPattern = newDashPattern;

      return (tempSeriesCollection.getSeriesCount()-n);
   }


   /**
    * Adds a data series into the series collection. The input format of
    *  <TT>data</TT> is described in constructor
    * <TT>XYListSeriesCollection (DoubleArrayList... data)</TT>.
    *
    * @param data data series.
    *
    *    @return Integer that represent the new point set's position in the JFreeChart <TT>XYSeriesCollection</TT> object.
    *
    */
   public int add (DoubleArrayList data)  {
      XYSeries serie = new XYSeries(" ");
      DoubleArrayList temp = data.copy()// deep copy
      XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;

      temp.trimToSize();      // set capacity to the current size
      temp.quickSortFromTo(0, temp.size()-1);   // sort list in increasing order, simplify the next processings
      double[] elements = temp.elements();

      int count = 0;
      int j = 0;
      int l = 0;
      while(j < elements.length) {
         while(j < elements.length && elements[j] == elements[l]) {
            j++;
            count++;
         }
         serie.add(elements[l], count);
         count = 0;
         l = j;
      }
      tempSeriesCollection.addSeries(serie);

      // color
      j = tempSeriesCollection.getSeriesCount()-1;
      renderer.setSeriesPaint(j, getDefaultColor(j));

      String[] newPlotStyle = new String[tempSeriesCollection.getSeriesCount()];
      String[] newMarksType = new String[tempSeriesCollection.getSeriesCount()];
      String[] newDashPattern = new String[tempSeriesCollection.getSeriesCount()];
      for(j = 0; j < tempSeriesCollection.getSeriesCount()-1; j++) {
         newPlotStyle[j] = plotStyle[j];
         newMarksType[j] = marksType[j];
         newDashPattern[j] = dashPattern[j];
      }

      newPlotStyle[j] = "smooth";
      newMarksType[j] = " ";
      newDashPattern[j] = "solid";
      plotStyle = newPlotStyle;
      marksType = newMarksType;
      dashPattern = newDashPattern;

      return tempSeriesCollection.getSeriesCount()-1;
   }


   /**
    * Gets the current name of the selected series.
    *
    * @param series series index.
    *
    *    @return current name of the series.
    *
    */
   public String getName (int series)  {
      return (String)((XYSeriesCollection )seriesCollection).getSeries(series).getKey();
   }


   /**
    * Sets the name of the selected series.
    *
    * @param series series index.
    *
    *    @param name point set new name.
    *
    */
   public void setName (int series, String name)  {
      if(name == null)
         name = " ";
      ((XYSeriesCollection)seriesCollection).getSeries(series).setKey(name);
   }


   /**
    * Enables the auto completion option. When this parameter
    *    is enabled, straight lines are used to approximate points on the
    *    chart bounds if the method isn't able to display all points,
    *    because the user defined bounds are smaller than the
    *    most significant data point coordinate, for instance.
    *    It does not extrapolate the point sets, but simply estimates
    *    point coordinates on the curve at bound positions for a better visual rendering.
    *
    */
   public void enableAutoCompletion()  {
      this.autoCompletion = true;
   }


   /**
    * Disables auto completion option. Default status is <TT>disabled</TT>.
    *
    */
   public void disableAutoCompletion()  {
      this.autoCompletion = false;
   }


   /**
    * Returns the mark type associated with the <TT>series</TT>th data series.
    *
    * @param series series index.
    *
    *    @return mark type.
    *
    */
   public String getMarksType (int series)  {
      return marksType[series];
   }


   /**
    * Adds marks on the points of a data series.
    *    It is possible to use any of the marks provided by the TikZ package,
    *    some of which are ``<TT>*</TT>'', ``<TT>+</TT>'' and ``<TT>x</TT>''.
    *    A blank character, used by default, disables marks.
    *    The PGF/TikZ documentation provides more information about placing marks on plots.
    *
    * @param series series index.
    *
    *    @param marksType mark type.
    *
    *
    */
   public void setMarksType (int series, String marksType)  {
      this.marksType[series] = marksType;
   }


   /**
    * Returns the dash pattern associated with the <TT>series</TT>th data series.
    *
    * @param series series index.
    *
    *    @return mark type.
    *
    */
   public String getDashPattern (int series)  {
      return dashPattern[series];
   }


   /**
    * Selects dash pattern for a data series. It is possible to use all the dash
    * options provided by the TikZ package: ``<TT>solid</TT>'', ``<TT>dotted</TT>'',
    * ``<TT>densely dotted</TT>'', ``<TT>loosely dotted</TT>'', ``<TT>dashed</TT>'',
    * ``<TT>densely dashed</TT>'', ``<TT>loosely dashed</TT>'' and
    * ``<TT>only marks</TT>''. If ``<TT>only marks</TT>" is chosen, then method
    * {@link #setMarksType setMarksType} must be called to choose the marks
    * (which are blank by default).
    *
    * @param series series index.
    *
    *    @param dashPattern dash style.
    *
    *
    */
   public void setDashPattern (int series, String dashPattern)  {
      this.dashPattern[series] = dashPattern;
      if (dashPattern.equals("only marks")) {
          ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(series, false);
          ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(series, true);
      } else {
          ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(series, true);
          ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(series, false);
      }
   }


   /**
    * Gets the current plot style for the selected series.
    *
    * @param series series index.
    *
    *    @return current plot style.
    *
    */
   public String getPlotStyle (int series)  {
      return plotStyle[series];
   }


   /**
    * Selects the plot style for a given series. It is possible to use all the
    *    plot options provided by the TikZ package. Some of which are:
    *    ``<TT>sharp plot</TT>'', which joins points with straight lines,
    *    ``<TT>smooth</TT>'', which joins points with a smoothing curve,
    *    ``<TT>only marks</TT>'', which does not join points, etc.
    *    The PGF/TikZ documentation provides more information about smooth plots,
    *      sharp plots and comb plots.
    *
    * @param series series index.
    *
    *    @param plotStyle plot style.
    *
    *
    */
   public void setPlotStyle (int series, String plotStyle)  {
      this.plotStyle[series] = plotStyle;
   }


   public String toLatex (double XScale, double YScale,
                          double XShift, double YShift,
                          double xmin, double xmax,
                          double ymin, double ymax) {

      // Calcule les bornes reelles du graphique, en prenant en compte la position des axes
      xmin = Math.min(XShift, xmin);
      xmax = Math.max(XShift, xmax);
      ymin = Math.min(YShift, ymin);
      ymax = Math.max(YShift, ymax);

      Formatter formatter = new Formatter(Locale.US);
      XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
      double XEPSILON = (1.0E-4/XScale)+XShift;
      double YEPSILON = (1.0E-4/YScale)+YShift;
      boolean outOfBounds = false;
      MathFunction[] spline = null;
      double[] xBounds = getRangeBounds();
      double[] yBounds = getDomainBounds();
      double x, y;
// Smoothing splines, consulter  ref: QA278.2 G74, QA278.2 T35, QA278.2 E87

//       if(xBounds[0] < xmin || xBounds[1] > xmax || yBounds[0] < ymin || yBounds[1] > ymax) {
//          // on sait qu'il y a des points qui vont sortir du chart
//          // initialisation d'une spline pour chaque courbe
//          spline = new SmoothingCubicSpline[seriesCollection.getSeriesCount()];
//          for(int i = 0; i<seriesCollection.getSeriesCount(); i++)
//             spline[i] = new SmoothingCubicSpline(  (seriesCollection.getSeries(i).toArray())[0],
//                                                    (seriesCollection.getSeries(i).toArray())[1], 0.5);
//       }

      // on sait qu'il y a des points qui vont sortir du chart
      // initialisation d'une spline pour chaque courbe
      if (true) {
         spline = new SmoothingCubicSpline[tempSeriesCollection.getSeriesCount()];
         for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
            spline[i] = new SmoothingCubicSpline((tempSeriesCollection.getSeries(i).toArray())[0],
                                                 (tempSeriesCollection.getSeries(i).toArray())[1], 1);
      } else {
         spline = new AffineFit[tempSeriesCollection.getSeriesCount()];
         for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
            spline[i] = new AffineFit((tempSeriesCollection.getSeries(i).toArray())[0],
                                      (tempSeriesCollection.getSeries(i).toArray())[1]);
      }

      for(int i = tempSeriesCollection.getSeriesCount()-1; i >= 0; i--) {
         XYSeries temp = tempSeriesCollection.getSeries(i);

         if (temp.getItemCount() < 2)
            throw new IllegalArgumentException("Unable to plot series " + i +
                                               ": this series must have two points at least");

         Color color = (Color)renderer.getSeriesPaint(i);
         String colorString = detectXColorClassic(color);
         if( colorString == null) {
            colorString = "color"+i;
            formatter.format( "\\definecolor{%s}{rgb}{%.2f, %.2f, %.2f}%n",
                              colorString, color.getRed()/255.0, color.getGreen()/255.0, color.getBlue()/255.0);
         }

         // Cas particulier pour le premier point, on doit savoir si il est dans le chart ou pas
         if temp.getX(0).doubleValue() >= xmin && temp.getX(0).doubleValue() <= xmax &&
               temp.getY(0).doubleValue() >= ymin && temp.getY(0).doubleValue() <= ymax) {
            outOfBounds = false;
            formatter.format( "\\draw [%s, color=%s, mark=%s, style=%s] plot coordinates {%%%n",
                              plotStyle[i], colorString, marksType[i], dashPattern[i]);
         }
         else {
            outOfBounds = true;
            formatter.format("%% ");
         }
         formatter.format("(%.2f,%.4f)",   (temp.getX(0).doubleValue()-XShift)*XScale,
                                              (temp.getY(0).doubleValue()-YShift)*YScale);
         formatter.format(" %%   (%f,  %f)%n", temp.getX(0).doubleValue(), temp.getY(0).doubleValue());

         // Cas general
         for(int j = 1; j < temp.getItemCount(); j++) {
            double[] result;
            if (!outOfBounds) { //on est dans le chart
               result = evalLimitValues(xmin, xmax, ymin, ymax, XEPSILON, YEPSILON, spline[i], temp, j, false);
               // on regarde si on ne sort pas du chart, si c'est le cas on evalue le point en limite
               if (result != null) { // le point courant n'est pas dans le chart, on sort donc du chart
                  outOfBounds = true;
                  if (autoCompletion)
                     formatter.format("(%.2f,%.4f) %%%n", (result[0]-XShift)*XScale, (result[1]-YShift)*YScale);
                  formatter.format("}%%%n%% ");
               }
            }
            else { // le point precedent etait hors du chart
               if temp.getX(j).doubleValue() >= xmin && temp.getX(j).doubleValue() <= xmax &&
                     temp.getY(j).doubleValue() >= ymin && temp.getY(j).doubleValue() <= ymax) {
                     // on rentre dans le chart, il faut evaluer le point en limite
                  j = j-1;
                  result = evalLimitValues(xmin, xmax, ymin, ymax, XEPSILON, YEPSILON, spline[i], temp, j, true);
                  // ici result ne peut pas etre null
                  formatter.format( ";%%%n\\draw [%s, color=%s, mark=%s, style=%s] plot coordinates {%%%n",
                                    plotStyle[i], colorString, marksType[i], dashPattern[i]);
                  if (autoCompletion)
                     formatter.format("(%.2f,%.4f) %%%n ", (result[0]-XShift)*XScale, (result[1]-YShift)*YScale);
                  formatter.format("%% ");
                  outOfBounds = false;
               }
               else {
                  formatter.format("%% ");
                  // on les donnees sont toujours hors du chart
               }
            }
            /* on affiche les coordonnees du point quoiqu'il arrive,
            si celui ci est hors du chart alors la balise de commentaire a ete deja place */
            formatter.format("(%.2f,%.4f)",   (temp.getX(j).doubleValue()-XShift)*XScale,
                                              (temp.getY(j).doubleValue()-YShift)*YScale);
            if(j == temp.getItemCount()-1)
               formatter.format("}");
            formatter.format(" %%   (%f,  %f)%n", temp.getX(j).doubleValue(), temp.getY(j).doubleValue());
//            formatter.format(" %%%n");
         }
         formatter.format(" node[right] {%s};%n", (String)temp.getKey());
      }
      return formatter.toString();
   }


   /* *
    * Compute x and y to chart limit bounds for extra-bounded points
    *
    * @param   xmin     lower bound for x coordinates
    * @param   xmax     upper bound for x coordinates
    * @param   ymin     lower bound for y coordinates
    * @param   ymax     upper bound for y coordinates
    * @param   XEPSILON increment step size for x coordinates
    * @param   YEPSILON increment step size for y coordinates
    * @param   spline   sline used to approximate points
    * @param   temp     current series
    * @param   numPoint point index in the current series
    * @param   sens     direction of the in chart last point
    *                    true  : point numPoint+1 is in the chart
    *                    false : point numPoint-1 is in the chart
    *
    * @return           x and y coordinates on the chart bounds.
    */
   private static double[] evalLimitValues(double xmin, double xmax, double ymin, double ymax, double XEPSILON, double YEPSILON, MathFunction spline, XYSeries temp, int numPoint, boolean sens) {
      int j = numPoint;
      int k = 0;
      double x, y;
      if(sens)
         k = j+1;
      else
         k = j-1;
      if(temp.getX(j).doubleValue() < xmin) {// Hors du chart mais on etait dans le chart au point precedent
         x = xmin;
         y = spline.evaluate(xmin); // spline puis evaluer en xmin
         while(y<ymin) {
            x += XEPSILON;
            y = spline.evaluate(x);
         // evaluer un x>xmin tantque y<ymin, y peut etre superieur a ymin car le point precedent est dans le chart
         while(y > ymax) {
            x += XEPSILON;
            y = spline.evaluate(x);
         // evaluer un x en ymax avec x > xmin
      }
      else if(temp.getX(j).doubleValue() > xmax) {
         x = xmax;
         y = spline.evaluate(xmax);
         while(y<ymin) {
            x -= XEPSILON;
            y = spline.evaluate(x);
         // evaluer un x<xmax tantque y<ymin
         while(y > ymax) {
            x -= XEPSILON;
            y = spline.evaluate(x);
         // evaluer un x<xmax tantque y>ymax
      }
      else if(temp.getY(j).doubleValue() < ymin) {
         y = ymin;
         x = evaluateX(spline, y, temp.getX(j).doubleValue(), temp.getX(k).doubleValue());// spline puis evaluer en ymin avec x ente xValue(ptCourant) et xValue(ptCourant-1)
         while(x < xmin) {
            y += YEPSILON;
            x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
         }
         while(x > xmax) {
           y += YEPSILON;
           x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
         }
      }
      else if(temp.getY(j).doubleValue() > ymax) {
         y = ymax;
         x = evaluateX(spline, y, temp.getX(j).doubleValue(), temp.getX(k).doubleValue());// spline puis evaluer en ymax avec x ente xValue(ptCourant) et xValue(ptCourant-1)
         while(x < xmin) {
            y -= YEPSILON;
            x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
         }
         while(x > xmax) {
            y -= YEPSILON;
            x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
         }
      }
      else
         return null;
      double[] retour = new double[2];
      retour[0] = x;
      retour[1] = y;
      return retour;
   }


   private static double evaluateX (final MathFunction spline, final double y, double xPrincipal, double xAnnexe) {
      final MathFunction xFunction = new MathFunction () {
         public double evaluate (double t) {
            return spline.evaluate(t) - y;
         }
      };
      return RootFinder.brentDekker (xPrincipal, xAnnexe-1.0E-6, xFunction, 1e-6);
   }


   private class AffineFit implements MathFunction{

      double[] x;
      double[] y;

      public AffineFit(double[] x, double[] y) {
         this.x =x;
         this.y =y;
      }

      public double evaluate(double t) {
         int i = 0;
         if (t <= x[0])
            return y[0];
         while (i < x.length && t > x[i])
            i++;
         i--;
         if (i == x.length)
            return x[x.length-1];

         return y[i] + ((t - x[i]) / (x[i+1] - x[i])) * (y[i+1] - y[i]);
      }
   }
}
TOP

Related Classes of umontreal.iro.lecuyer.charts.XYListSeriesCollection$AffineFit

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.