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-2008, 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    * StackedXYAreaRenderer2.java
   29    * ---------------------------
   30    * (C) Copyright 2004-2008, by Object Refinery Limited and Contributors.
   31    *
   32    * Original Author:  David Gilbert (for Object Refinery Limited), based on
   33    *                   the StackedXYAreaRenderer class by Richard Atkinson;
   34    * Contributor(s):   -;
   35    *
   36    * Changes:
   37    * --------
   38    * 30-Apr-2004 : Version 1 (DG);
   39    * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
   40    *               getYValue() (DG);
   41    * 10-Sep-2004 : Removed getRangeType() method (DG);
   42    * 06-Jan-2004 : Renamed getRangeExtent() --> findRangeBounds (DG);
   43    * 28-Mar-2005 : Use getXValue() and getYValue() from dataset (DG);
   44    * 03-Oct-2005 : Add entity generation to drawItem() method (DG);
   45    * ------------- JFREECHART 1.0.x ---------------------------------------------
   46    * 22-Aug-2006 : Handle null and empty datasets correctly in the
   47    *               findRangeBounds() method (DG);
   48    * 22-Sep-2006 : Added a flag to allow rounding of x-coordinates (after
   49    *               translation to Java2D space) in order to avoid the striping
   50    *               that can result from anti-aliasing (thanks to Doug
   51    *               Clayton) (DG);
   52    * 30-Nov-2006 : Added accessor methods for the roundXCoordinates flag (DG);
   53    * 02-Jun-2008 : Fixed bug with PlotOrientation.HORIZONTAL (DG);
   54    *
   55    */
   56   
   57   package org.jfree.chart.renderer.xy;
   58   
   59   import java.awt.Graphics2D;
   60   import java.awt.Paint;
   61   import java.awt.Shape;
   62   import java.awt.geom.GeneralPath;
   63   import java.awt.geom.Rectangle2D;
   64   import java.io.Serializable;
   65   
   66   import org.jfree.chart.axis.ValueAxis;
   67   import org.jfree.chart.entity.EntityCollection;
   68   import org.jfree.chart.event.RendererChangeEvent;
   69   import org.jfree.chart.labels.XYToolTipGenerator;
   70   import org.jfree.chart.plot.CrosshairState;
   71   import org.jfree.chart.plot.PlotOrientation;
   72   import org.jfree.chart.plot.PlotRenderingInfo;
   73   import org.jfree.chart.plot.XYPlot;
   74   import org.jfree.chart.urls.XYURLGenerator;
   75   import org.jfree.data.Range;
   76   import org.jfree.data.xy.TableXYDataset;
   77   import org.jfree.data.xy.XYDataset;
   78   import org.jfree.ui.RectangleEdge;
   79   import org.jfree.util.PublicCloneable;
   80   
   81   /**
   82    * A stacked area renderer for the {@link XYPlot} class.
   83    */
   84   public class StackedXYAreaRenderer2 extends XYAreaRenderer2
   85           implements Cloneable, PublicCloneable, Serializable {
   86   
   87       /** For serialization. */
   88       private static final long serialVersionUID = 7752676509764539182L;
   89   
   90       /**
   91        * This flag controls whether or not the x-coordinates (in Java2D space)
   92        * are rounded to integers.  When set to true, this can avoid the vertical
   93        * striping that anti-aliasing can generate.  However, the rounding may not
   94        * be appropriate for output in high resolution formats (for example,
   95        * vector graphics formats such as SVG and PDF).
   96        *
   97        * @since 1.0.3
   98        */
   99       private boolean roundXCoordinates;
  100   
  101       /**
  102        * Creates a new renderer.
  103        */
  104       public StackedXYAreaRenderer2() {
  105           this(null, null);
  106       }
  107   
  108       /**
  109        * Constructs a new renderer.
  110        *
  111        * @param labelGenerator  the tool tip generator to use.  <code>null</code>
  112        *                        is none.
  113        * @param urlGenerator  the URL generator (<code>null</code> permitted).
  114        */
  115       public StackedXYAreaRenderer2(XYToolTipGenerator labelGenerator,
  116                                     XYURLGenerator urlGenerator) {
  117           super(labelGenerator, urlGenerator);
  118           this.roundXCoordinates = true;
  119       }
  120   
  121       /**
  122        * Returns the flag that controls whether or not the x-coordinates (in
  123        * Java2D space) are rounded to integer values.
  124        *
  125        * @return The flag.
  126        *
  127        * @since 1.0.4
  128        *
  129        * @see #setRoundXCoordinates(boolean)
  130        */
  131       public boolean getRoundXCoordinates() {
  132           return this.roundXCoordinates;
  133       }
  134   
  135       /**
  136        * Sets the flag that controls whether or not the x-coordinates (in
  137        * Java2D space) are rounded to integer values, and sends a
  138        * {@link RendererChangeEvent} to all registered listeners.
  139        *
  140        * @param round  the new flag value.
  141        *
  142        * @since 1.0.4
  143        *
  144        * @see #getRoundXCoordinates()
  145        */
  146       public void setRoundXCoordinates(boolean round) {
  147           this.roundXCoordinates = round;
  148           fireChangeEvent();
  149       }
  150   
  151       /**
  152        * Returns the range of values the renderer requires to display all the
  153        * items from the specified dataset.
  154        *
  155        * @param dataset  the dataset (<code>null</code> permitted).
  156        *
  157        * @return The range (or <code>null</code> if the dataset is
  158        *         <code>null</code> or empty).
  159        */
  160       public Range findRangeBounds(XYDataset dataset) {
  161           if (dataset == null) {
  162               return null;
  163           }
  164           double min = Double.POSITIVE_INFINITY;
  165           double max = Double.NEGATIVE_INFINITY;
  166           TableXYDataset d = (TableXYDataset) dataset;
  167           int itemCount = d.getItemCount();
  168           for (int i = 0; i < itemCount; i++) {
  169               double[] stackValues = getStackValues((TableXYDataset) dataset,
  170                       d.getSeriesCount(), i);
  171               min = Math.min(min, stackValues[0]);
  172               max = Math.max(max, stackValues[1]);
  173           }
  174           if (min == Double.POSITIVE_INFINITY) {
  175               return null;
  176           }
  177           return new Range(min, max);
  178       }
  179   
  180       /**
  181        * Returns the number of passes required by the renderer.
  182        *
  183        * @return 1.
  184        */
  185       public int getPassCount() {
  186           return 1;
  187       }
  188   
  189       /**
  190        * Draws the visual representation of a single data item.
  191        *
  192        * @param g2  the graphics device.
  193        * @param state  the renderer state.
  194        * @param dataArea  the area within which the data is being drawn.
  195        * @param info  collects information about the drawing.
  196        * @param plot  the plot (can be used to obtain standard color information
  197        *              etc).
  198        * @param domainAxis  the domain axis.
  199        * @param rangeAxis  the range axis.
  200        * @param dataset  the dataset.
  201        * @param series  the series index (zero-based).
  202        * @param item  the item index (zero-based).
  203        * @param crosshairState  information about crosshairs on a plot.
  204        * @param pass  the pass index.
  205        */
  206       public void drawItem(Graphics2D g2,
  207                            XYItemRendererState state,
  208                            Rectangle2D dataArea,
  209                            PlotRenderingInfo info,
  210                            XYPlot plot,
  211                            ValueAxis domainAxis,
  212                            ValueAxis rangeAxis,
  213                            XYDataset dataset,
  214                            int series,
  215                            int item,
  216                            CrosshairState crosshairState,
  217                            int pass) {
  218   
  219           // setup for collecting optional entity info...
  220           Shape entityArea = null;
  221           EntityCollection entities = null;
  222           if (info != null) {
  223               entities = info.getOwner().getEntityCollection();
  224           }
  225   
  226           TableXYDataset tdataset = (TableXYDataset) dataset;
  227           PlotOrientation orientation = plot.getOrientation();
  228   
  229           // get the data point...
  230           double x1 = dataset.getXValue(series, item);
  231           double y1 = dataset.getYValue(series, item);
  232           if (Double.isNaN(y1)) {
  233               y1 = 0.0;
  234           }
  235           double[] stack1 = getStackValues(tdataset, series, item);
  236   
  237           // get the previous point and the next point so we can calculate a
  238           // "hot spot" for the area (used by the chart entity)...
  239           double x0 = dataset.getXValue(series, Math.max(item - 1, 0));
  240           double y0 = dataset.getYValue(series, Math.max(item - 1, 0));
  241           if (Double.isNaN(y0)) {
  242               y0 = 0.0;
  243           }
  244           double[] stack0 = getStackValues(tdataset, series, Math.max(item - 1,
  245                   0));
  246   
  247           int itemCount = dataset.getItemCount(series);
  248           double x2 = dataset.getXValue(series, Math.min(item + 1,
  249                   itemCount - 1));
  250           double y2 = dataset.getYValue(series, Math.min(item + 1,
  251                   itemCount - 1));
  252           if (Double.isNaN(y2)) {
  253               y2 = 0.0;
  254           }
  255           double[] stack2 = getStackValues(tdataset, series, Math.min(item + 1,
  256                   itemCount - 1));
  257   
  258           double xleft = (x0 + x1) / 2.0;
  259           double xright = (x1 + x2) / 2.0;
  260           double[] stackLeft = averageStackValues(stack0, stack1);
  261           double[] stackRight = averageStackValues(stack1, stack2);
  262           double[] adjStackLeft = adjustedStackValues(stack0, stack1);
  263           double[] adjStackRight = adjustedStackValues(stack1, stack2);
  264   
  265           RectangleEdge edge0 = plot.getDomainAxisEdge();
  266   
  267           float transX1 = (float) domainAxis.valueToJava2D(x1, dataArea, edge0);
  268           float transXLeft = (float) domainAxis.valueToJava2D(xleft, dataArea,
  269                   edge0);
  270           float transXRight = (float) domainAxis.valueToJava2D(xright, dataArea,
  271                   edge0);
  272   
  273           if (this.roundXCoordinates) {
  274               transX1 = Math.round(transX1);
  275               transXLeft = Math.round(transXLeft);
  276               transXRight = Math.round(transXRight);
  277           }
  278           float transY1;
  279   
  280           RectangleEdge edge1 = plot.getRangeAxisEdge();
  281   
  282           GeneralPath left = new GeneralPath();
  283           GeneralPath right = new GeneralPath();
  284           if (y1 >= 0.0) {  // handle positive value
  285               transY1 = (float) rangeAxis.valueToJava2D(y1 + stack1[1], dataArea,
  286                       edge1);
  287               float transStack1 = (float) rangeAxis.valueToJava2D(stack1[1],
  288                       dataArea, edge1);
  289               float transStackLeft = (float) rangeAxis.valueToJava2D(
  290                       adjStackLeft[1], dataArea, edge1);
  291   
  292               // LEFT POLYGON
  293               if (y0 >= 0.0) {
  294                   double yleft = (y0 + y1) / 2.0 + stackLeft[1];
  295                   float transYLeft
  296                       = (float) rangeAxis.valueToJava2D(yleft, dataArea, edge1);
  297                   if (orientation == PlotOrientation.VERTICAL) {
  298                       left.moveTo(transX1, transY1);
  299                       left.lineTo(transX1, transStack1);
  300                       left.lineTo(transXLeft, transStackLeft);
  301                       left.lineTo(transXLeft, transYLeft);
  302                   }
  303                   else {
  304                   	left.moveTo(transY1, transX1);
  305                   	left.lineTo(transStack1, transX1);
  306                   	left.lineTo(transStackLeft, transXLeft);
  307                   	left.lineTo(transYLeft, transXLeft);
  308                   }
  309                   left.closePath();
  310               }
  311               else {
  312               	if (orientation == PlotOrientation.VERTICAL) {
  313                       left.moveTo(transX1, transStack1);
  314                       left.lineTo(transX1, transY1);
  315                       left.lineTo(transXLeft, transStackLeft);
  316               	}
  317               	else {
  318                       left.moveTo(transStack1, transX1);
  319                       left.lineTo(transY1, transX1);
  320                       left.lineTo(transStackLeft, transXLeft);
  321               	}
  322                   left.closePath();
  323               }
  324   
  325               float transStackRight = (float) rangeAxis.valueToJava2D(
  326                       adjStackRight[1], dataArea, edge1);
  327               // RIGHT POLYGON
  328               if (y2 >= 0.0) {
  329                   double yright = (y1 + y2) / 2.0 + stackRight[1];
  330                   float transYRight
  331                       = (float) rangeAxis.valueToJava2D(yright, dataArea, edge1);
  332                   if (orientation == PlotOrientation.VERTICAL) {
  333                       right.moveTo(transX1, transStack1);
  334                       right.lineTo(transX1, transY1);
  335                       right.lineTo(transXRight, transYRight);
  336                       right.lineTo(transXRight, transStackRight);
  337                   }
  338                   else {
  339                       right.moveTo(transStack1, transX1);
  340                       right.lineTo(transY1, transX1);
  341                       right.lineTo(transYRight, transXRight);
  342                       right.lineTo(transStackRight, transXRight);
  343                   }
  344                   right.closePath();
  345               }
  346               else {
  347               	if (orientation == PlotOrientation.VERTICAL) {
  348                       right.moveTo(transX1, transStack1);
  349                       right.lineTo(transX1, transY1);
  350                       right.lineTo(transXRight, transStackRight);
  351               	}
  352               	else {
  353                       right.moveTo(transStack1, transX1);
  354                       right.lineTo(transY1, transX1);
  355                       right.lineTo(transStackRight, transXRight);
  356               	}
  357                   right.closePath();
  358               }
  359           }
  360           else {  // handle negative value
  361               transY1 = (float) rangeAxis.valueToJava2D(y1 + stack1[0], dataArea,
  362                       edge1);
  363               float transStack1 = (float) rangeAxis.valueToJava2D(stack1[0],
  364                       dataArea, edge1);
  365               float transStackLeft = (float) rangeAxis.valueToJava2D(
  366                       adjStackLeft[0], dataArea, edge1);
  367   
  368               // LEFT POLYGON
  369               if (y0 >= 0.0) {
  370               	if (orientation == PlotOrientation.VERTICAL) {
  371                       left.moveTo(transX1, transStack1);
  372                       left.lineTo(transX1, transY1);
  373                       left.lineTo(transXLeft, transStackLeft);
  374               	}
  375               	else {
  376                       left.moveTo(transStack1, transX1);
  377                       left.lineTo(transY1, transX1);
  378                       left.lineTo(transStackLeft, transXLeft);
  379               	}
  380                   left.clone();
  381               }
  382               else {
  383                   double yleft = (y0 + y1) / 2.0 + stackLeft[0];
  384                   float transYLeft = (float) rangeAxis.valueToJava2D(yleft,
  385                           dataArea, edge1);
  386                   if (orientation == PlotOrientation.VERTICAL) {
  387                       left.moveTo(transX1, transY1);
  388                       left.lineTo(transX1, transStack1);
  389                       left.lineTo(transXLeft, transStackLeft);
  390                       left.lineTo(transXLeft, transYLeft);
  391                   }
  392                   else {
  393                       left.moveTo(transY1, transX1);
  394                       left.lineTo(transStack1, transX1);
  395                       left.lineTo(transStackLeft, transXLeft);
  396                       left.lineTo(transYLeft, transXLeft);
  397                   }
  398                   left.closePath();
  399               }
  400               float transStackRight = (float) rangeAxis.valueToJava2D(
  401                       adjStackRight[0], dataArea, edge1);
  402   
  403               // RIGHT POLYGON
  404               if (y2 >= 0.0) {
  405               	if (orientation == PlotOrientation.VERTICAL) {
  406                       right.moveTo(transX1, transStack1);
  407                       right.lineTo(transX1, transY1);
  408                       right.lineTo(transXRight, transStackRight);
  409               	}
  410               	else {
  411                       right.moveTo(transStack1, transX1);
  412                       right.lineTo(transY1, transX1);
  413                       right.lineTo(transStackRight, transXRight);
  414               	}
  415                   right.closePath();
  416               }
  417               else {
  418                   double yright = (y1 + y2) / 2.0 + stackRight[0];
  419                   float transYRight = (float) rangeAxis.valueToJava2D(yright,
  420                           dataArea, edge1);
  421                   if (orientation == PlotOrientation.VERTICAL) {
  422                       right.moveTo(transX1, transStack1);
  423                       right.lineTo(transX1, transY1);
  424                       right.lineTo(transXRight, transYRight);
  425                       right.lineTo(transXRight, transStackRight);
  426                   }
  427                   else {
  428                       right.moveTo(transStack1, transX1);
  429                       right.lineTo(transY1, transX1);
  430                       right.lineTo(transYRight, transXRight);
  431                       right.lineTo(transStackRight, transXRight);
  432                   }
  433                   right.closePath();
  434               }
  435           }
  436   
  437           //  Get series Paint and Stroke
  438           Paint itemPaint = getItemPaint(series, item);
  439           if (pass == 0) {
  440               g2.setPaint(itemPaint);
  441               g2.fill(left);
  442               g2.fill(right);
  443           }
  444   
  445           // add an entity for the item...
  446           if (entities != null) {
  447               GeneralPath gp = new GeneralPath(left);
  448               gp.append(right, false);
  449               entityArea = gp;
  450               addEntity(entities, entityArea, dataset, series, item,
  451                       transX1, transY1);
  452           }
  453   
  454       }
  455   
  456       /**
  457        * Calculates the stacked values (one positive and one negative) of all
  458        * series up to, but not including, <code>series</code> for the specified
  459        * item. It returns [0.0, 0.0] if <code>series</code> is the first series.
  460        *
  461        * @param dataset  the dataset (<code>null</code> not permitted).
  462        * @param series  the series index.
  463        * @param index  the item index.
  464        *
  465        * @return An array containing the cumulative negative and positive values
  466        *     for all series values up to but excluding <code>series</code>
  467        *     for <code>index</code>.
  468        */
  469       private double[] getStackValues(TableXYDataset dataset,
  470                                       int series, int index) {
  471           double[] result = new double[2];
  472           for (int i = 0; i < series; i++) {
  473               double v = dataset.getYValue(i, index);
  474               if (!Double.isNaN(v)) {
  475                   if (v >= 0.0) {
  476                       result[1] += v;
  477                   }
  478                   else {
  479                       result[0] += v;
  480                   }
  481               }
  482           }
  483           return result;
  484       }
  485   
  486       /**
  487        * Returns a pair of "stack" values calculated as the mean of the two
  488        * specified stack value pairs.
  489        *
  490        * @param stack1  the first stack pair.
  491        * @param stack2  the second stack pair.
  492        *
  493        * @return A pair of average stack values.
  494        */
  495       private double[] averageStackValues(double[] stack1, double[] stack2) {
  496           double[] result = new double[2];
  497           result[0] = (stack1[0] + stack2[0]) / 2.0;
  498           result[1] = (stack1[1] + stack2[1]) / 2.0;
  499           return result;
  500       }
  501   
  502       /**
  503        * Calculates adjusted stack values from the supplied values.  The value is
  504        * the mean of the supplied values, unless either of the supplied values
  505        * is zero, in which case the adjusted value is zero also.
  506        *
  507        * @param stack1  the first stack pair.
  508        * @param stack2  the second stack pair.
  509        *
  510        * @return A pair of average stack values.
  511        */
  512       private double[] adjustedStackValues(double[] stack1, double[] stack2) {
  513           double[] result = new double[2];
  514           if (stack1[0] == 0.0 || stack2[0] == 0.0) {
  515               result[0] = 0.0;
  516           }
  517           else {
  518               result[0] = (stack1[0] + stack2[0]) / 2.0;
  519           }
  520           if (stack1[1] == 0.0 || stack2[1] == 0.0) {
  521               result[1] = 0.0;
  522           }
  523           else {
  524               result[1] = (stack1[1] + stack2[1]) / 2.0;
  525           }
  526           return result;
  527       }
  528   
  529       /**
  530        * Tests this renderer for equality with an arbitrary object.
  531        *
  532        * @param obj  the object (<code>null</code> permitted).
  533        *
  534        * @return A boolean.
  535        */
  536       public boolean equals(Object obj) {
  537           if (obj == this) {
  538               return true;
  539           }
  540           if (!(obj instanceof StackedXYAreaRenderer2)) {
  541               return false;
  542           }
  543           StackedXYAreaRenderer2 that = (StackedXYAreaRenderer2) obj;
  544           if (this.roundXCoordinates != that.roundXCoordinates) {
  545               return false;
  546           }
  547           return super.equals(obj);
  548       }
  549   
  550       /**
  551        * Returns a clone of the renderer.
  552        *
  553        * @return A clone.
  554        *
  555        * @throws CloneNotSupportedException  if the renderer cannot be cloned.
  556        */
  557       public Object clone() throws CloneNotSupportedException {
  558           return super.clone();
  559       }
  560   
  561   }

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