Save This Page
Home » jcommon-1.0.13 » org.jfree » chart » renderer » xy » [javadoc | source]
    1   /* ===========================================================
    2    * JFreeChart : a free chart library for the Java(tm) platform
    3    * ===========================================================
    4    *
    5    * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
    6    *
    7    * Project Info:  http://www.jfree.org/jfreechart/index.html
    8    *
    9    * This library is free software; you can redistribute it and/or modify it 
   10    * under the terms of the GNU Lesser General Public License as published by 
   11    * the Free Software Foundation; either version 2.1 of the License, or 
   12    * (at your option) any later version.
   13    *
   14    * This library is distributed in the hope that it will be useful, but 
   15    * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
   16    * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
   17    * License for more details.
   18    *
   19    * You should have received a copy of the GNU Lesser General Public
   20    * License along with this library; if not, write to the Free Software
   21    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
   22    * USA.  
   23    *
   24    * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
   25    * in the United States and other countries.]
   26    *
   27    * ---------------------------
   28    * CyclicXYItemRenderer.java
   29    * ---------------------------
   30    * (C) Copyright 2003-2007, by Nicolas Brodu and Contributors.
   31    *
   32    * Original Author:  Nicolas Brodu;
   33    * Contributor(s):   David Gilbert (for Object Refinery Limited);
   34    *
   35    * Changes
   36    * -------
   37    * 19-Nov-2003 : Initial import to JFreeChart from the JSynoptic project (NB);
   38    * 23-Dec-2003 : Added missing Javadocs (DG);
   39    * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
   40    * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 
   41    *               getYValue() (DG);
   42    * ------------- JFREECHART 1.0.0 ---------------------------------------------
   43    * 06-Jul-2006 : Modified to call only dataset methods that return double
   44    *               primitives (DG);
   45    * 
   46    */
   47   
   48   package org.jfree.chart.renderer.xy;
   49   
   50   import java.awt.Graphics2D;
   51   import java.awt.geom.Rectangle2D;
   52   import java.io.Serializable;
   53   
   54   import org.jfree.chart.axis.CyclicNumberAxis;
   55   import org.jfree.chart.axis.ValueAxis;
   56   import org.jfree.chart.labels.XYToolTipGenerator;
   57   import org.jfree.chart.plot.CrosshairState;
   58   import org.jfree.chart.plot.PlotRenderingInfo;
   59   import org.jfree.chart.plot.XYPlot;
   60   import org.jfree.chart.urls.XYURLGenerator;
   61   import org.jfree.data.DomainOrder;
   62   import org.jfree.data.general.DatasetChangeListener;
   63   import org.jfree.data.general.DatasetGroup;
   64   import org.jfree.data.xy.XYDataset;
   65   
   66   /**
   67    * The Cyclic XY item renderer is specially designed to handle cyclic axis. 
   68    * While the standard renderer would draw a line across the plot when a cycling 
   69    * occurs, the cyclic renderer splits the line at each cycle end instead. This 
   70    * is done by interpolating new points at cycle boundary. Thus, correct 
   71    * appearance is restored. 
   72    * 
   73    * The Cyclic XY item renderer works exactly like a standard XY item renderer 
   74    * with non-cyclic axis. 
   75    */
   76   public class CyclicXYItemRenderer extends StandardXYItemRenderer 
   77                                     implements Serializable {
   78   
   79       /** For serialization. */
   80       private static final long serialVersionUID = 4035912243303764892L;
   81       
   82       /**
   83        * Default constructor.
   84        */
   85       public CyclicXYItemRenderer() {
   86           super();
   87       }
   88   
   89       /**
   90        * Creates a new renderer.
   91        * 
   92        * @param type  the renderer type.
   93        */
   94       public CyclicXYItemRenderer(int type) {
   95           super(type);
   96       }
   97   
   98       /**
   99        * Creates a new renderer.
  100        * 
  101        * @param type  the renderer type.
  102        * @param labelGenerator  the tooltip generator.
  103        */
  104       public CyclicXYItemRenderer(int type, XYToolTipGenerator labelGenerator) {
  105           super(type, labelGenerator);
  106       }
  107   
  108       /**
  109        * Creates a new renderer.
  110        * 
  111        * @param type  the renderer type.
  112        * @param labelGenerator  the tooltip generator.
  113        * @param urlGenerator  the url generator.
  114        */
  115       public CyclicXYItemRenderer(int type, 
  116                                   XYToolTipGenerator labelGenerator,
  117                                   XYURLGenerator urlGenerator) {
  118           super(type, labelGenerator, urlGenerator);
  119       }
  120   
  121       
  122       /** 
  123        * Draws the visual representation of a single data item.
  124        * When using cyclic axis, do not draw a line from right to left when 
  125        * cycling as would a standard XY item renderer, but instead draw a line 
  126        * from the previous point to the cycle bound in the last cycle, and a line
  127        * from the cycle bound to current point in the current cycle.  
  128        * 
  129        * @param g2  the graphics device.
  130        * @param state  the renderer state.
  131        * @param dataArea  the data area.
  132        * @param info  the plot rendering info.
  133        * @param plot  the plot.
  134        * @param domainAxis  the domain axis.
  135        * @param rangeAxis  the range axis.
  136        * @param dataset  the dataset.
  137        * @param series  the series index.
  138        * @param item  the item index.
  139        * @param crosshairState  crosshair information for the plot 
  140        *                        (<code>null</code> permitted).
  141        * @param pass  the current pass index.
  142        */
  143       public void drawItem(Graphics2D g2, 
  144                            XYItemRendererState state,
  145                            Rectangle2D dataArea, 
  146                            PlotRenderingInfo info, 
  147                            XYPlot plot,
  148                            ValueAxis domainAxis, 
  149                            ValueAxis rangeAxis, 
  150                            XYDataset dataset,
  151                            int series, 
  152                            int item, 
  153                            CrosshairState crosshairState, 
  154                            int pass) {
  155   
  156           if ((!getPlotLines()) || ((!(domainAxis instanceof CyclicNumberAxis)) 
  157                   && (!(rangeAxis instanceof CyclicNumberAxis))) || (item <= 0)) {
  158               super.drawItem(g2, state, dataArea, info, plot, domainAxis, 
  159                       rangeAxis, dataset, series, item, crosshairState, pass);
  160               return;
  161           }
  162   
  163           // get the previous data point...
  164           double xn = dataset.getXValue(series, item - 1);
  165           double yn = dataset.getYValue(series, item - 1);
  166           // If null, don't draw line => then delegate to parent
  167           if (Double.isNaN(yn)) {
  168               super.drawItem(g2, state, dataArea, info, plot, domainAxis, 
  169                       rangeAxis, dataset, series, item, crosshairState, pass);
  170               return;
  171           }
  172           double[] x = new double[2];
  173           double[] y = new double[2];
  174           x[0] = xn;
  175           y[0] = yn;
  176           
  177           // get the data point...
  178           xn = dataset.getXValue(series, item);
  179           yn = dataset.getYValue(series, item);
  180           // If null, don't draw line at all
  181           if (Double.isNaN(yn)) {
  182               return;
  183           }
  184           x[1] = xn;
  185           y[1] = yn;
  186   
  187           // Now split the segment as needed
  188           double xcycleBound = Double.NaN;
  189           double ycycleBound = Double.NaN;
  190           boolean xBoundMapping = false, yBoundMapping = false;
  191           CyclicNumberAxis cnax = null, cnay = null;
  192   
  193           if (domainAxis instanceof CyclicNumberAxis) {
  194               cnax = (CyclicNumberAxis) domainAxis;
  195               xcycleBound = cnax.getCycleBound();
  196               xBoundMapping = cnax.isBoundMappedToLastCycle();
  197               // If the segment must be splitted, insert a new point
  198               // Strict test forces to have real segments (not 2 equal points) 
  199               // and avoids division by 0 
  200               if ((x[0] != x[1]) 
  201                       && ((xcycleBound >= x[0]) 
  202                       && (xcycleBound <= x[1]) 
  203                       || (xcycleBound >= x[1]) 
  204                       && (xcycleBound <= x[0]))) {
  205                   double[] nx = new double[3];
  206                   double[] ny = new double[3];
  207                   nx[0] = x[0]; nx[2] = x[1]; ny[0] = y[0]; ny[2] = y[1];
  208                   nx[1] = xcycleBound;
  209                   ny[1] = (y[1] - y[0]) * (xcycleBound - x[0]) 
  210                           / (x[1] - x[0]) + y[0];
  211                   x = nx; y = ny;
  212               }
  213           }
  214   
  215           if (rangeAxis instanceof CyclicNumberAxis) {
  216               cnay = (CyclicNumberAxis) rangeAxis;
  217               ycycleBound = cnay.getCycleBound();
  218               yBoundMapping = cnay.isBoundMappedToLastCycle();
  219               // The split may occur in either x splitted segments, if any, but 
  220               // not in both
  221               if ((y[0] != y[1]) && ((ycycleBound >= y[0]) 
  222                       && (ycycleBound <= y[1]) 
  223                       || (ycycleBound >= y[1]) && (ycycleBound <= y[0]))) {
  224                   double[] nx = new double[x.length + 1];
  225                   double[] ny = new double[y.length + 1];
  226                   nx[0] = x[0]; nx[2] = x[1]; ny[0] = y[0]; ny[2] = y[1];
  227                   ny[1] = ycycleBound;
  228                   nx[1] = (x[1] - x[0]) * (ycycleBound - y[0]) 
  229                           / (y[1] - y[0]) + x[0];
  230                   if (x.length == 3) { 
  231                       nx[3] = x[2]; ny[3] = y[2]; 
  232                   }
  233                   x = nx; y = ny;
  234               }
  235               else if ((x.length == 3) && (y[1] != y[2]) && ((ycycleBound >= y[1])
  236                       && (ycycleBound <= y[2]) 
  237                       || (ycycleBound >= y[2]) && (ycycleBound <= y[1]))) {
  238                   double[] nx = new double[4];
  239                   double[] ny = new double[4];
  240                   nx[0] = x[0]; nx[1] = x[1]; nx[3] = x[2]; 
  241                   ny[0] = y[0]; ny[1] = y[1]; ny[3] = y[2];
  242                   ny[2] = ycycleBound;
  243                   nx[2] = (x[2] - x[1]) * (ycycleBound - y[1]) 
  244                           / (y[2] - y[1]) + x[1];
  245                   x = nx; y = ny;
  246               }
  247           }
  248           
  249           // If the line is not wrapping, then parent is OK
  250           if (x.length == 2) {
  251               super.drawItem(g2, state, dataArea, info, plot, domainAxis, 
  252                       rangeAxis, dataset, series, item, crosshairState, pass);
  253               return;
  254           }
  255   
  256           OverwriteDataSet newset = new OverwriteDataSet(x, y, dataset);
  257   
  258           if (cnax != null) {
  259               if (xcycleBound == x[0]) {
  260                   cnax.setBoundMappedToLastCycle(x[1] <= xcycleBound);
  261               }
  262               if (xcycleBound == x[1]) {
  263                   cnax.setBoundMappedToLastCycle(x[0] <= xcycleBound);
  264               }
  265           }
  266           if (cnay != null) {
  267               if (ycycleBound == y[0]) {
  268                   cnay.setBoundMappedToLastCycle(y[1] <= ycycleBound);
  269               }
  270               if (ycycleBound == y[1]) {
  271                   cnay.setBoundMappedToLastCycle(y[0] <= ycycleBound);
  272               }
  273           }
  274           super.drawItem(
  275               g2, state, dataArea, info, plot, domainAxis, rangeAxis, 
  276               newset, series, 1, crosshairState, pass
  277           );
  278   
  279           if (cnax != null) {
  280               if (xcycleBound == x[1]) {
  281                   cnax.setBoundMappedToLastCycle(x[2] <= xcycleBound);
  282               }
  283               if (xcycleBound == x[2]) {
  284                   cnax.setBoundMappedToLastCycle(x[1] <= xcycleBound);
  285               }
  286           }
  287           if (cnay != null) {
  288               if (ycycleBound == y[1]) {
  289                   cnay.setBoundMappedToLastCycle(y[2] <= ycycleBound);
  290               }
  291               if (ycycleBound == y[2]) {
  292                   cnay.setBoundMappedToLastCycle(y[1] <= ycycleBound);
  293               }
  294           }
  295           super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, 
  296                   newset, series, 2, crosshairState, pass);
  297   
  298           if (x.length == 4) {
  299               if (cnax != null) {
  300                   if (xcycleBound == x[2]) {
  301                       cnax.setBoundMappedToLastCycle(x[3] <= xcycleBound);
  302                   }
  303                   if (xcycleBound == x[3]) {
  304                       cnax.setBoundMappedToLastCycle(x[2] <= xcycleBound);
  305                   }
  306               }
  307               if (cnay != null) {
  308                   if (ycycleBound == y[2]) {
  309                       cnay.setBoundMappedToLastCycle(y[3] <= ycycleBound);
  310                   }
  311                   if (ycycleBound == y[3]) {
  312                       cnay.setBoundMappedToLastCycle(y[2] <= ycycleBound);
  313                   }
  314               }
  315               super.drawItem(g2, state, dataArea, info, plot, domainAxis, 
  316                       rangeAxis, newset, series, 3, crosshairState, pass);
  317           }
  318           
  319           if (cnax != null) {
  320               cnax.setBoundMappedToLastCycle(xBoundMapping);
  321           }
  322           if (cnay != null) {
  323               cnay.setBoundMappedToLastCycle(yBoundMapping);
  324           }
  325       }
  326   
  327       /** 
  328        * A dataset to hold the interpolated points when drawing new lines. 
  329        */
  330       protected static class OverwriteDataSet implements XYDataset {
  331           
  332           /** The delegate dataset. */
  333           protected XYDataset delegateSet;
  334           
  335           /** Storage for the x and y values. */
  336           Double[] x, y;
  337           
  338           /**
  339            * Creates a new dataset.
  340            * 
  341            * @param x  the x values.
  342            * @param y  the y values.
  343            * @param delegateSet  the dataset.
  344            */
  345           public OverwriteDataSet(double [] x, double[] y, 
  346                                   XYDataset delegateSet) {
  347               this.delegateSet = delegateSet;
  348               this.x = new Double[x.length]; this.y = new Double[y.length];
  349               for (int i = 0; i < x.length; ++i) { 
  350                   this.x[i] = new Double(x[i]);
  351                   this.y[i] = new Double(y[i]);
  352               }
  353           }
  354   
  355           /**
  356            * Returns the order of the domain (X) values.
  357            * 
  358            * @return The domain order.
  359            */
  360           public DomainOrder getDomainOrder() {
  361               return DomainOrder.NONE;
  362           }
  363           
  364           /**
  365            * Returns the number of items for the given series.
  366            * 
  367            * @param series  the series index (zero-based).
  368            * 
  369            * @return The item count.
  370            */
  371           public int getItemCount(int series) {
  372               return this.x.length;
  373           }
  374   
  375           /**
  376            * Returns the x-value.
  377            * 
  378            * @param series  the series index (zero-based).
  379            * @param item  the item index (zero-based).
  380            * 
  381            * @return The x-value.
  382            */
  383           public Number getX(int series, int item) {
  384               return this.x[item];
  385           }
  386   
  387           /**
  388            * Returns the x-value (as a double primitive) for an item within a 
  389            * series.
  390            * 
  391            * @param series  the series (zero-based index).
  392            * @param item  the item (zero-based index).
  393            * 
  394            * @return The x-value.
  395            */
  396           public double getXValue(int series, int item) {
  397               double result = Double.NaN;
  398               Number x = getX(series, item);
  399               if (x != null) {
  400                   result = x.doubleValue();   
  401               }
  402               return result;   
  403           }
  404   
  405           /**
  406            * Returns the y-value.
  407            * 
  408            * @param series  the series index (zero-based).
  409            * @param item  the item index (zero-based).
  410            * 
  411            * @return The y-value.
  412            */
  413           public Number getY(int series, int item) {
  414               return this.y[item];
  415           }
  416   
  417           /**
  418            * Returns the y-value (as a double primitive) for an item within a 
  419            * series.
  420            * 
  421            * @param series  the series (zero-based index).
  422            * @param item  the item (zero-based index).
  423            * 
  424            * @return The y-value.
  425            */
  426           public double getYValue(int series, int item) {
  427               double result = Double.NaN;
  428               Number y = getY(series, item);
  429               if (y != null) {
  430                   result = y.doubleValue();   
  431               }
  432               return result;   
  433           }
  434   
  435           /**
  436            * Returns the number of series in the dataset.
  437            * 
  438            * @return The series count.
  439            */
  440           public int getSeriesCount() {
  441               return this.delegateSet.getSeriesCount();
  442           }
  443   
  444           /**
  445            * Returns the name of the given series.
  446            * 
  447            * @param series  the series index (zero-based).
  448            * 
  449            * @return The series name.
  450            */
  451           public Comparable getSeriesKey(int series) {
  452               return this.delegateSet.getSeriesKey(series);
  453           }
  454   
  455           /**
  456            * Returns the index of the named series, or -1.
  457            * 
  458            * @param seriesName  the series name.
  459            * 
  460            * @return The index.
  461            */
  462           public int indexOf(Comparable seriesName) {
  463               return this.delegateSet.indexOf(seriesName);
  464           }
  465   
  466           /**
  467            * Does nothing.
  468            * 
  469            * @param listener  ignored.
  470            */
  471           public void addChangeListener(DatasetChangeListener listener) {
  472               // unused in parent
  473           }
  474   
  475           /**
  476            * Does nothing.
  477            * 
  478            * @param listener  ignored.
  479            */
  480           public void removeChangeListener(DatasetChangeListener listener) {
  481               // unused in parent
  482           }
  483   
  484           /**
  485            * Returns the dataset group.
  486            * 
  487            * @return The dataset group.
  488            */
  489           public DatasetGroup getGroup() {
  490               // unused but must return something, so while we are at it...
  491               return this.delegateSet.getGroup();
  492           }
  493   
  494           /**
  495            * Does nothing.
  496            * 
  497            * @param group  ignored.
  498            */
  499           public void setGroup(DatasetGroup group) {
  500               // unused in parent
  501           }
  502           
  503       }
  504       
  505   }
  506   
  507   

Save This Page
Home » jcommon-1.0.13 » org.jfree » chart » renderer » xy » [javadoc | source]