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    * XYLineAndShapeRenderer.java
   29    * ---------------------------
   30    * (C) Copyright 2004-2008, by Object Refinery Limited.
   31    *
   32    * Original Author:  David Gilbert (for Object Refinery Limited);
   33    * Contributor(s):   -;
   34    *
   35    * Changes:
   36    * --------
   37    * 27-Jan-2004 : Version 1 (DG);
   38    * 10-Feb-2004 : Minor change to drawItem() method to make cut-and-paste
   39    *               overriding easier (DG);
   40    * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
   41    * 25-Aug-2004 : Added support for chart entities (required for tooltips) (DG);
   42    * 24-Sep-2004 : Added flag to allow whole series to be drawn as a path
   43    *               (necessary when using a dashed stroke with many data
   44    *               items) (DG);
   45    * 04-Oct-2004 : Renamed BooleanUtils --> BooleanUtilities (DG);
   46    * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
   47    * 27-Jan-2005 : The getLegendItem() method now omits hidden series (DG);
   48    * 28-Jan-2005 : Added new constructor (DG);
   49    * 09-Mar-2005 : Added fillPaint settings (DG);
   50    * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG);
   51    * 22-Jul-2005 : Renamed defaultLinesVisible --> baseLinesVisible,
   52    *               defaultShapesVisible --> baseShapesVisible and
   53    *               defaultShapesFilled --> baseShapesFilled (DG);
   54    * 29-Jul-2005 : Added code to draw item labels (DG);
   55    * ------------- JFREECHART 1.0.x ---------------------------------------------
   56    * 20-Jul-2006 : Set dataset and series indices in LegendItem (DG);
   57    * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG);
   58    * 21-Feb-2007 : Fixed bugs in clone() and equals() (DG);
   59    * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
   60    * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
   61    * 08-Jun-2007 : Fix for bug 1731912 where entities are created even for data
   62    *               items that are not displayed (DG);
   63    * 26-Oct-2007 : Deprecated override attributes (DG);
   64    * 02-Jun-2008 : Fixed tooltips at lower edges of data area (DG);
   65    *
   66    */
   67   
   68   package org.jfree.chart.renderer.xy;
   69   
   70   import java.awt.Graphics2D;
   71   import java.awt.Paint;
   72   import java.awt.Shape;
   73   import java.awt.Stroke;
   74   import java.awt.geom.GeneralPath;
   75   import java.awt.geom.Line2D;
   76   import java.awt.geom.Rectangle2D;
   77   import java.io.IOException;
   78   import java.io.ObjectInputStream;
   79   import java.io.ObjectOutputStream;
   80   import java.io.Serializable;
   81   
   82   import org.jfree.chart.LegendItem;
   83   import org.jfree.chart.axis.ValueAxis;
   84   import org.jfree.chart.entity.EntityCollection;
   85   import org.jfree.chart.event.RendererChangeEvent;
   86   import org.jfree.chart.plot.CrosshairState;
   87   import org.jfree.chart.plot.PlotOrientation;
   88   import org.jfree.chart.plot.PlotRenderingInfo;
   89   import org.jfree.chart.plot.XYPlot;
   90   import org.jfree.data.xy.XYDataset;
   91   import org.jfree.io.SerialUtilities;
   92   import org.jfree.ui.RectangleEdge;
   93   import org.jfree.util.BooleanList;
   94   import org.jfree.util.BooleanUtilities;
   95   import org.jfree.util.ObjectUtilities;
   96   import org.jfree.util.PublicCloneable;
   97   import org.jfree.util.ShapeUtilities;
   98   
   99   /**
  100    * A renderer that connects data points with lines and/or draws shapes at each
  101    * data point.  This renderer is designed for use with the {@link XYPlot}
  102    * class.
  103    */
  104   public class XYLineAndShapeRenderer extends AbstractXYItemRenderer
  105           implements XYItemRenderer, Cloneable, PublicCloneable, Serializable {
  106   
  107       /** For serialization. */
  108       private static final long serialVersionUID = -7435246895986425885L;
  109   
  110       /**
  111        * A flag that controls whether or not lines are visible for ALL series.
  112        *
  113        * @deprecated As of 1.0.7.
  114        */
  115       private Boolean linesVisible;
  116   
  117       /**
  118        * A table of flags that control (per series) whether or not lines are
  119        * visible.
  120        */
  121       private BooleanList seriesLinesVisible;
  122   
  123       /** The default value returned by the getLinesVisible() method. */
  124       private boolean baseLinesVisible;
  125   
  126       /** The shape that is used to represent a line in the legend. */
  127       private transient Shape legendLine;
  128   
  129       /**
  130        * A flag that controls whether or not shapes are visible for ALL series.
  131        *
  132        * @deprecated As of 1.0.7.
  133        */
  134       private Boolean shapesVisible;
  135   
  136       /**
  137        * A table of flags that control (per series) whether or not shapes are
  138        * visible.
  139        */
  140       private BooleanList seriesShapesVisible;
  141   
  142       /** The default value returned by the getShapeVisible() method. */
  143       private boolean baseShapesVisible;
  144   
  145       /**
  146        * A flag that controls whether or not shapes are filled for ALL series.
  147        *
  148        * @deprecated As of 1.0.7.
  149        */
  150       private Boolean shapesFilled;
  151   
  152       /**
  153        * A table of flags that control (per series) whether or not shapes are
  154        * filled.
  155        */
  156       private BooleanList seriesShapesFilled;
  157   
  158       /** The default value returned by the getShapeFilled() method. */
  159       private boolean baseShapesFilled;
  160   
  161       /** A flag that controls whether outlines are drawn for shapes. */
  162       private boolean drawOutlines;
  163   
  164       /**
  165        * A flag that controls whether the fill paint is used for filling
  166        * shapes.
  167        */
  168       private boolean useFillPaint;
  169   
  170       /**
  171        * A flag that controls whether the outline paint is used for drawing shape
  172        * outlines.
  173        */
  174       private boolean useOutlinePaint;
  175   
  176       /**
  177        * A flag that controls whether or not each series is drawn as a single
  178        * path.
  179        */
  180       private boolean drawSeriesLineAsPath;
  181   
  182       /**
  183        * Creates a new renderer with both lines and shapes visible.
  184        */
  185       public XYLineAndShapeRenderer() {
  186           this(true, true);
  187       }
  188   
  189       /**
  190        * Creates a new renderer.
  191        *
  192        * @param lines  lines visible?
  193        * @param shapes  shapes visible?
  194        */
  195       public XYLineAndShapeRenderer(boolean lines, boolean shapes) {
  196           this.linesVisible = null;
  197           this.seriesLinesVisible = new BooleanList();
  198           this.baseLinesVisible = lines;
  199           this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0);
  200   
  201           this.shapesVisible = null;
  202           this.seriesShapesVisible = new BooleanList();
  203           this.baseShapesVisible = shapes;
  204   
  205           this.shapesFilled = null;
  206           this.useFillPaint = false;     // use item paint for fills by default
  207           this.seriesShapesFilled = new BooleanList();
  208           this.baseShapesFilled = true;
  209   
  210           this.drawOutlines = true;
  211           this.useOutlinePaint = false;  // use item paint for outlines by
  212                                          // default, not outline paint
  213   
  214           this.drawSeriesLineAsPath = false;
  215       }
  216   
  217       /**
  218        * Returns a flag that controls whether or not each series is drawn as a
  219        * single path.
  220        *
  221        * @return A boolean.
  222        *
  223        * @see #setDrawSeriesLineAsPath(boolean)
  224        */
  225       public boolean getDrawSeriesLineAsPath() {
  226           return this.drawSeriesLineAsPath;
  227       }
  228   
  229       /**
  230        * Sets the flag that controls whether or not each series is drawn as a
  231        * single path and sends a {@link RendererChangeEvent} to all registered
  232        * listeners.
  233        *
  234        * @param flag  the flag.
  235        *
  236        * @see #getDrawSeriesLineAsPath()
  237        */
  238       public void setDrawSeriesLineAsPath(boolean flag) {
  239           if (this.drawSeriesLineAsPath != flag) {
  240               this.drawSeriesLineAsPath = flag;
  241               fireChangeEvent();
  242           }
  243       }
  244   
  245       /**
  246        * Returns the number of passes through the data that the renderer requires
  247        * in order to draw the chart.  Most charts will require a single pass, but
  248        * some require two passes.
  249        *
  250        * @return The pass count.
  251        */
  252       public int getPassCount() {
  253           return 2;
  254       }
  255   
  256       // LINES VISIBLE
  257   
  258       /**
  259        * Returns the flag used to control whether or not the shape for an item is
  260        * visible.
  261        *
  262        * @param series  the series index (zero-based).
  263        * @param item  the item index (zero-based).
  264        *
  265        * @return A boolean.
  266        */
  267       public boolean getItemLineVisible(int series, int item) {
  268           Boolean flag = this.linesVisible;
  269           if (flag == null) {
  270               flag = getSeriesLinesVisible(series);
  271           }
  272           if (flag != null) {
  273               return flag.booleanValue();
  274           }
  275           else {
  276               return this.baseLinesVisible;
  277           }
  278       }
  279   
  280       /**
  281        * Returns a flag that controls whether or not lines are drawn for ALL
  282        * series.  If this flag is <code>null</code>, then the "per series"
  283        * settings will apply.
  284        *
  285        * @return A flag (possibly <code>null</code>).
  286        *
  287        * @see #setLinesVisible(Boolean)
  288        *
  289        * @deprecated As of 1.0.7, use the per-series and base level settings.
  290        */
  291       public Boolean getLinesVisible() {
  292           return this.linesVisible;
  293       }
  294   
  295       /**
  296        * Sets a flag that controls whether or not lines are drawn between the
  297        * items in ALL series, and sends a {@link RendererChangeEvent} to all
  298        * registered listeners.  You need to set this to <code>null</code> if you
  299        * want the "per series" settings to apply.
  300        *
  301        * @param visible  the flag (<code>null</code> permitted).
  302        *
  303        * @see #getLinesVisible()
  304        *
  305        * @deprecated As of 1.0.7, use the per-series and base level settings.
  306        */
  307       public void setLinesVisible(Boolean visible) {
  308           this.linesVisible = visible;
  309           fireChangeEvent();
  310       }
  311   
  312       /**
  313        * Sets a flag that controls whether or not lines are drawn between the
  314        * items in ALL series, and sends a {@link RendererChangeEvent} to all
  315        * registered listeners.
  316        *
  317        * @param visible  the flag.
  318        *
  319        * @see #getLinesVisible()
  320        *
  321        * @deprecated As of 1.0.7, use the per-series and base level settings.
  322        */
  323       public void setLinesVisible(boolean visible) {
  324           // we use BooleanUtilities here to preserve JRE 1.3.1 compatibility
  325           setLinesVisible(BooleanUtilities.valueOf(visible));
  326       }
  327   
  328       /**
  329        * Returns the flag used to control whether or not the lines for a series
  330        * are visible.
  331        *
  332        * @param series  the series index (zero-based).
  333        *
  334        * @return The flag (possibly <code>null</code>).
  335        *
  336        * @see #setSeriesLinesVisible(int, Boolean)
  337        */
  338       public Boolean getSeriesLinesVisible(int series) {
  339           return this.seriesLinesVisible.getBoolean(series);
  340       }
  341   
  342       /**
  343        * Sets the 'lines visible' flag for a series and sends a
  344        * {@link RendererChangeEvent} to all registered listeners.
  345        *
  346        * @param series  the series index (zero-based).
  347        * @param flag  the flag (<code>null</code> permitted).
  348        *
  349        * @see #getSeriesLinesVisible(int)
  350        */
  351       public void setSeriesLinesVisible(int series, Boolean flag) {
  352           this.seriesLinesVisible.setBoolean(series, flag);
  353           fireChangeEvent();
  354       }
  355   
  356       /**
  357        * Sets the 'lines visible' flag for a series and sends a
  358        * {@link RendererChangeEvent} to all registered listeners.
  359        *
  360        * @param series  the series index (zero-based).
  361        * @param visible  the flag.
  362        *
  363        * @see #getSeriesLinesVisible(int)
  364        */
  365       public void setSeriesLinesVisible(int series, boolean visible) {
  366           setSeriesLinesVisible(series, BooleanUtilities.valueOf(visible));
  367       }
  368   
  369       /**
  370        * Returns the base 'lines visible' attribute.
  371        *
  372        * @return The base flag.
  373        *
  374        * @see #setBaseLinesVisible(boolean)
  375        */
  376       public boolean getBaseLinesVisible() {
  377           return this.baseLinesVisible;
  378       }
  379   
  380       /**
  381        * Sets the base 'lines visible' flag and sends a
  382        * {@link RendererChangeEvent} to all registered listeners.
  383        *
  384        * @param flag  the flag.
  385        *
  386        * @see #getBaseLinesVisible()
  387        */
  388       public void setBaseLinesVisible(boolean flag) {
  389           this.baseLinesVisible = flag;
  390           fireChangeEvent();
  391       }
  392   
  393       /**
  394        * Returns the shape used to represent a line in the legend.
  395        *
  396        * @return The legend line (never <code>null</code>).
  397        *
  398        * @see #setLegendLine(Shape)
  399        */
  400       public Shape getLegendLine() {
  401           return this.legendLine;
  402       }
  403   
  404       /**
  405        * Sets the shape used as a line in each legend item and sends a
  406        * {@link RendererChangeEvent} to all registered listeners.
  407        *
  408        * @param line  the line (<code>null</code> not permitted).
  409        *
  410        * @see #getLegendLine()
  411        */
  412       public void setLegendLine(Shape line) {
  413           if (line == null) {
  414               throw new IllegalArgumentException("Null 'line' argument.");
  415           }
  416           this.legendLine = line;
  417           fireChangeEvent();
  418       }
  419   
  420       // SHAPES VISIBLE
  421   
  422       /**
  423        * Returns the flag used to control whether or not the shape for an item is
  424        * visible.
  425        * <p>
  426        * The default implementation passes control to the
  427        * <code>getSeriesShapesVisible</code> method. You can override this method
  428        * if you require different behaviour.
  429        *
  430        * @param series  the series index (zero-based).
  431        * @param item  the item index (zero-based).
  432        *
  433        * @return A boolean.
  434        */
  435       public boolean getItemShapeVisible(int series, int item) {
  436           Boolean flag = this.shapesVisible;
  437           if (flag == null) {
  438               flag = getSeriesShapesVisible(series);
  439           }
  440           if (flag != null) {
  441               return flag.booleanValue();
  442           }
  443           else {
  444               return this.baseShapesVisible;
  445           }
  446       }
  447   
  448       /**
  449        * Returns the flag that controls whether the shapes are visible for the
  450        * items in ALL series.
  451        *
  452        * @return The flag (possibly <code>null</code>).
  453        *
  454        * @see #setShapesVisible(Boolean)
  455        *
  456        * @deprecated As of 1.0.7, use the per-series and base level settings.
  457        */
  458       public Boolean getShapesVisible() {
  459           return this.shapesVisible;
  460       }
  461   
  462       /**
  463        * Sets the 'shapes visible' for ALL series and sends a
  464        * {@link RendererChangeEvent} to all registered listeners.
  465        *
  466        * @param visible  the flag (<code>null</code> permitted).
  467        *
  468        * @see #getShapesVisible()
  469        *
  470        * @deprecated As of 1.0.7, use the per-series and base level settings.
  471        */
  472       public void setShapesVisible(Boolean visible) {
  473           this.shapesVisible = visible;
  474           fireChangeEvent();
  475       }
  476   
  477       /**
  478        * Sets the 'shapes visible' for ALL series and sends a
  479        * {@link RendererChangeEvent} to all registered listeners.
  480        *
  481        * @param visible  the flag.
  482        *
  483        * @see #getShapesVisible()
  484        *
  485        * @deprecated As of 1.0.7, use the per-series and base level settings.
  486        */
  487       public void setShapesVisible(boolean visible) {
  488           setShapesVisible(BooleanUtilities.valueOf(visible));
  489       }
  490   
  491       /**
  492        * Returns the flag used to control whether or not the shapes for a series
  493        * are visible.
  494        *
  495        * @param series  the series index (zero-based).
  496        *
  497        * @return A boolean.
  498        *
  499        * @see #setSeriesShapesVisible(int, Boolean)
  500        */
  501       public Boolean getSeriesShapesVisible(int series) {
  502           return this.seriesShapesVisible.getBoolean(series);
  503       }
  504   
  505       /**
  506        * Sets the 'shapes visible' flag for a series and sends a
  507        * {@link RendererChangeEvent} to all registered listeners.
  508        *
  509        * @param series  the series index (zero-based).
  510        * @param visible  the flag.
  511        *
  512        * @see #getSeriesShapesVisible(int)
  513        */
  514       public void setSeriesShapesVisible(int series, boolean visible) {
  515           setSeriesShapesVisible(series, BooleanUtilities.valueOf(visible));
  516       }
  517   
  518       /**
  519        * Sets the 'shapes visible' flag for a series and sends a
  520        * {@link RendererChangeEvent} to all registered listeners.
  521        *
  522        * @param series  the series index (zero-based).
  523        * @param flag  the flag.
  524        *
  525        * @see #getSeriesShapesVisible(int)
  526        */
  527       public void setSeriesShapesVisible(int series, Boolean flag) {
  528           this.seriesShapesVisible.setBoolean(series, flag);
  529           fireChangeEvent();
  530       }
  531   
  532       /**
  533        * Returns the base 'shape visible' attribute.
  534        *
  535        * @return The base flag.
  536        *
  537        * @see #setBaseShapesVisible(boolean)
  538        */
  539       public boolean getBaseShapesVisible() {
  540           return this.baseShapesVisible;
  541       }
  542   
  543       /**
  544        * Sets the base 'shapes visible' flag and sends a
  545        * {@link RendererChangeEvent} to all registered listeners.
  546        *
  547        * @param flag  the flag.
  548        *
  549        * @see #getBaseShapesVisible()
  550        */
  551       public void setBaseShapesVisible(boolean flag) {
  552           this.baseShapesVisible = flag;
  553           fireChangeEvent();
  554       }
  555   
  556       // SHAPES FILLED
  557   
  558       /**
  559        * Returns the flag used to control whether or not the shape for an item
  560        * is filled.
  561        * <p>
  562        * The default implementation passes control to the
  563        * <code>getSeriesShapesFilled</code> method. You can override this method
  564        * if you require different behaviour.
  565        *
  566        * @param series  the series index (zero-based).
  567        * @param item  the item index (zero-based).
  568        *
  569        * @return A boolean.
  570        */
  571       public boolean getItemShapeFilled(int series, int item) {
  572           Boolean flag = this.shapesFilled;
  573           if (flag == null) {
  574               flag = getSeriesShapesFilled(series);
  575           }
  576           if (flag != null) {
  577               return flag.booleanValue();
  578           }
  579           else {
  580               return this.baseShapesFilled;
  581           }
  582       }
  583   
  584       /**
  585        * Sets the 'shapes filled' for ALL series and sends a
  586        * {@link RendererChangeEvent} to all registered listeners.
  587        *
  588        * @param filled  the flag.
  589        *
  590        * @deprecated As of 1.0.7, use the per-series and base level settings.
  591        */
  592       public void setShapesFilled(boolean filled) {
  593           setShapesFilled(BooleanUtilities.valueOf(filled));
  594       }
  595   
  596       /**
  597        * Sets the 'shapes filled' for ALL series and sends a
  598        * {@link RendererChangeEvent} to all registered listeners.
  599        *
  600        * @param filled  the flag (<code>null</code> permitted).
  601        *
  602        * @deprecated As of 1.0.7, use the per-series and base level settings.
  603        */
  604       public void setShapesFilled(Boolean filled) {
  605           this.shapesFilled = filled;
  606           fireChangeEvent();
  607       }
  608   
  609       /**
  610        * Returns the flag used to control whether or not the shapes for a series
  611        * are filled.
  612        *
  613        * @param series  the series index (zero-based).
  614        *
  615        * @return A boolean.
  616        *
  617        * @see #setSeriesShapesFilled(int, Boolean)
  618        */
  619       public Boolean getSeriesShapesFilled(int series) {
  620           return this.seriesShapesFilled.getBoolean(series);
  621       }
  622   
  623       /**
  624        * Sets the 'shapes filled' flag for a series and sends a
  625        * {@link RendererChangeEvent} to all registered listeners.
  626        *
  627        * @param series  the series index (zero-based).
  628        * @param flag  the flag.
  629        *
  630        * @see #getSeriesShapesFilled(int)
  631        */
  632       public void setSeriesShapesFilled(int series, boolean flag) {
  633           setSeriesShapesFilled(series, BooleanUtilities.valueOf(flag));
  634       }
  635   
  636       /**
  637        * Sets the 'shapes filled' flag for a series and sends a
  638        * {@link RendererChangeEvent} to all registered listeners.
  639        *
  640        * @param series  the series index (zero-based).
  641        * @param flag  the flag.
  642        *
  643        * @see #getSeriesShapesFilled(int)
  644        */
  645       public void setSeriesShapesFilled(int series, Boolean flag) {
  646           this.seriesShapesFilled.setBoolean(series, flag);
  647           fireChangeEvent();
  648       }
  649   
  650       /**
  651        * Returns the base 'shape filled' attribute.
  652        *
  653        * @return The base flag.
  654        *
  655        * @see #setBaseShapesFilled(boolean)
  656        */
  657       public boolean getBaseShapesFilled() {
  658           return this.baseShapesFilled;
  659       }
  660   
  661       /**
  662        * Sets the base 'shapes filled' flag and sends a
  663        * {@link RendererChangeEvent} to all registered listeners.
  664        *
  665        * @param flag  the flag.
  666        *
  667        * @see #getBaseShapesFilled()
  668        */
  669       public void setBaseShapesFilled(boolean flag) {
  670           this.baseShapesFilled = flag;
  671           fireChangeEvent();
  672       }
  673   
  674       /**
  675        * Returns <code>true</code> if outlines should be drawn for shapes, and
  676        * <code>false</code> otherwise.
  677        *
  678        * @return A boolean.
  679        *
  680        * @see #setDrawOutlines(boolean)
  681        */
  682       public boolean getDrawOutlines() {
  683           return this.drawOutlines;
  684       }
  685   
  686       /**
  687        * Sets the flag that controls whether outlines are drawn for
  688        * shapes, and sends a {@link RendererChangeEvent} to all registered
  689        * listeners.
  690        * <P>
  691        * In some cases, shapes look better if they do NOT have an outline, but
  692        * this flag allows you to set your own preference.
  693        *
  694        * @param flag  the flag.
  695        *
  696        * @see #getDrawOutlines()
  697        */
  698       public void setDrawOutlines(boolean flag) {
  699           this.drawOutlines = flag;
  700           fireChangeEvent();
  701       }
  702   
  703       /**
  704        * Returns <code>true</code> if the renderer should use the fill paint
  705        * setting to fill shapes, and <code>false</code> if it should just
  706        * use the regular paint.
  707        * <p>
  708        * Refer to <code>XYLineAndShapeRendererDemo2.java</code> to see the
  709        * effect of this flag.
  710        *
  711        * @return A boolean.
  712        *
  713        * @see #setUseFillPaint(boolean)
  714        * @see #getUseOutlinePaint()
  715        */
  716       public boolean getUseFillPaint() {
  717           return this.useFillPaint;
  718       }
  719   
  720       /**
  721        * Sets the flag that controls whether the fill paint is used to fill
  722        * shapes, and sends a {@link RendererChangeEvent} to all
  723        * registered listeners.
  724        *
  725        * @param flag  the flag.
  726        *
  727        * @see #getUseFillPaint()
  728        */
  729       public void setUseFillPaint(boolean flag) {
  730           this.useFillPaint = flag;
  731           fireChangeEvent();
  732       }
  733   
  734       /**
  735        * Returns <code>true</code> if the renderer should use the outline paint
  736        * setting to draw shape outlines, and <code>false</code> if it should just
  737        * use the regular paint.
  738        *
  739        * @return A boolean.
  740        *
  741        * @see #setUseOutlinePaint(boolean)
  742        * @see #getUseFillPaint()
  743        */
  744       public boolean getUseOutlinePaint() {
  745           return this.useOutlinePaint;
  746       }
  747   
  748       /**
  749        * Sets the flag that controls whether the outline paint is used to draw
  750        * shape outlines, and sends a {@link RendererChangeEvent} to all
  751        * registered listeners.
  752        * <p>
  753        * Refer to <code>XYLineAndShapeRendererDemo2.java</code> to see the
  754        * effect of this flag.
  755        *
  756        * @param flag  the flag.
  757        *
  758        * @see #getUseOutlinePaint()
  759        */
  760       public void setUseOutlinePaint(boolean flag) {
  761           this.useOutlinePaint = flag;
  762           fireChangeEvent();
  763       }
  764   
  765       /**
  766        * Records the state for the renderer.  This is used to preserve state
  767        * information between calls to the drawItem() method for a single chart
  768        * drawing.
  769        */
  770       public static class State extends XYItemRendererState {
  771   
  772           /** The path for the current series. */
  773           public GeneralPath seriesPath;
  774   
  775           /**
  776            * A flag that indicates if the last (x, y) point was 'good'
  777            * (non-null).
  778            */
  779           private boolean lastPointGood;
  780   
  781           /**
  782            * Creates a new state instance.
  783            *
  784            * @param info  the plot rendering info.
  785            */
  786           public State(PlotRenderingInfo info) {
  787               super(info);
  788           }
  789   
  790           /**
  791            * Returns a flag that indicates if the last point drawn (in the
  792            * current series) was 'good' (non-null).
  793            *
  794            * @return A boolean.
  795            */
  796           public boolean isLastPointGood() {
  797               return this.lastPointGood;
  798           }
  799   
  800           /**
  801            * Sets a flag that indicates if the last point drawn (in the current
  802            * series) was 'good' (non-null).
  803            *
  804            * @param good  the flag.
  805            */
  806           public void setLastPointGood(boolean good) {
  807               this.lastPointGood = good;
  808           }
  809       }
  810   
  811       /**
  812        * Initialises the renderer.
  813        * <P>
  814        * This method will be called before the first item is rendered, giving the
  815        * renderer an opportunity to initialise any state information it wants to
  816        * maintain.  The renderer can do nothing if it chooses.
  817        *
  818        * @param g2  the graphics device.
  819        * @param dataArea  the area inside the axes.
  820        * @param plot  the plot.
  821        * @param data  the data.
  822        * @param info  an optional info collection object to return data back to
  823        *              the caller.
  824        *
  825        * @return The renderer state.
  826        */
  827       public XYItemRendererState initialise(Graphics2D g2,
  828                                             Rectangle2D dataArea,
  829                                             XYPlot plot,
  830                                             XYDataset data,
  831                                             PlotRenderingInfo info) {
  832   
  833           State state = new State(info);
  834           state.seriesPath = new GeneralPath();
  835           return state;
  836   
  837       }
  838   
  839       /**
  840        * Draws the visual representation of a single data item.
  841        *
  842        * @param g2  the graphics device.
  843        * @param state  the renderer state.
  844        * @param dataArea  the area within which the data is being drawn.
  845        * @param info  collects information about the drawing.
  846        * @param plot  the plot (can be used to obtain standard color
  847        *              information etc).
  848        * @param domainAxis  the domain axis.
  849        * @param rangeAxis  the range axis.
  850        * @param dataset  the dataset.
  851        * @param series  the series index (zero-based).
  852        * @param item  the item index (zero-based).
  853        * @param crosshairState  crosshair information for the plot
  854        *                        (<code>null</code> permitted).
  855        * @param pass  the pass index.
  856        */
  857       public void drawItem(Graphics2D g2,
  858                            XYItemRendererState state,
  859                            Rectangle2D dataArea,
  860                            PlotRenderingInfo info,
  861                            XYPlot plot,
  862                            ValueAxis domainAxis,
  863                            ValueAxis rangeAxis,
  864                            XYDataset dataset,
  865                            int series,
  866                            int item,
  867                            CrosshairState crosshairState,
  868                            int pass) {
  869   
  870           // do nothing if item is not visible
  871           if (!getItemVisible(series, item)) {
  872               return;
  873           }
  874   
  875           // first pass draws the background (lines, for instance)
  876           if (isLinePass(pass)) {
  877               if (item == 0) {
  878                   if (this.drawSeriesLineAsPath) {
  879                       State s = (State) state;
  880                       s.seriesPath.reset();
  881                       s.lastPointGood = false;
  882                   }
  883               }
  884   
  885               if (getItemLineVisible(series, item)) {
  886                   if (this.drawSeriesLineAsPath) {
  887                       drawPrimaryLineAsPath(state, g2, plot, dataset, pass,
  888                               series, item, domainAxis, rangeAxis, dataArea);
  889                   }
  890                   else {
  891                       drawPrimaryLine(state, g2, plot, dataset, pass, series,
  892                               item, domainAxis, rangeAxis, dataArea);
  893                   }
  894               }
  895           }
  896           // second pass adds shapes where the items are ..
  897           else if (isItemPass(pass)) {
  898   
  899               // setup for collecting optional entity info...
  900               EntityCollection entities = null;
  901               if (info != null) {
  902                   entities = info.getOwner().getEntityCollection();
  903               }
  904   
  905               drawSecondaryPass(g2, plot, dataset, pass, series, item,
  906                       domainAxis, dataArea, rangeAxis, crosshairState, entities);
  907           }
  908       }
  909   
  910       /**
  911        * Returns <code>true</code> if the specified pass is the one for drawing
  912        * lines.
  913        *
  914        * @param pass  the pass.
  915        *
  916        * @return A boolean.
  917        */
  918       protected boolean isLinePass(int pass) {
  919           return pass == 0;
  920       }
  921   
  922       /**
  923        * Returns <code>true</code> if the specified pass is the one for drawing
  924        * items.
  925        *
  926        * @param pass  the pass.
  927        *
  928        * @return A boolean.
  929        */
  930       protected boolean isItemPass(int pass) {
  931           return pass == 1;
  932       }
  933   
  934       /**
  935        * Draws the item (first pass). This method draws the lines
  936        * connecting the items.
  937        *
  938        * @param g2  the graphics device.
  939        * @param state  the renderer state.
  940        * @param dataArea  the area within which the data is being drawn.
  941        * @param plot  the plot (can be used to obtain standard color
  942        *              information etc).
  943        * @param domainAxis  the domain axis.
  944        * @param rangeAxis  the range axis.
  945        * @param dataset  the dataset.
  946        * @param pass  the pass.
  947        * @param series  the series index (zero-based).
  948        * @param item  the item index (zero-based).
  949        */
  950       protected void drawPrimaryLine(XYItemRendererState state,
  951                                      Graphics2D g2,
  952                                      XYPlot plot,
  953                                      XYDataset dataset,
  954                                      int pass,
  955                                      int series,
  956                                      int item,
  957                                      ValueAxis domainAxis,
  958                                      ValueAxis rangeAxis,
  959                                      Rectangle2D dataArea) {
  960           if (item == 0) {
  961               return;
  962           }
  963   
  964           // get the data point...
  965           double x1 = dataset.getXValue(series, item);
  966           double y1 = dataset.getYValue(series, item);
  967           if (Double.isNaN(y1) || Double.isNaN(x1)) {
  968               return;
  969           }
  970   
  971           double x0 = dataset.getXValue(series, item - 1);
  972           double y0 = dataset.getYValue(series, item - 1);
  973           if (Double.isNaN(y0) || Double.isNaN(x0)) {
  974               return;
  975           }
  976   
  977           RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
  978           RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
  979   
  980           double transX0 = domainAxis.valueToJava2D(x0, dataArea, xAxisLocation);
  981           double transY0 = rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation);
  982   
  983           double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
  984           double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
  985   
  986           // only draw if we have good values
  987           if (Double.isNaN(transX0) || Double.isNaN(transY0)
  988               || Double.isNaN(transX1) || Double.isNaN(transY1)) {
  989               return;
  990           }
  991   
  992           PlotOrientation orientation = plot.getOrientation();
  993           if (orientation == PlotOrientation.HORIZONTAL) {
  994               state.workingLine.setLine(transY0, transX0, transY1, transX1);
  995           }
  996           else if (orientation == PlotOrientation.VERTICAL) {
  997               state.workingLine.setLine(transX0, transY0, transX1, transY1);
  998           }
  999   
 1000           if (state.workingLine.intersects(dataArea)) {
 1001               drawFirstPassShape(g2, pass, series, item, state.workingLine);
 1002           }
 1003       }
 1004   
 1005       /**
 1006        * Draws the first pass shape.
 1007        *
 1008        * @param g2  the graphics device.
 1009        * @param pass  the pass.
 1010        * @param series  the series index.
 1011        * @param item  the item index.
 1012        * @param shape  the shape.
 1013        */
 1014       protected void drawFirstPassShape(Graphics2D g2, int pass, int series,
 1015                                         int item, Shape shape) {
 1016           g2.setStroke(getItemStroke(series, item));
 1017           g2.setPaint(getItemPaint(series, item));
 1018           g2.draw(shape);
 1019       }
 1020   
 1021   
 1022       /**
 1023        * Draws the item (first pass). This method draws the lines
 1024        * connecting the items. Instead of drawing separate lines,
 1025        * a GeneralPath is constructed and drawn at the end of
 1026        * the series painting.
 1027        *
 1028        * @param g2  the graphics device.
 1029        * @param state  the renderer state.
 1030        * @param plot  the plot (can be used to obtain standard color information
 1031        *              etc).
 1032        * @param dataset  the dataset.
 1033        * @param pass  the pass.
 1034        * @param series  the series index (zero-based).
 1035        * @param item  the item index (zero-based).
 1036        * @param domainAxis  the domain axis.
 1037        * @param rangeAxis  the range axis.
 1038        * @param dataArea  the area within which the data is being drawn.
 1039        */
 1040       protected void drawPrimaryLineAsPath(XYItemRendererState state,
 1041                                            Graphics2D g2, XYPlot plot,
 1042                                            XYDataset dataset,
 1043                                            int pass,
 1044                                            int series,
 1045                                            int item,
 1046                                            ValueAxis domainAxis,
 1047                                            ValueAxis rangeAxis,
 1048                                            Rectangle2D dataArea) {
 1049   
 1050   
 1051           RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
 1052           RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
 1053   
 1054           // get the data point...
 1055           double x1 = dataset.getXValue(series, item);
 1056           double y1 = dataset.getYValue(series, item);
 1057           double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
 1058           double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
 1059   
 1060           State s = (State) state;
 1061           // update path to reflect latest point
 1062           if (!Double.isNaN(transX1) && !Double.isNaN(transY1)) {
 1063               float x = (float) transX1;
 1064               float y = (float) transY1;
 1065               PlotOrientation orientation = plot.getOrientation();
 1066               if (orientation == PlotOrientation.HORIZONTAL) {
 1067                   x = (float) transY1;
 1068                   y = (float) transX1;
 1069               }
 1070               if (s.isLastPointGood()) {
 1071                   s.seriesPath.lineTo(x, y);
 1072               }
 1073               else {
 1074                   s.seriesPath.moveTo(x, y);
 1075               }
 1076               s.setLastPointGood(true);
 1077           }
 1078           else {
 1079               s.setLastPointGood(false);
 1080           }
 1081           // if this is the last item, draw the path ...
 1082           if (item == dataset.getItemCount(series) - 1) {
 1083               // draw path
 1084               drawFirstPassShape(g2, pass, series, item, s.seriesPath);
 1085           }
 1086       }
 1087   
 1088       /**
 1089        * Draws the item shapes and adds chart entities (second pass). This method
 1090        * draws the shapes which mark the item positions. If <code>entities</code>
 1091        * is not <code>null</code> it will be populated with entity information
 1092        * for points that fall within the data area.
 1093        *
 1094        * @param g2  the graphics device.
 1095        * @param plot  the plot (can be used to obtain standard color
 1096        *              information etc).
 1097        * @param domainAxis  the domain axis.
 1098        * @param dataArea  the area within which the data is being drawn.
 1099        * @param rangeAxis  the range axis.
 1100        * @param dataset  the dataset.
 1101        * @param pass  the pass.
 1102        * @param series  the series index (zero-based).
 1103        * @param item  the item index (zero-based).
 1104        * @param crosshairState  the crosshair state.
 1105        * @param entities the entity collection.
 1106        */
 1107       protected void drawSecondaryPass(Graphics2D g2, XYPlot plot,
 1108                                        XYDataset dataset,
 1109                                        int pass, int series, int item,
 1110                                        ValueAxis domainAxis,
 1111                                        Rectangle2D dataArea,
 1112                                        ValueAxis rangeAxis,
 1113                                        CrosshairState crosshairState,
 1114                                        EntityCollection entities) {
 1115   
 1116           Shape entityArea = null;
 1117   
 1118           // get the data point...
 1119           double x1 = dataset.getXValue(series, item);
 1120           double y1 = dataset.getYValue(series, item);
 1121           if (Double.isNaN(y1) || Double.isNaN(x1)) {
 1122               return;
 1123           }
 1124   
 1125           PlotOrientation orientation = plot.getOrientation();
 1126           RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
 1127           RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
 1128           double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
 1129           double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
 1130   
 1131           if (getItemShapeVisible(series, item)) {
 1132               Shape shape = getItemShape(series, item);
 1133               if (orientation == PlotOrientation.HORIZONTAL) {
 1134                   shape = ShapeUtilities.createTranslatedShape(shape, transY1,
 1135                           transX1);
 1136               }
 1137               else if (orientation == PlotOrientation.VERTICAL) {
 1138                   shape = ShapeUtilities.createTranslatedShape(shape, transX1,
 1139                           transY1);
 1140               }
 1141               entityArea = shape;
 1142               if (shape.intersects(dataArea)) {
 1143                   if (getItemShapeFilled(series, item)) {
 1144                       if (this.useFillPaint) {
 1145                           g2.setPaint(getItemFillPaint(series, item));
 1146                       }
 1147                       else {
 1148                           g2.setPaint(getItemPaint(series, item));
 1149                       }
 1150                       g2.fill(shape);
 1151                   }
 1152                   if (this.drawOutlines) {
 1153                       if (getUseOutlinePaint()) {
 1154                           g2.setPaint(getItemOutlinePaint(series, item));
 1155                       }
 1156                       else {
 1157                           g2.setPaint(getItemPaint(series, item));
 1158                       }
 1159                       g2.setStroke(getItemOutlineStroke(series, item));
 1160                       g2.draw(shape);
 1161                   }
 1162               }
 1163           }
 1164   
 1165           double xx = transX1;
 1166           double yy = transY1;
 1167           if (orientation == PlotOrientation.HORIZONTAL) {
 1168               xx = transY1;
 1169               yy = transX1;
 1170           }
 1171   
 1172           // draw the item label if there is one...
 1173           if (isItemLabelVisible(series, item)) {
 1174               drawItemLabel(g2, orientation, dataset, series, item, xx, yy,
 1175                       (y1 < 0.0));
 1176           }
 1177   
 1178           int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
 1179           int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
 1180           updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
 1181                   rangeAxisIndex, transX1, transY1, orientation);
 1182   
 1183           // add an entity for the item, but only if it falls within the data
 1184           // area...
 1185           if (entities != null && isPointInRect(dataArea, xx, yy)) {
 1186               addEntity(entities, entityArea, dataset, series, item, xx, yy);
 1187           }
 1188       }
 1189   
 1190   
 1191       /**
 1192        * Returns a legend item for the specified series.
 1193        *
 1194        * @param datasetIndex  the dataset index (zero-based).
 1195        * @param series  the series index (zero-based).
 1196        *
 1197        * @return A legend item for the series.
 1198        */
 1199       public LegendItem getLegendItem(int datasetIndex, int series) {
 1200   
 1201           XYPlot plot = getPlot();
 1202           if (plot == null) {
 1203               return null;
 1204           }
 1205   
 1206           LegendItem result = null;
 1207           XYDataset dataset = plot.getDataset(datasetIndex);
 1208           if (dataset != null) {
 1209               if (getItemVisible(series, 0)) {
 1210                   String label = getLegendItemLabelGenerator().generateLabel(
 1211                           dataset, series);
 1212                   String description = label;
 1213                   String toolTipText = null;
 1214                   if (getLegendItemToolTipGenerator() != null) {
 1215                       toolTipText = getLegendItemToolTipGenerator().generateLabel(
 1216                               dataset, series);
 1217                   }
 1218                   String urlText = null;
 1219                   if (getLegendItemURLGenerator() != null) {
 1220                       urlText = getLegendItemURLGenerator().generateLabel(
 1221                               dataset, series);
 1222                   }
 1223                   boolean shapeIsVisible = getItemShapeVisible(series, 0);
 1224                   Shape shape = lookupSeriesShape(series);
 1225                   boolean shapeIsFilled = getItemShapeFilled(series, 0);
 1226                   Paint fillPaint = (this.useFillPaint
 1227                       ? lookupSeriesFillPaint(series)
 1228                       : lookupSeriesPaint(series));
 1229                   boolean shapeOutlineVisible = this.drawOutlines;
 1230                   Paint outlinePaint = (this.useOutlinePaint
 1231                       ? lookupSeriesOutlinePaint(series)
 1232                       : lookupSeriesPaint(series));
 1233                   Stroke outlineStroke = lookupSeriesOutlineStroke(series);
 1234                   boolean lineVisible = getItemLineVisible(series, 0);
 1235                   Stroke lineStroke = lookupSeriesStroke(series);
 1236                   Paint linePaint = lookupSeriesPaint(series);
 1237                   result = new LegendItem(label, description, toolTipText,
 1238                           urlText, shapeIsVisible, shape, shapeIsFilled,
 1239                           fillPaint, shapeOutlineVisible, outlinePaint,
 1240                           outlineStroke, lineVisible, this.legendLine,
 1241                           lineStroke, linePaint);
 1242                   result.setSeriesKey(dataset.getSeriesKey(series));
 1243                   result.setSeriesIndex(series);
 1244                   result.setDataset(dataset);
 1245                   result.setDatasetIndex(datasetIndex);
 1246               }
 1247           }
 1248   
 1249           return result;
 1250   
 1251       }
 1252   
 1253       /**
 1254        * Returns a clone of the renderer.
 1255        *
 1256        * @return A clone.
 1257        *
 1258        * @throws CloneNotSupportedException if the clone cannot be created.
 1259        */
 1260       public Object clone() throws CloneNotSupportedException {
 1261           XYLineAndShapeRenderer clone = (XYLineAndShapeRenderer) super.clone();
 1262           clone.seriesLinesVisible
 1263                   = (BooleanList) this.seriesLinesVisible.clone();
 1264           if (this.legendLine != null) {
 1265               clone.legendLine = ShapeUtilities.clone(this.legendLine);
 1266           }
 1267           clone.seriesShapesVisible
 1268                   = (BooleanList) this.seriesShapesVisible.clone();
 1269           clone.seriesShapesFilled
 1270                   = (BooleanList) this.seriesShapesFilled.clone();
 1271           return clone;
 1272       }
 1273   
 1274       /**
 1275        * Tests this renderer for equality with an arbitrary object.
 1276        *
 1277        * @param obj  the object (<code>null</code> permitted).
 1278        *
 1279        * @return <code>true</code> or <code>false</code>.
 1280        */
 1281       public boolean equals(Object obj) {
 1282   
 1283           if (obj == this) {
 1284               return true;
 1285           }
 1286           if (!(obj instanceof XYLineAndShapeRenderer)) {
 1287               return false;
 1288           }
 1289           if (!super.equals(obj)) {
 1290               return false;
 1291           }
 1292           XYLineAndShapeRenderer that = (XYLineAndShapeRenderer) obj;
 1293           if (!ObjectUtilities.equal(this.linesVisible, that.linesVisible)) {
 1294               return false;
 1295           }
 1296           if (!ObjectUtilities.equal(
 1297               this.seriesLinesVisible, that.seriesLinesVisible)
 1298           ) {
 1299               return false;
 1300           }
 1301           if (this.baseLinesVisible != that.baseLinesVisible) {
 1302               return false;
 1303           }
 1304           if (!ShapeUtilities.equal(this.legendLine, that.legendLine)) {
 1305               return false;
 1306           }
 1307           if (!ObjectUtilities.equal(this.shapesVisible, that.shapesVisible)) {
 1308               return false;
 1309           }
 1310           if (!ObjectUtilities.equal(
 1311               this.seriesShapesVisible, that.seriesShapesVisible)
 1312           ) {
 1313               return false;
 1314           }
 1315           if (this.baseShapesVisible != that.baseShapesVisible) {
 1316               return false;
 1317           }
 1318           if (!ObjectUtilities.equal(this.shapesFilled, that.shapesFilled)) {
 1319               return false;
 1320           }
 1321           if (!ObjectUtilities.equal(
 1322               this.seriesShapesFilled, that.seriesShapesFilled)
 1323           ) {
 1324               return false;
 1325           }
 1326           if (this.baseShapesFilled != that.baseShapesFilled) {
 1327               return false;
 1328           }
 1329           if (this.drawOutlines != that.drawOutlines) {
 1330               return false;
 1331           }
 1332           if (this.useOutlinePaint != that.useOutlinePaint) {
 1333               return false;
 1334           }
 1335           if (this.useFillPaint != that.useFillPaint) {
 1336               return false;
 1337           }
 1338           if (this.drawSeriesLineAsPath != that.drawSeriesLineAsPath) {
 1339               return false;
 1340           }
 1341           return true;
 1342   
 1343       }
 1344   
 1345       /**
 1346        * Provides serialization support.
 1347        *
 1348        * @param stream  the input stream.
 1349        *
 1350        * @throws IOException  if there is an I/O error.
 1351        * @throws ClassNotFoundException  if there is a classpath problem.
 1352        */
 1353       private void readObject(ObjectInputStream stream)
 1354               throws IOException, ClassNotFoundException {
 1355           stream.defaultReadObject();
 1356           this.legendLine = SerialUtilities.readShape(stream);
 1357       }
 1358   
 1359       /**
 1360        * Provides serialization support.
 1361        *
 1362        * @param stream  the output stream.
 1363        *
 1364        * @throws IOException  if there is an I/O error.
 1365        */
 1366       private void writeObject(ObjectOutputStream stream) throws IOException {
 1367           stream.defaultWriteObject();
 1368           SerialUtilities.writeShape(this.legendLine, stream);
 1369       }
 1370   
 1371   }

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