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    * XYStepAreaRenderer.java
   29    * -----------------------
   30    * (C) Copyright 2003-2008, by Matthias Rose and Contributors.
   31    *
   32    * Original Author:  Matthias Rose (based on XYAreaRenderer.java);
   33    * Contributor(s):   David Gilbert (for Object Refinery Limited);
   34    *
   35    * Changes:
   36    * --------
   37    * 07-Oct-2003 : Version 1, contributed by Matthias Rose (DG);
   38    * 10-Feb-2004 : Added some getter and setter methods (DG);
   39    * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState.  Renamed
   40    *               XYToolTipGenerator --> XYItemLabelGenerator (DG);
   41    * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
   42    *               getYValue() (DG);
   43    * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
   44    * 06-Jul-2005 : Renamed get/setPlotShapes() --> get/setShapesVisible() (DG);
   45    * ------------- JFREECHART 1.0.x ---------------------------------------------
   46    * 06-Jul-2006 : Modified to call dataset methods that return double
   47    *               primitives only (DG);
   48    * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG);
   49    * 14-Feb-2007 : Added equals() method override (DG);
   50    * 04-May-2007 : Set processVisibleItemsOnly flag to false (DG);
   51    * 14-May-2008 : Call addEntity() from within drawItem() (DG);
   52    *
   53    */
   54   
   55   package org.jfree.chart.renderer.xy;
   56   
   57   import java.awt.Graphics2D;
   58   import java.awt.Paint;
   59   import java.awt.Polygon;
   60   import java.awt.Shape;
   61   import java.awt.Stroke;
   62   import java.awt.geom.Rectangle2D;
   63   import java.io.Serializable;
   64   
   65   import org.jfree.chart.axis.ValueAxis;
   66   import org.jfree.chart.entity.EntityCollection;
   67   import org.jfree.chart.event.RendererChangeEvent;
   68   import org.jfree.chart.labels.XYToolTipGenerator;
   69   import org.jfree.chart.plot.CrosshairState;
   70   import org.jfree.chart.plot.PlotOrientation;
   71   import org.jfree.chart.plot.PlotRenderingInfo;
   72   import org.jfree.chart.plot.XYPlot;
   73   import org.jfree.chart.urls.XYURLGenerator;
   74   import org.jfree.data.xy.XYDataset;
   75   import org.jfree.util.PublicCloneable;
   76   import org.jfree.util.ShapeUtilities;
   77   
   78   /**
   79    * A step chart renderer that fills the area between the step and the x-axis.
   80    */
   81   public class XYStepAreaRenderer extends AbstractXYItemRenderer
   82           implements XYItemRenderer, Cloneable, PublicCloneable, Serializable {
   83   
   84       /** For serialization. */
   85       private static final long serialVersionUID = -7311560779702649635L;
   86   
   87       /** Useful constant for specifying the type of rendering (shapes only). */
   88       public static final int SHAPES = 1;
   89   
   90       /** Useful constant for specifying the type of rendering (area only). */
   91       public static final int AREA = 2;
   92   
   93       /**
   94        * Useful constant for specifying the type of rendering (area and shapes).
   95        */
   96       public static final int AREA_AND_SHAPES = 3;
   97   
   98       /** A flag indicating whether or not shapes are drawn at each XY point. */
   99       private boolean shapesVisible;
  100   
  101       /** A flag that controls whether or not shapes are filled for ALL series. */
  102       private boolean shapesFilled;
  103   
  104       /** A flag indicating whether or not Area are drawn at each XY point. */
  105       private boolean plotArea;
  106   
  107       /** A flag that controls whether or not the outline is shown. */
  108       private boolean showOutline;
  109   
  110       /** Area of the complete series */
  111       protected transient Polygon pArea = null;
  112   
  113       /**
  114        * The value on the range axis which defines the 'lower' border of the
  115        * area.
  116        */
  117       private double rangeBase;
  118   
  119       /**
  120        * Constructs a new renderer.
  121        */
  122       public XYStepAreaRenderer() {
  123           this(AREA);
  124       }
  125   
  126       /**
  127        * Constructs a new renderer.
  128        *
  129        * @param type  the type of the renderer.
  130        */
  131       public XYStepAreaRenderer(int type) {
  132           this(type, null, null);
  133       }
  134   
  135       /**
  136        * Constructs a new renderer.
  137        * <p>
  138        * To specify the type of renderer, use one of the constants:
  139        * AREA, SHAPES or AREA_AND_SHAPES.
  140        *
  141        * @param type  the type of renderer.
  142        * @param toolTipGenerator  the tool tip generator to use
  143        *                          (<code>null</code> permitted).
  144        * @param urlGenerator  the URL generator (<code>null</code> permitted).
  145        */
  146       public XYStepAreaRenderer(int type,
  147                                 XYToolTipGenerator toolTipGenerator,
  148                                 XYURLGenerator urlGenerator) {
  149   
  150           super();
  151           setBaseToolTipGenerator(toolTipGenerator);
  152           setURLGenerator(urlGenerator);
  153   
  154           if (type == AREA) {
  155               this.plotArea = true;
  156           }
  157           else if (type == SHAPES) {
  158               this.shapesVisible = true;
  159           }
  160           else if (type == AREA_AND_SHAPES) {
  161               this.plotArea = true;
  162               this.shapesVisible = true;
  163           }
  164           this.showOutline = false;
  165       }
  166   
  167       /**
  168        * Returns a flag that controls whether or not outlines of the areas are
  169        * drawn.
  170        *
  171        * @return The flag.
  172        *
  173        * @see #setOutline(boolean)
  174        */
  175       public boolean isOutline() {
  176           return this.showOutline;
  177       }
  178   
  179       /**
  180        * Sets a flag that controls whether or not outlines of the areas are
  181        * drawn, and sends a {@link RendererChangeEvent} to all registered
  182        * listeners.
  183        *
  184        * @param show  the flag.
  185        *
  186        * @see #isOutline()
  187        */
  188       public void setOutline(boolean show) {
  189           this.showOutline = show;
  190           fireChangeEvent();
  191       }
  192   
  193       /**
  194        * Returns true if shapes are being plotted by the renderer.
  195        *
  196        * @return <code>true</code> if shapes are being plotted by the renderer.
  197        *
  198        * @see #setShapesVisible(boolean)
  199        */
  200       public boolean getShapesVisible() {
  201           return this.shapesVisible;
  202       }
  203   
  204       /**
  205        * Sets the flag that controls whether or not shapes are displayed for each
  206        * data item, and sends a {@link RendererChangeEvent} to all registered
  207        * listeners.
  208        *
  209        * @param flag  the flag.
  210        *
  211        * @see #getShapesVisible()
  212        */
  213       public void setShapesVisible(boolean flag) {
  214           this.shapesVisible = flag;
  215           fireChangeEvent();
  216       }
  217   
  218       /**
  219        * Returns the flag that controls whether or not the shapes are filled.
  220        *
  221        * @return A boolean.
  222        *
  223        * @see #setShapesFilled(boolean)
  224        */
  225       public boolean isShapesFilled() {
  226           return this.shapesFilled;
  227       }
  228   
  229       /**
  230        * Sets the 'shapes filled' for ALL series and sends a
  231        * {@link RendererChangeEvent} to all registered listeners.
  232        *
  233        * @param filled  the flag.
  234        *
  235        * @see #isShapesFilled()
  236        */
  237       public void setShapesFilled(boolean filled) {
  238           this.shapesFilled = filled;
  239           fireChangeEvent();
  240       }
  241   
  242       /**
  243        * Returns true if Area is being plotted by the renderer.
  244        *
  245        * @return <code>true</code> if Area is being plotted by the renderer.
  246        *
  247        * @see #setPlotArea(boolean)
  248        */
  249       public boolean getPlotArea() {
  250           return this.plotArea;
  251       }
  252   
  253       /**
  254        * Sets a flag that controls whether or not areas are drawn for each data
  255        * item and sends a {@link RendererChangeEvent} to all registered
  256        * listeners.
  257        *
  258        * @param flag  the flag.
  259        *
  260        * @see #getPlotArea()
  261        */
  262       public void setPlotArea(boolean flag) {
  263           this.plotArea = flag;
  264           fireChangeEvent();
  265       }
  266   
  267       /**
  268        * Returns the value on the range axis which defines the 'lower' border of
  269        * the area.
  270        *
  271        * @return <code>double</code> the value on the range axis which defines
  272        *         the 'lower' border of the area.
  273        *
  274        * @see #setRangeBase(double)
  275        */
  276       public double getRangeBase() {
  277           return this.rangeBase;
  278       }
  279   
  280       /**
  281        * Sets the value on the range axis which defines the default border of the
  282        * area, and sends a {@link RendererChangeEvent} to all registered
  283        * listeners.  E.g. setRangeBase(Double.NEGATIVE_INFINITY) lets areas always
  284        * reach the lower border of the plotArea.
  285        *
  286        * @param val  the value on the range axis which defines the default border
  287        *             of the area.
  288        *
  289        * @see #getRangeBase()
  290        */
  291       public void setRangeBase(double val) {
  292           this.rangeBase = val;
  293           fireChangeEvent();
  294       }
  295   
  296       /**
  297        * Initialises the renderer.  Here we calculate the Java2D y-coordinate for
  298        * zero, since all the bars have their bases fixed at zero.
  299        *
  300        * @param g2  the graphics device.
  301        * @param dataArea  the area inside the axes.
  302        * @param plot  the plot.
  303        * @param data  the data.
  304        * @param info  an optional info collection object to return data back to
  305        *              the caller.
  306        *
  307        * @return The number of passes required by the renderer.
  308        */
  309       public XYItemRendererState initialise(Graphics2D g2,
  310                                             Rectangle2D dataArea,
  311                                             XYPlot plot,
  312                                             XYDataset data,
  313                                             PlotRenderingInfo info) {
  314   
  315   
  316           XYItemRendererState state = super.initialise(g2, dataArea, plot, data,
  317                   info);
  318           // disable visible items optimisation - it doesn't work for this
  319           // renderer...
  320           state.setProcessVisibleItemsOnly(false);
  321           return state;
  322   
  323       }
  324   
  325   
  326       /**
  327        * Draws the visual representation of a single data item.
  328        *
  329        * @param g2  the graphics device.
  330        * @param state  the renderer state.
  331        * @param dataArea  the area within which the data is being drawn.
  332        * @param info  collects information about the drawing.
  333        * @param plot  the plot (can be used to obtain standard color information
  334        *              etc).
  335        * @param domainAxis  the domain axis.
  336        * @param rangeAxis  the range axis.
  337        * @param dataset  the dataset.
  338        * @param series  the series index (zero-based).
  339        * @param item  the item index (zero-based).
  340        * @param crosshairState  crosshair information for the plot
  341        *                        (<code>null</code> permitted).
  342        * @param pass  the pass index.
  343        */
  344       public void drawItem(Graphics2D g2,
  345                            XYItemRendererState state,
  346                            Rectangle2D dataArea,
  347                            PlotRenderingInfo info,
  348                            XYPlot plot,
  349                            ValueAxis domainAxis,
  350                            ValueAxis rangeAxis,
  351                            XYDataset dataset,
  352                            int series,
  353                            int item,
  354                            CrosshairState crosshairState,
  355                            int pass) {
  356   
  357           PlotOrientation orientation = plot.getOrientation();
  358   
  359           // Get the item count for the series, so that we can know which is the
  360           // end of the series.
  361           int itemCount = dataset.getItemCount(series);
  362   
  363           Paint paint = getItemPaint(series, item);
  364           Stroke seriesStroke = getItemStroke(series, item);
  365           g2.setPaint(paint);
  366           g2.setStroke(seriesStroke);
  367   
  368           // get the data point...
  369           double x1 = dataset.getXValue(series, item);
  370           double y1 = dataset.getYValue(series, item);
  371           double x = x1;
  372           double y = Double.isNaN(y1) ? getRangeBase() : y1;
  373           double transX1 = domainAxis.valueToJava2D(x, dataArea,
  374                   plot.getDomainAxisEdge());
  375           double transY1 = rangeAxis.valueToJava2D(y, dataArea,
  376                   plot.getRangeAxisEdge());
  377   
  378           // avoid possible sun.dc.pr.PRException: endPath: bad path
  379           transY1 = restrictValueToDataArea(transY1, plot, dataArea);
  380   
  381           if (this.pArea == null && !Double.isNaN(y1)) {
  382   
  383               // Create a new Area for the series
  384               this.pArea = new Polygon();
  385   
  386               // start from Y = rangeBase
  387               double transY2 = rangeAxis.valueToJava2D(getRangeBase(), dataArea,
  388                       plot.getRangeAxisEdge());
  389   
  390               // avoid possible sun.dc.pr.PRException: endPath: bad path
  391               transY2 = restrictValueToDataArea(transY2, plot, dataArea);
  392   
  393               // The first point is (x, this.baseYValue)
  394               if (orientation == PlotOrientation.VERTICAL) {
  395                   this.pArea.addPoint((int) transX1, (int) transY2);
  396               }
  397               else if (orientation == PlotOrientation.HORIZONTAL) {
  398                   this.pArea.addPoint((int) transY2, (int) transX1);
  399               }
  400           }
  401   
  402           double transX0 = 0;
  403           double transY0 = restrictValueToDataArea(getRangeBase(), plot,
  404                   dataArea);
  405   
  406           double x0;
  407           double y0;
  408           if (item > 0) {
  409               // get the previous data point...
  410               x0 = dataset.getXValue(series, item - 1);
  411               y0 = Double.isNaN(y1) ? y1 : dataset.getYValue(series, item - 1);
  412   
  413               x = x0;
  414               y = Double.isNaN(y0) ? getRangeBase() : y0;
  415               transX0 = domainAxis.valueToJava2D(x, dataArea,
  416                       plot.getDomainAxisEdge());
  417               transY0 = rangeAxis.valueToJava2D(y, dataArea,
  418                       plot.getRangeAxisEdge());
  419   
  420               // avoid possible sun.dc.pr.PRException: endPath: bad path
  421               transY0 = restrictValueToDataArea(transY0, plot, dataArea);
  422   
  423               if (Double.isNaN(y1)) {
  424                   // NULL value -> insert point on base line
  425                   // instead of 'step point'
  426                   transX1 = transX0;
  427                   transY0 = transY1;
  428               }
  429               if (transY0 != transY1) {
  430                   // not just a horizontal bar but need to perform a 'step'.
  431                   if (orientation == PlotOrientation.VERTICAL) {
  432                       this.pArea.addPoint((int) transX1, (int) transY0);
  433                   }
  434                   else if (orientation == PlotOrientation.HORIZONTAL) {
  435                       this.pArea.addPoint((int) transY0, (int) transX1);
  436                   }
  437               }
  438           }
  439   
  440           Shape shape = null;
  441           if (!Double.isNaN(y1)) {
  442               // Add each point to Area (x, y)
  443               if (orientation == PlotOrientation.VERTICAL) {
  444                   this.pArea.addPoint((int) transX1, (int) transY1);
  445               }
  446               else if (orientation == PlotOrientation.HORIZONTAL) {
  447                   this.pArea.addPoint((int) transY1, (int) transX1);
  448               }
  449   
  450               if (getShapesVisible()) {
  451                   shape = getItemShape(series, item);
  452                   if (orientation == PlotOrientation.VERTICAL) {
  453                       shape = ShapeUtilities.createTranslatedShape(shape,
  454                               transX1, transY1);
  455                   }
  456                   else if (orientation == PlotOrientation.HORIZONTAL) {
  457                       shape = ShapeUtilities.createTranslatedShape(shape,
  458                               transY1, transX1);
  459                   }
  460                   if (isShapesFilled()) {
  461                       g2.fill(shape);
  462                   }
  463                   else {
  464                       g2.draw(shape);
  465                   }
  466               }
  467               else {
  468                   if (orientation == PlotOrientation.VERTICAL) {
  469                       shape = new Rectangle2D.Double(transX1 - 2, transY1 - 2,
  470                               4.0, 4.0);
  471                   }
  472                   else if (orientation == PlotOrientation.HORIZONTAL) {
  473                       shape = new Rectangle2D.Double(transY1 - 2, transX1 - 2,
  474                               4.0, 4.0);
  475                   }
  476               }
  477           }
  478   
  479           // Check if the item is the last item for the series or if it
  480           // is a NULL value and number of items > 0.  We can't draw an area for
  481           // a single point.
  482           if (getPlotArea() && item > 0 && this.pArea != null
  483                             && (item == (itemCount - 1) || Double.isNaN(y1))) {
  484   
  485               double transY2 = rangeAxis.valueToJava2D(getRangeBase(), dataArea,
  486                       plot.getRangeAxisEdge());
  487   
  488               // avoid possible sun.dc.pr.PRException: endPath: bad path
  489               transY2 = restrictValueToDataArea(transY2, plot, dataArea);
  490   
  491               if (orientation == PlotOrientation.VERTICAL) {
  492                   // Add the last point (x,0)
  493                   this.pArea.addPoint((int) transX1, (int) transY2);
  494               }
  495               else if (orientation == PlotOrientation.HORIZONTAL) {
  496                   // Add the last point (x,0)
  497                   this.pArea.addPoint((int) transY2, (int) transX1);
  498               }
  499   
  500               // fill the polygon
  501               g2.fill(this.pArea);
  502   
  503               // draw an outline around the Area.
  504               if (isOutline()) {
  505                   g2.setStroke(plot.getOutlineStroke());
  506                   g2.setPaint(plot.getOutlinePaint());
  507                   g2.draw(this.pArea);
  508               }
  509   
  510               // start new area when needed (see above)
  511               this.pArea = null;
  512           }
  513   
  514           // do we need to update the crosshair values?
  515           if (!Double.isNaN(y1)) {
  516               int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
  517               int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
  518               updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
  519                       rangeAxisIndex, transX1, transY1, orientation);
  520           }
  521   
  522           // collect entity and tool tip information...
  523           EntityCollection entities = state.getEntityCollection();
  524           if (entities != null) {
  525               addEntity(entities, shape, dataset, series, item, transX1, transY1);
  526           }
  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 XYStepAreaRenderer)) {
  541               return false;
  542           }
  543           XYStepAreaRenderer that = (XYStepAreaRenderer) obj;
  544           if (this.showOutline != that.showOutline) {
  545               return false;
  546           }
  547           if (this.shapesVisible != that.shapesVisible) {
  548               return false;
  549           }
  550           if (this.shapesFilled != that.shapesFilled) {
  551               return false;
  552           }
  553           if (this.plotArea != that.plotArea) {
  554               return false;
  555           }
  556           if (this.rangeBase != that.rangeBase) {
  557               return false;
  558           }
  559           return super.equals(obj);
  560       }
  561   
  562       /**
  563        * Returns a clone of the renderer.
  564        *
  565        * @return A clone.
  566        *
  567        * @throws CloneNotSupportedException  if the renderer cannot be cloned.
  568        */
  569       public Object clone() throws CloneNotSupportedException {
  570           return super.clone();
  571       }
  572   
  573       /**
  574        * Helper method which returns a value if it lies
  575        * inside the visible dataArea and otherwise the corresponding
  576        * coordinate on the border of the dataArea. The PlotOrientation
  577        * is taken into account.
  578        * Useful to avoid possible sun.dc.pr.PRException: endPath: bad path
  579        * which occurs when trying to draw lines/shapes which in large part
  580        * lie outside of the visible dataArea.
  581        *
  582        * @param value the value which shall be
  583        * @param dataArea  the area within which the data is being drawn.
  584        * @param plot  the plot (can be used to obtain standard color
  585        *              information etc).
  586        * @return <code>double</code> value inside the data area.
  587        */
  588       protected static double restrictValueToDataArea(double value,
  589                                                       XYPlot plot,
  590                                                       Rectangle2D dataArea) {
  591           double min = 0;
  592           double max = 0;
  593           if (plot.getOrientation() == PlotOrientation.VERTICAL) {
  594               min = dataArea.getMinY();
  595               max = dataArea.getMaxY();
  596           }
  597           else if (plot.getOrientation() ==  PlotOrientation.HORIZONTAL) {
  598               min = dataArea.getMinX();
  599               max = dataArea.getMaxX();
  600           }
  601           if (value < min) {
  602               value = min;
  603           }
  604           else if (value > max) {
  605               value = max;
  606           }
  607           return value;
  608       }
  609   
  610   }

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