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    * StandardXYItemRenderer.java
   29    * ---------------------------
   30    * (C) Copyright 2001-2008, by Object Refinery Limited and Contributors.
   31    *
   32    * Original Author:  David Gilbert (for Object Refinery Limited);
   33    * Contributor(s):   Mark Watson (www.markwatson.com);
   34    *                   Jonathan Nash;
   35    *                   Andreas Schneider;
   36    *                   Norbert Kiesel (for TBD Networks);
   37    *                   Christian W. Zuckschwerdt;
   38    *                   Bill Kelemen;
   39    *                   Nicolas Brodu (for Astrium and EADS Corporate Research
   40    *                   Center);
   41    *
   42    * Changes:
   43    * --------
   44    * 19-Oct-2001 : Version 1, based on code by Mark Watson (DG);
   45    * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
   46    * 21-Dec-2001 : Added working line instance to improve performance (DG);
   47    * 22-Jan-2002 : Added code to lock crosshairs to data points.  Based on code
   48    *               by Jonathan Nash (DG);
   49    * 23-Jan-2002 : Added DrawInfo parameter to drawItem() method (DG);
   50    * 28-Mar-2002 : Added a property change listener mechanism so that the
   51    *               renderer no longer needs to be immutable (DG);
   52    * 02-Apr-2002 : Modified to handle null values (DG);
   53    * 09-Apr-2002 : Modified draw method to return void.  Removed the translated
   54    *               zero from the drawItem method.  Override the initialise()
   55    *               method to calculate it (DG);
   56    * 13-May-2002 : Added code from Andreas Schneider to allow changing
   57    *               shapes/colors per item (DG);
   58    * 24-May-2002 : Incorporated tooltips into chart entities (DG);
   59    * 25-Jun-2002 : Removed redundant code (DG);
   60    * 05-Aug-2002 : Incorporated URLs for HTML image maps into chart entities (RA);
   61    * 08-Aug-2002 : Added discontinuous lines option contributed by
   62    *               Norbert Kiesel (DG);
   63    * 20-Aug-2002 : Added user definable default values to be returned by
   64    *               protected methods unless overridden by a subclass (DG);
   65    * 23-Sep-2002 : Updated for changes in the XYItemRenderer interface (DG);
   66    * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
   67    * 25-Mar-2003 : Implemented Serializable (DG);
   68    * 01-May-2003 : Modified drawItem() method signature (DG);
   69    * 15-May-2003 : Modified to take into account the plot orientation (DG);
   70    * 29-Jul-2003 : Amended code that doesn't compile with JDK 1.2.2 (DG);
   71    * 30-Jul-2003 : Modified entity constructor (CZ);
   72    * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
   73    * 24-Aug-2003 : Added null/NaN checks in drawItem (BK);
   74    * 08-Sep-2003 : Fixed serialization (NB);
   75    * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
   76    * 21-Jan-2004 : Override for getLegendItem() method (DG);
   77    * 27-Jan-2004 : Moved working line into state object (DG);
   78    * 10-Feb-2004 : Changed drawItem() method to make cut-and-paste overriding
   79    *               easier (DG);
   80    * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState.  Renamed
   81    *               XYToolTipGenerator --> XYItemLabelGenerator (DG);
   82    * 08-Jun-2004 : Modified to use getX() and getY() methods (DG);
   83    * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
   84    *               getYValue() (DG);
   85    * 25-Aug-2004 : Created addEntity() method in superclass (DG);
   86    * 08-Oct-2004 : Added 'gapThresholdType' as suggested by Mike Watts (DG);
   87    * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
   88    * 23-Feb-2005 : Fixed getLegendItem() method to show lines.  Fixed bug
   89    *               1077108 (shape not visible for first item in series) (DG);
   90    * 10-Apr-2005 : Fixed item label positioning with horizontal orientation (DG);
   91    * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG);
   92    * 27-Apr-2005 : Use generator for series label in legend (DG);
   93    * ------------- JFREECHART 1.0.x ---------------------------------------------
   94    * 15-Jun-2006 : Fixed bug (1380480) for rendering series as path (DG);
   95    * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG);
   96    * 14-Mar-2007 : Fixed problems with the equals() and clone() methods (DG);
   97    * 23-Mar-2007 : Clean-up of shapesFilled attributes (DG);
   98    * 20-Apr-2007 : Updated getLegendItem() and drawItem() for renderer
   99    *               change (DG);
  100    * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem()
  101    *               method (DG);
  102    * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
  103    * 08-Jun-2007 : Fixed bug in entity creation (DG);
  104    * 21-Nov-2007 : Deprecated override flag methods (DG);
  105    * 02-Jun-2008 : Fixed tooltips for data items at lower edges of data area (DG);
  106    *
  107    */
  108   
  109   package org.jfree.chart.renderer.xy;
  110   
  111   import java.awt.Graphics2D;
  112   import java.awt.Image;
  113   import java.awt.Paint;
  114   import java.awt.Point;
  115   import java.awt.Shape;
  116   import java.awt.Stroke;
  117   import java.awt.geom.GeneralPath;
  118   import java.awt.geom.Line2D;
  119   import java.awt.geom.Rectangle2D;
  120   import java.io.IOException;
  121   import java.io.ObjectInputStream;
  122   import java.io.ObjectOutputStream;
  123   import java.io.Serializable;
  124   
  125   import org.jfree.chart.LegendItem;
  126   import org.jfree.chart.axis.ValueAxis;
  127   import org.jfree.chart.entity.EntityCollection;
  128   import org.jfree.chart.event.RendererChangeEvent;
  129   import org.jfree.chart.labels.XYToolTipGenerator;
  130   import org.jfree.chart.plot.CrosshairState;
  131   import org.jfree.chart.plot.Plot;
  132   import org.jfree.chart.plot.PlotOrientation;
  133   import org.jfree.chart.plot.PlotRenderingInfo;
  134   import org.jfree.chart.plot.XYPlot;
  135   import org.jfree.chart.urls.XYURLGenerator;
  136   import org.jfree.data.xy.XYDataset;
  137   import org.jfree.io.SerialUtilities;
  138   import org.jfree.ui.RectangleEdge;
  139   import org.jfree.util.BooleanList;
  140   import org.jfree.util.BooleanUtilities;
  141   import org.jfree.util.ObjectUtilities;
  142   import org.jfree.util.PublicCloneable;
  143   import org.jfree.util.ShapeUtilities;
  144   import org.jfree.util.UnitType;
  145   
  146   /**
  147    * Standard item renderer for an {@link XYPlot}.  This class can draw (a)
  148    * shapes at each point, or (b) lines between points, or (c) both shapes and
  149    * lines.
  150    * <P>
  151    * This renderer has been retained for historical reasons and, in general, you
  152    * should use the {@link XYLineAndShapeRenderer} class instead.
  153    */
  154   public class StandardXYItemRenderer extends AbstractXYItemRenderer
  155           implements XYItemRenderer, Cloneable, PublicCloneable, Serializable {
  156   
  157       /** For serialization. */
  158       private static final long serialVersionUID = -3271351259436865995L;
  159   
  160       /** Constant for the type of rendering (shapes only). */
  161       public static final int SHAPES = 1;
  162   
  163       /** Constant for the type of rendering (lines only). */
  164       public static final int LINES = 2;
  165   
  166       /** Constant for the type of rendering (shapes and lines). */
  167       public static final int SHAPES_AND_LINES = SHAPES | LINES;
  168   
  169       /** Constant for the type of rendering (images only). */
  170       public static final int IMAGES = 4;
  171   
  172       /** Constant for the type of rendering (discontinuous lines). */
  173       public static final int DISCONTINUOUS = 8;
  174   
  175       /** Constant for the type of rendering (discontinuous lines). */
  176       public static final int DISCONTINUOUS_LINES = LINES | DISCONTINUOUS;
  177   
  178       /** A flag indicating whether or not shapes are drawn at each XY point. */
  179       private boolean baseShapesVisible;
  180   
  181       /** A flag indicating whether or not lines are drawn between XY points. */
  182       private boolean plotLines;
  183   
  184       /** A flag indicating whether or not images are drawn between XY points. */
  185       private boolean plotImages;
  186   
  187       /** A flag controlling whether or not discontinuous lines are used. */
  188       private boolean plotDiscontinuous;
  189   
  190       /** Specifies how the gap threshold value is interpreted. */
  191       private UnitType gapThresholdType = UnitType.RELATIVE;
  192   
  193       /** Threshold for deciding when to discontinue a line. */
  194       private double gapThreshold = 1.0;
  195   
  196       /**
  197        * A flag that controls whether or not shapes are filled for ALL series.
  198        *
  199        * @deprecated As of 1.0.8, this override should not be used.
  200        */
  201       private Boolean shapesFilled;
  202   
  203       /**
  204        * A table of flags that control (per series) whether or not shapes are
  205        * filled.
  206        */
  207       private BooleanList seriesShapesFilled;
  208   
  209       /** The default value returned by the getShapeFilled() method. */
  210       private boolean baseShapesFilled;
  211   
  212       /**
  213        * A flag that controls whether or not each series is drawn as a single
  214        * path.
  215        */
  216       private boolean drawSeriesLineAsPath;
  217   
  218       /**
  219        * The shape that is used to represent a line in the legend.
  220        * This should never be set to <code>null</code>.
  221        */
  222       private transient Shape legendLine;
  223   
  224       /**
  225        * Constructs a new renderer.
  226        */
  227       public StandardXYItemRenderer() {
  228           this(LINES, null);
  229       }
  230   
  231       /**
  232        * Constructs a new renderer.  To specify the type of renderer, use one of
  233        * the constants: {@link #SHAPES}, {@link #LINES} or
  234        * {@link #SHAPES_AND_LINES}.
  235        *
  236        * @param type  the type.
  237        */
  238       public StandardXYItemRenderer(int type) {
  239           this(type, null);
  240       }
  241   
  242       /**
  243        * Constructs a new renderer.  To specify the type of renderer, use one of
  244        * the constants: {@link #SHAPES}, {@link #LINES} or
  245        * {@link #SHAPES_AND_LINES}.
  246        *
  247        * @param type  the type of renderer.
  248        * @param toolTipGenerator  the item label generator (<code>null</code>
  249        *                          permitted).
  250        */
  251       public StandardXYItemRenderer(int type,
  252                                     XYToolTipGenerator toolTipGenerator) {
  253           this(type, toolTipGenerator, null);
  254       }
  255   
  256       /**
  257        * Constructs a new renderer.  To specify the type of renderer, use one of
  258        * the constants: {@link #SHAPES}, {@link #LINES} or
  259        * {@link #SHAPES_AND_LINES}.
  260        *
  261        * @param type  the type of renderer.
  262        * @param toolTipGenerator  the item label generator (<code>null</code>
  263        *                          permitted).
  264        * @param urlGenerator  the URL generator.
  265        */
  266       public StandardXYItemRenderer(int type,
  267                                     XYToolTipGenerator toolTipGenerator,
  268                                     XYURLGenerator urlGenerator) {
  269   
  270           super();
  271           setBaseToolTipGenerator(toolTipGenerator);
  272           setURLGenerator(urlGenerator);
  273           if ((type & SHAPES) != 0) {
  274               this.baseShapesVisible = true;
  275           }
  276           if ((type & LINES) != 0) {
  277               this.plotLines = true;
  278           }
  279           if ((type & IMAGES) != 0) {
  280               this.plotImages = true;
  281           }
  282           if ((type & DISCONTINUOUS) != 0) {
  283               this.plotDiscontinuous = true;
  284           }
  285   
  286           this.shapesFilled = null;
  287           this.seriesShapesFilled = new BooleanList();
  288           this.baseShapesFilled = true;
  289           this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0);
  290           this.drawSeriesLineAsPath = false;
  291       }
  292   
  293       /**
  294        * Returns true if shapes are being plotted by the renderer.
  295        *
  296        * @return <code>true</code> if shapes are being plotted by the renderer.
  297        *
  298        * @see #setBaseShapesVisible
  299        */
  300       public boolean getBaseShapesVisible() {
  301           return this.baseShapesVisible;
  302       }
  303   
  304       /**
  305        * Sets the flag that controls whether or not a shape is plotted at each
  306        * data point.
  307        *
  308        * @param flag  the flag.
  309        *
  310        * @see #getBaseShapesVisible
  311        */
  312       public void setBaseShapesVisible(boolean flag) {
  313           if (this.baseShapesVisible != flag) {
  314               this.baseShapesVisible = flag;
  315               fireChangeEvent();
  316           }
  317       }
  318   
  319       // SHAPES FILLED
  320   
  321       /**
  322        * Returns the flag used to control whether or not the shape for an item is
  323        * filled.
  324        * <p>
  325        * The default implementation passes control to the
  326        * <code>getSeriesShapesFilled</code> method.  You can override this method
  327        * if you require different behaviour.
  328        *
  329        * @param series  the series index (zero-based).
  330        * @param item  the item index (zero-based).
  331        *
  332        * @return A boolean.
  333        *
  334        * @see #getSeriesShapesFilled(int)
  335        */
  336       public boolean getItemShapeFilled(int series, int item) {
  337           // return the overall setting, if there is one...
  338           if (this.shapesFilled != null) {
  339               return this.shapesFilled.booleanValue();
  340           }
  341   
  342           // otherwise look up the paint table
  343           Boolean flag = this.seriesShapesFilled.getBoolean(series);
  344           if (flag != null) {
  345               return flag.booleanValue();
  346           }
  347           else {
  348               return this.baseShapesFilled;
  349           }
  350       }
  351   
  352       /**
  353        * Returns the override flag that controls whether or not shapes are filled
  354        * for ALL series.
  355        *
  356        * @return The flag (possibly <code>null</code>).
  357        *
  358        * @since 1.0.5
  359        *
  360        * @deprecated As of 1.0.8, you should avoid using this method and rely
  361        *             on just the per-series ({@link #getSeriesShapesFilled(int)})
  362        *             and base-level ({@link #getBaseShapesFilled()}) settings.
  363        */
  364       public Boolean getShapesFilled() {
  365           return this.shapesFilled;
  366       }
  367   
  368       /**
  369        * Sets the override flag that controls whether or not shapes are filled
  370        * for ALL series and sends a {@link RendererChangeEvent} to all registered
  371        * listeners.
  372        *
  373        * @param filled  the flag.
  374        *
  375        * @see #setShapesFilled(Boolean)
  376        *
  377        * @deprecated As of 1.0.8, you should avoid using this method and rely
  378        *             on just the per-series ({@link #setSeriesShapesFilled(int,
  379        *             Boolean)}) and base-level ({@link #setBaseShapesVisible(
  380        *             boolean)}) settings.
  381        */
  382       public void setShapesFilled(boolean filled) {
  383           // here we use BooleanUtilities to remain compatible with JDKs < 1.4
  384           setShapesFilled(BooleanUtilities.valueOf(filled));
  385       }
  386   
  387       /**
  388        * Sets the override flag that controls whether or not shapes are filled
  389        * for ALL series and sends a {@link RendererChangeEvent} to all registered
  390        * listeners.
  391        *
  392        * @param filled  the flag (<code>null</code> permitted).
  393        *
  394        * @see #setShapesFilled(boolean)
  395        *
  396        * @deprecated As of 1.0.8, you should avoid using this method and rely
  397        *             on just the per-series ({@link #setSeriesShapesFilled(int,
  398        *             Boolean)}) and base-level ({@link #setBaseShapesVisible(
  399        *             boolean)}) settings.
  400        */
  401       public void setShapesFilled(Boolean filled) {
  402           this.shapesFilled = filled;
  403           fireChangeEvent();
  404       }
  405   
  406       /**
  407        * Returns the flag used to control whether or not the shapes for a series
  408        * are filled.
  409        *
  410        * @param series  the series index (zero-based).
  411        *
  412        * @return A boolean.
  413        */
  414       public Boolean getSeriesShapesFilled(int series) {
  415           return this.seriesShapesFilled.getBoolean(series);
  416       }
  417   
  418       /**
  419        * Sets the 'shapes filled' flag for a series and sends a
  420        * {@link RendererChangeEvent} to all registered listeners.
  421        *
  422        * @param series  the series index (zero-based).
  423        * @param flag  the flag.
  424        *
  425        * @see #getSeriesShapesFilled(int)
  426        */
  427       public void setSeriesShapesFilled(int series, Boolean flag) {
  428           this.seriesShapesFilled.setBoolean(series, flag);
  429           fireChangeEvent();
  430       }
  431   
  432       /**
  433        * Returns the base 'shape filled' attribute.
  434        *
  435        * @return The base flag.
  436        *
  437        * @see #setBaseShapesFilled(boolean)
  438        */
  439       public boolean getBaseShapesFilled() {
  440           return this.baseShapesFilled;
  441       }
  442   
  443       /**
  444        * Sets the base 'shapes filled' flag and sends a
  445        * {@link RendererChangeEvent} to all registered listeners.
  446        *
  447        * @param flag  the flag.
  448        *
  449        * @see #getBaseShapesFilled()
  450        */
  451       public void setBaseShapesFilled(boolean flag) {
  452           this.baseShapesFilled = flag;
  453       }
  454   
  455       /**
  456        * Returns true if lines are being plotted by the renderer.
  457        *
  458        * @return <code>true</code> if lines are being plotted by the renderer.
  459        *
  460        * @see #setPlotLines(boolean)
  461        */
  462       public boolean getPlotLines() {
  463           return this.plotLines;
  464       }
  465   
  466       /**
  467        * Sets the flag that controls whether or not a line is plotted between
  468        * each data point and sends a {@link RendererChangeEvent} to all
  469        * registered listeners.
  470        *
  471        * @param flag  the flag.
  472        *
  473        * @see #getPlotLines()
  474        */
  475       public void setPlotLines(boolean flag) {
  476           if (this.plotLines != flag) {
  477               this.plotLines = flag;
  478               fireChangeEvent();
  479           }
  480       }
  481   
  482       /**
  483        * Returns the gap threshold type (relative or absolute).
  484        *
  485        * @return The type.
  486        *
  487        * @see #setGapThresholdType(UnitType)
  488        */
  489       public UnitType getGapThresholdType() {
  490           return this.gapThresholdType;
  491       }
  492   
  493       /**
  494        * Sets the gap threshold type and sends a {@link RendererChangeEvent} to
  495        * all registered listeners.
  496        *
  497        * @param thresholdType  the type (<code>null</code> not permitted).
  498        *
  499        * @see #getGapThresholdType()
  500        */
  501       public void setGapThresholdType(UnitType thresholdType) {
  502           if (thresholdType == null) {
  503               throw new IllegalArgumentException(
  504                       "Null 'thresholdType' argument.");
  505           }
  506           this.gapThresholdType = thresholdType;
  507           fireChangeEvent();
  508       }
  509   
  510       /**
  511        * Returns the gap threshold for discontinuous lines.
  512        *
  513        * @return The gap threshold.
  514        *
  515        * @see #setGapThreshold(double)
  516        */
  517       public double getGapThreshold() {
  518           return this.gapThreshold;
  519       }
  520   
  521       /**
  522        * Sets the gap threshold for discontinuous lines and sends a
  523        * {@link RendererChangeEvent} to all registered listeners.
  524        *
  525        * @param t  the threshold.
  526        *
  527        * @see #getGapThreshold()
  528        */
  529       public void setGapThreshold(double t) {
  530           this.gapThreshold = t;
  531           fireChangeEvent();
  532       }
  533   
  534       /**
  535        * Returns true if images are being plotted by the renderer.
  536        *
  537        * @return <code>true</code> if images are being plotted by the renderer.
  538        *
  539        * @see #setPlotImages(boolean)
  540        */
  541       public boolean getPlotImages() {
  542           return this.plotImages;
  543       }
  544   
  545       /**
  546        * Sets the flag that controls whether or not an image is drawn at each
  547        * data point and sends a {@link RendererChangeEvent} to all registered
  548        * listeners.
  549        *
  550        * @param flag  the flag.
  551        *
  552        * @see #getPlotImages()
  553        */
  554       public void setPlotImages(boolean flag) {
  555           if (this.plotImages != flag) {
  556               this.plotImages = flag;
  557               fireChangeEvent();
  558           }
  559       }
  560   
  561       /**
  562        * Returns a flag that controls whether or not the renderer shows
  563        * discontinuous lines.
  564        *
  565        * @return <code>true</code> if lines should be discontinuous.
  566        */
  567       public boolean getPlotDiscontinuous() {
  568           return this.plotDiscontinuous;
  569       }
  570   
  571       /**
  572        * Sets the flag that controls whether or not the renderer shows
  573        * discontinuous lines, and sends a {@link RendererChangeEvent} to all
  574        * registered listeners.
  575        *
  576        * @param flag  the new flag value.
  577        *
  578        * @since 1.0.5
  579        */
  580       public void setPlotDiscontinuous(boolean flag) {
  581           if (this.plotDiscontinuous != flag) {
  582               this.plotDiscontinuous = flag;
  583               fireChangeEvent();
  584           }
  585       }
  586   
  587       /**
  588        * Returns a flag that controls whether or not each series is drawn as a
  589        * single path.
  590        *
  591        * @return A boolean.
  592        *
  593        * @see #setDrawSeriesLineAsPath(boolean)
  594        */
  595       public boolean getDrawSeriesLineAsPath() {
  596           return this.drawSeriesLineAsPath;
  597       }
  598   
  599       /**
  600        * Sets the flag that controls whether or not each series is drawn as a
  601        * single path.
  602        *
  603        * @param flag  the flag.
  604        *
  605        * @see #getDrawSeriesLineAsPath()
  606        */
  607       public void setDrawSeriesLineAsPath(boolean flag) {
  608           this.drawSeriesLineAsPath = flag;
  609       }
  610   
  611       /**
  612        * Returns the shape used to represent a line in the legend.
  613        *
  614        * @return The legend line (never <code>null</code>).
  615        *
  616        * @see #setLegendLine(Shape)
  617        */
  618       public Shape getLegendLine() {
  619           return this.legendLine;
  620       }
  621   
  622       /**
  623        * Sets the shape used as a line in each legend item and sends a
  624        * {@link RendererChangeEvent} to all registered listeners.
  625        *
  626        * @param line  the line (<code>null</code> not permitted).
  627        *
  628        * @see #getLegendLine()
  629        */
  630       public void setLegendLine(Shape line) {
  631           if (line == null) {
  632               throw new IllegalArgumentException("Null 'line' argument.");
  633           }
  634           this.legendLine = line;
  635           fireChangeEvent();
  636       }
  637   
  638       /**
  639        * Returns a legend item for a series.
  640        *
  641        * @param datasetIndex  the dataset index (zero-based).
  642        * @param series  the series index (zero-based).
  643        *
  644        * @return A legend item for the series.
  645        */
  646       public LegendItem getLegendItem(int datasetIndex, int series) {
  647           XYPlot plot = getPlot();
  648           if (plot == null) {
  649               return null;
  650           }
  651           LegendItem result = null;
  652           XYDataset dataset = plot.getDataset(datasetIndex);
  653           if (dataset != null) {
  654               if (getItemVisible(series, 0)) {
  655                   String label = getLegendItemLabelGenerator().generateLabel(
  656                           dataset, series);
  657                   String description = label;
  658                   String toolTipText = null;
  659                   if (getLegendItemToolTipGenerator() != null) {
  660                       toolTipText = getLegendItemToolTipGenerator().generateLabel(
  661                               dataset, series);
  662                   }
  663                   String urlText = null;
  664                   if (getLegendItemURLGenerator() != null) {
  665                       urlText = getLegendItemURLGenerator().generateLabel(
  666                               dataset, series);
  667                   }
  668                   Shape shape = lookupSeriesShape(series);
  669                   boolean shapeFilled = getItemShapeFilled(series, 0);
  670                   Paint paint = lookupSeriesPaint(series);
  671                   Paint linePaint = paint;
  672                   Stroke lineStroke = lookupSeriesStroke(series);
  673                   result = new LegendItem(label, description, toolTipText,
  674                           urlText, this.baseShapesVisible, shape, shapeFilled,
  675                           paint, !shapeFilled, paint, lineStroke,
  676                           this.plotLines, this.legendLine, lineStroke, linePaint);
  677                   result.setDataset(dataset);
  678                   result.setDatasetIndex(datasetIndex);
  679                   result.setSeriesKey(dataset.getSeriesKey(series));
  680                   result.setSeriesIndex(series);
  681               }
  682           }
  683           return result;
  684       }
  685   
  686       /**
  687        * Records the state for the renderer.  This is used to preserve state
  688        * information between calls to the drawItem() method for a single chart
  689        * drawing.
  690        */
  691       public static class State extends XYItemRendererState {
  692   
  693           /** The path for the current series. */
  694           public GeneralPath seriesPath;
  695   
  696           /** The series index. */
  697           private int seriesIndex;
  698   
  699           /**
  700            * A flag that indicates if the last (x, y) point was 'good'
  701            * (non-null).
  702            */
  703           private boolean lastPointGood;
  704   
  705           /**
  706            * Creates a new state instance.
  707            *
  708            * @param info  the plot rendering info.
  709            */
  710           public State(PlotRenderingInfo info) {
  711               super(info);
  712           }
  713   
  714           /**
  715            * Returns a flag that indicates if the last point drawn (in the
  716            * current series) was 'good' (non-null).
  717            *
  718            * @return A boolean.
  719            */
  720           public boolean isLastPointGood() {
  721               return this.lastPointGood;
  722           }
  723   
  724           /**
  725            * Sets a flag that indicates if the last point drawn (in the current
  726            * series) was 'good' (non-null).
  727            *
  728            * @param good  the flag.
  729            */
  730           public void setLastPointGood(boolean good) {
  731               this.lastPointGood = good;
  732           }
  733   
  734           /**
  735            * Returns the series index for the current path.
  736            *
  737            * @return The series index for the current path.
  738            */
  739           public int getSeriesIndex() {
  740               return this.seriesIndex;
  741           }
  742   
  743           /**
  744            * Sets the series index for the current path.
  745            *
  746            * @param index  the index.
  747            */
  748           public void setSeriesIndex(int index) {
  749               this.seriesIndex = index;
  750           }
  751       }
  752   
  753       /**
  754        * Initialises the renderer.
  755        * <P>
  756        * This method will be called before the first item is rendered, giving the
  757        * renderer an opportunity to initialise any state information it wants to
  758        * maintain. The renderer can do nothing if it chooses.
  759        *
  760        * @param g2  the graphics device.
  761        * @param dataArea  the area inside the axes.
  762        * @param plot  the plot.
  763        * @param data  the data.
  764        * @param info  an optional info collection object to return data back to
  765        *              the caller.
  766        *
  767        * @return The renderer state.
  768        */
  769       public XYItemRendererState initialise(Graphics2D g2,
  770                                             Rectangle2D dataArea,
  771                                             XYPlot plot,
  772                                             XYDataset data,
  773                                             PlotRenderingInfo info) {
  774   
  775           State state = new State(info);
  776           state.seriesPath = new GeneralPath();
  777           state.seriesIndex = -1;
  778           return state;
  779   
  780       }
  781   
  782       /**
  783        * Draws the visual representation of a single data item.
  784        *
  785        * @param g2  the graphics device.
  786        * @param state  the renderer state.
  787        * @param dataArea  the area within which the data is being drawn.
  788        * @param info  collects information about the drawing.
  789        * @param plot  the plot (can be used to obtain standard color information
  790        *              etc).
  791        * @param domainAxis  the domain axis.
  792        * @param rangeAxis  the range axis.
  793        * @param dataset  the dataset.
  794        * @param series  the series index (zero-based).
  795        * @param item  the item index (zero-based).
  796        * @param crosshairState  crosshair information for the plot
  797        *                        (<code>null</code> permitted).
  798        * @param pass  the pass index.
  799        */
  800       public void drawItem(Graphics2D g2,
  801                            XYItemRendererState state,
  802                            Rectangle2D dataArea,
  803                            PlotRenderingInfo info,
  804                            XYPlot plot,
  805                            ValueAxis domainAxis,
  806                            ValueAxis rangeAxis,
  807                            XYDataset dataset,
  808                            int series,
  809                            int item,
  810                            CrosshairState crosshairState,
  811                            int pass) {
  812   
  813           boolean itemVisible = getItemVisible(series, item);
  814   
  815           // setup for collecting optional entity info...
  816           Shape entityArea = null;
  817           EntityCollection entities = null;
  818           if (info != null) {
  819               entities = info.getOwner().getEntityCollection();
  820           }
  821   
  822           PlotOrientation orientation = plot.getOrientation();
  823           Paint paint = getItemPaint(series, item);
  824           Stroke seriesStroke = getItemStroke(series, item);
  825           g2.setPaint(paint);
  826           g2.setStroke(seriesStroke);
  827   
  828           // get the data point...
  829           double x1 = dataset.getXValue(series, item);
  830           double y1 = dataset.getYValue(series, item);
  831           if (Double.isNaN(x1) || Double.isNaN(y1)) {
  832               itemVisible = false;
  833           }
  834   
  835           RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
  836           RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
  837           double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
  838           double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
  839   
  840           if (getPlotLines()) {
  841               if (this.drawSeriesLineAsPath) {
  842                   State s = (State) state;
  843                   if (s.getSeriesIndex() != series) {
  844                       // we are starting a new series path
  845                       s.seriesPath.reset();
  846                       s.lastPointGood = false;
  847                       s.setSeriesIndex(series);
  848                   }
  849   
  850                   // update path to reflect latest point
  851                   if (itemVisible && !Double.isNaN(transX1)
  852                           && !Double.isNaN(transY1)) {
  853                       float x = (float) transX1;
  854                       float y = (float) transY1;
  855                       if (orientation == PlotOrientation.HORIZONTAL) {
  856                           x = (float) transY1;
  857                           y = (float) transX1;
  858                       }
  859                       if (s.isLastPointGood()) {
  860                           // TODO: check threshold
  861                           s.seriesPath.lineTo(x, y);
  862                       }
  863                       else {
  864                           s.seriesPath.moveTo(x, y);
  865                       }
  866                       s.setLastPointGood(true);
  867                   }
  868                   else {
  869                       s.setLastPointGood(false);
  870                   }
  871                   if (item == dataset.getItemCount(series) - 1) {
  872                       if (s.seriesIndex == series) {
  873                           // draw path
  874                           g2.setStroke(lookupSeriesStroke(series));
  875                           g2.setPaint(lookupSeriesPaint(series));
  876                           g2.draw(s.seriesPath);
  877                       }
  878                   }
  879               }
  880   
  881               else if (item != 0 && itemVisible) {
  882                   // get the previous data point...
  883                   double x0 = dataset.getXValue(series, item - 1);
  884                   double y0 = dataset.getYValue(series, item - 1);
  885                   if (!Double.isNaN(x0) && !Double.isNaN(y0)) {
  886                       boolean drawLine = true;
  887                       if (getPlotDiscontinuous()) {
  888                           // only draw a line if the gap between the current and
  889                           // previous data point is within the threshold
  890                           int numX = dataset.getItemCount(series);
  891                           double minX = dataset.getXValue(series, 0);
  892                           double maxX = dataset.getXValue(series, numX - 1);
  893                           if (this.gapThresholdType == UnitType.ABSOLUTE) {
  894                               drawLine = Math.abs(x1 - x0) <= this.gapThreshold;
  895                           }
  896                           else {
  897                               drawLine = Math.abs(x1 - x0) <= ((maxX - minX)
  898                                   / numX * getGapThreshold());
  899                           }
  900                       }
  901                       if (drawLine) {
  902                           double transX0 = domainAxis.valueToJava2D(x0, dataArea,
  903                                   xAxisLocation);
  904                           double transY0 = rangeAxis.valueToJava2D(y0, dataArea,
  905                                   yAxisLocation);
  906   
  907                           // only draw if we have good values
  908                           if (Double.isNaN(transX0) || Double.isNaN(transY0)
  909                               || Double.isNaN(transX1) || Double.isNaN(transY1)) {
  910                               return;
  911                           }
  912   
  913                           if (orientation == PlotOrientation.HORIZONTAL) {
  914                               state.workingLine.setLine(transY0, transX0,
  915                                       transY1, transX1);
  916                           }
  917                           else if (orientation == PlotOrientation.VERTICAL) {
  918                               state.workingLine.setLine(transX0, transY0,
  919                                       transX1, transY1);
  920                           }
  921   
  922                           if (state.workingLine.intersects(dataArea)) {
  923                               g2.draw(state.workingLine);
  924                           }
  925                       }
  926                   }
  927               }
  928           }
  929   
  930           // we needed to get this far even for invisible items, to ensure that
  931           // seriesPath updates happened, but now there is nothing more we need
  932           // to do for non-visible items...
  933           if (!itemVisible) {
  934               return;
  935           }
  936   
  937           if (getBaseShapesVisible()) {
  938   
  939               Shape shape = getItemShape(series, item);
  940               if (orientation == PlotOrientation.HORIZONTAL) {
  941                   shape = ShapeUtilities.createTranslatedShape(shape, transY1,
  942                           transX1);
  943               }
  944               else if (orientation == PlotOrientation.VERTICAL) {
  945                   shape = ShapeUtilities.createTranslatedShape(shape, transX1,
  946                           transY1);
  947               }
  948               if (shape.intersects(dataArea)) {
  949                   if (getItemShapeFilled(series, item)) {
  950                       g2.fill(shape);
  951                   }
  952                   else {
  953                       g2.draw(shape);
  954                   }
  955               }
  956               entityArea = shape;
  957   
  958           }
  959   
  960           if (getPlotImages()) {
  961               Image image = getImage(plot, series, item, transX1, transY1);
  962               if (image != null) {
  963                   Point hotspot = getImageHotspot(plot, series, item, transX1,
  964                           transY1, image);
  965                   g2.drawImage(image, (int) (transX1 - hotspot.getX()),
  966                           (int) (transY1 - hotspot.getY()), null);
  967                   entityArea = new Rectangle2D.Double(transX1 - hotspot.getX(),
  968                           transY1 - hotspot.getY(), image.getWidth(null),
  969                           image.getHeight(null));
  970               }
  971   
  972           }
  973   
  974           double xx = transX1;
  975           double yy = transY1;
  976           if (orientation == PlotOrientation.HORIZONTAL) {
  977               xx = transY1;
  978               yy = transX1;
  979           }
  980   
  981           // draw the item label if there is one...
  982           if (isItemLabelVisible(series, item)) {
  983               drawItemLabel(g2, orientation, dataset, series, item, xx, yy,
  984                       (y1 < 0.0));
  985           }
  986   
  987           int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
  988           int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
  989           updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
  990                   rangeAxisIndex, transX1, transY1, orientation);
  991   
  992           // add an entity for the item...
  993           if (entities != null && isPointInRect(dataArea, xx, yy)) {
  994               addEntity(entities, entityArea, dataset, series, item, xx, yy);
  995           }
  996   
  997       }
  998   
  999       /**
 1000        * Tests this renderer for equality with another object.
 1001        *
 1002        * @param obj  the object (<code>null</code> permitted).
 1003        *
 1004        * @return A boolean.
 1005        */
 1006       public boolean equals(Object obj) {
 1007   
 1008           if (obj == this) {
 1009               return true;
 1010           }
 1011           if (!(obj instanceof StandardXYItemRenderer)) {
 1012               return false;
 1013           }
 1014           StandardXYItemRenderer that = (StandardXYItemRenderer) obj;
 1015           if (this.baseShapesVisible != that.baseShapesVisible) {
 1016               return false;
 1017           }
 1018           if (this.plotLines != that.plotLines) {
 1019               return false;
 1020           }
 1021           if (this.plotImages != that.plotImages) {
 1022               return false;
 1023           }
 1024           if (this.plotDiscontinuous != that.plotDiscontinuous) {
 1025               return false;
 1026           }
 1027           if (this.gapThresholdType != that.gapThresholdType) {
 1028               return false;
 1029           }
 1030           if (this.gapThreshold != that.gapThreshold) {
 1031               return false;
 1032           }
 1033           if (!ObjectUtilities.equal(this.shapesFilled, that.shapesFilled)) {
 1034               return false;
 1035           }
 1036           if (!this.seriesShapesFilled.equals(that.seriesShapesFilled)) {
 1037               return false;
 1038           }
 1039           if (this.baseShapesFilled != that.baseShapesFilled) {
 1040               return false;
 1041           }
 1042           if (this.drawSeriesLineAsPath != that.drawSeriesLineAsPath) {
 1043               return false;
 1044           }
 1045           if (!ShapeUtilities.equal(this.legendLine, that.legendLine)) {
 1046               return false;
 1047           }
 1048           return super.equals(obj);
 1049   
 1050       }
 1051   
 1052       /**
 1053        * Returns a clone of the renderer.
 1054        *
 1055        * @return A clone.
 1056        *
 1057        * @throws CloneNotSupportedException  if the renderer cannot be cloned.
 1058        */
 1059       public Object clone() throws CloneNotSupportedException {
 1060           StandardXYItemRenderer clone = (StandardXYItemRenderer) super.clone();
 1061           clone.seriesShapesFilled
 1062                   = (BooleanList) this.seriesShapesFilled.clone();
 1063           clone.legendLine = ShapeUtilities.clone(this.legendLine);
 1064           return clone;
 1065       }
 1066   
 1067       ////////////////////////////////////////////////////////////////////////////
 1068       // PROTECTED METHODS
 1069       // These provide the opportunity to subclass the standard renderer and
 1070       // create custom effects.
 1071       ////////////////////////////////////////////////////////////////////////////
 1072   
 1073       /**
 1074        * Returns the image used to draw a single data item.
 1075        *
 1076        * @param plot  the plot (can be used to obtain standard color information
 1077        *              etc).
 1078        * @param series  the series index.
 1079        * @param item  the item index.
 1080        * @param x  the x value of the item.
 1081        * @param y  the y value of the item.
 1082        *
 1083        * @return The image.
 1084        *
 1085        * @see #getPlotImages()
 1086        */
 1087       protected Image getImage(Plot plot, int series, int item,
 1088                                double x, double y) {
 1089           // this method must be overridden if you want to display images
 1090           return null;
 1091       }
 1092   
 1093       /**
 1094        * Returns the hotspot of the image used to draw a single data item.
 1095        * The hotspot is the point relative to the top left of the image
 1096        * that should indicate the data item. The default is the center of the
 1097        * image.
 1098        *
 1099        * @param plot  the plot (can be used to obtain standard color information
 1100        *              etc).
 1101        * @param image  the image (can be used to get size information about the
 1102        *               image)
 1103        * @param series  the series index
 1104        * @param item  the item index
 1105        * @param x  the x value of the item
 1106        * @param y  the y value of the item
 1107        *
 1108        * @return The hotspot used to draw the data item.
 1109        */
 1110       protected Point getImageHotspot(Plot plot, int series, int item,
 1111                                       double x, double y, Image image) {
 1112   
 1113           int height = image.getHeight(null);
 1114           int width = image.getWidth(null);
 1115           return new Point(width / 2, height / 2);
 1116   
 1117       }
 1118   
 1119       /**
 1120        * Provides serialization support.
 1121        *
 1122        * @param stream  the input stream.
 1123        *
 1124        * @throws IOException  if there is an I/O error.
 1125        * @throws ClassNotFoundException  if there is a classpath problem.
 1126        */
 1127       private void readObject(ObjectInputStream stream)
 1128               throws IOException, ClassNotFoundException {
 1129           stream.defaultReadObject();
 1130           this.legendLine = SerialUtilities.readShape(stream);
 1131       }
 1132   
 1133       /**
 1134        * Provides serialization support.
 1135        *
 1136        * @param stream  the output stream.
 1137        *
 1138        * @throws IOException  if there is an I/O error.
 1139        */
 1140       private void writeObject(ObjectOutputStream stream) throws IOException {
 1141           stream.defaultWriteObject();
 1142           SerialUtilities.writeShape(this.legendLine, stream);
 1143       }
 1144   
 1145   }

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