Save This Page
Home » jcommon-1.0.13 » org.jfree » chart » renderer » category » [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    * BarRenderer.java
   29    * ----------------
   30    * (C) Copyright 2002-2008, by Object Refinery Limited.
   31    *
   32    * Original Author:  David Gilbert (for Object Refinery Limited);
   33    * Contributor(s):   Christian W. Zuckschwerdt;
   34    *
   35    * Changes
   36    * -------
   37    * 14-Mar-2002 : Version 1 (DG);
   38    * 23-May-2002 : Added tooltip generator to renderer (DG);
   39    * 29-May-2002 : Moved tooltip generator to abstract super-class (DG);
   40    * 25-Jun-2002 : Changed constructor to protected and removed redundant
   41    *               code (DG);
   42    * 26-Jun-2002 : Added axis to initialise method, and record upper and lower
   43    *               clip values (DG);
   44    * 24-Sep-2002 : Added getLegendItem() method (DG);
   45    * 09-Oct-2002 : Modified constructor to include URL generator (DG);
   46    * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
   47    * 10-Jan-2003 : Moved get/setItemMargin() method up from subclasses (DG);
   48    * 17-Jan-2003 : Moved plot classes into a separate package (DG);
   49    * 25-Mar-2003 : Implemented Serializable (DG);
   50    * 01-May-2003 : Modified clipping to allow for dual axes and datasets (DG);
   51    * 12-May-2003 : Merged horizontal and vertical bar renderers (DG);
   52    * 12-Jun-2003 : Updates for item labels (DG);
   53    * 30-Jul-2003 : Modified entity constructor (CZ);
   54    * 02-Sep-2003 : Changed initialise method to fix bug 790407 (DG);
   55    * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
   56    * 07-Oct-2003 : Added renderer state (DG);
   57    * 27-Oct-2003 : Merged drawHorizontalItem() and drawVerticalItem()
   58    *               methods (DG);
   59    * 28-Oct-2003 : Added support for gradient paint on bars (DG);
   60    * 14-Nov-2003 : Added 'maxBarWidth' attribute (DG);
   61    * 10-Feb-2004 : Small changes inside drawItem() method to ease cut-and-paste
   62    *               overriding (DG);
   63    * 19-Mar-2004 : Fixed bug introduced with separation of tool tip and item
   64    *               label generators.  Fixed equals() method (DG);
   65    * 11-May-2004 : Fix for null pointer exception (bug id 951127) (DG);
   66    * 05-Nov-2004 : Modified drawItem() signature (DG);
   67    * 26-Jan-2005 : Provided override for getLegendItem() method (DG);
   68    * 20-Apr-2005 : Generate legend labels, tooltips and URLs (DG);
   69    * 18-May-2005 : Added configurable base value (DG);
   70    * 09-Jun-2005 : Use addItemEntity() method from superclass (DG);
   71    * 01-Dec-2005 : Update legend item to use/not use outline (DG);
   72    * ------------: JFreeChart 1.0.x ---------------------------------------------
   73    * 06-Dec-2005 : Fixed bug 1374222 (JDK 1.4 specific code) (DG);
   74    * 11-Jan-2006 : Fixed bug 1401856 (bad rendering for non-zero base) (DG);
   75    * 04-Aug-2006 : Fixed bug 1467706 (missing item labels for zero value
   76    *               bars) (DG);
   77    * 04-Dec-2006 : Fixed bug in rendering to non-primary axis (DG);
   78    * 13-Dec-2006 : Add support for GradientPaint display in legend items (DG);
   79    * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
   80    * 11-May-2007 : Check for visibility in getLegendItem() (DG);
   81    * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
   82    * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
   83    * 07-May-2008 : If minimumBarLength is > 0.0, extend the non-base end of the
   84    *               bar (DG);
   85    *
   86    */
   87   
   88   package org.jfree.chart.renderer.category;
   89   
   90   import java.awt.BasicStroke;
   91   import java.awt.Color;
   92   import java.awt.Font;
   93   import java.awt.GradientPaint;
   94   import java.awt.Graphics2D;
   95   import java.awt.Paint;
   96   import java.awt.Shape;
   97   import java.awt.Stroke;
   98   import java.awt.geom.Line2D;
   99   import java.awt.geom.Point2D;
  100   import java.awt.geom.Rectangle2D;
  101   import java.io.Serializable;
  102   
  103   import org.jfree.chart.LegendItem;
  104   import org.jfree.chart.axis.CategoryAxis;
  105   import org.jfree.chart.axis.ValueAxis;
  106   import org.jfree.chart.entity.EntityCollection;
  107   import org.jfree.chart.event.RendererChangeEvent;
  108   import org.jfree.chart.labels.CategoryItemLabelGenerator;
  109   import org.jfree.chart.labels.ItemLabelAnchor;
  110   import org.jfree.chart.labels.ItemLabelPosition;
  111   import org.jfree.chart.plot.CategoryPlot;
  112   import org.jfree.chart.plot.PlotOrientation;
  113   import org.jfree.chart.plot.PlotRenderingInfo;
  114   import org.jfree.data.Range;
  115   import org.jfree.data.category.CategoryDataset;
  116   import org.jfree.data.general.DatasetUtilities;
  117   import org.jfree.text.TextUtilities;
  118   import org.jfree.ui.GradientPaintTransformer;
  119   import org.jfree.ui.RectangleEdge;
  120   import org.jfree.ui.StandardGradientPaintTransformer;
  121   import org.jfree.util.ObjectUtilities;
  122   import org.jfree.util.PublicCloneable;
  123   
  124   /**
  125    * A {@link CategoryItemRenderer} that draws individual data items as bars.
  126    */
  127   public class BarRenderer extends AbstractCategoryItemRenderer
  128                            implements Cloneable, PublicCloneable, Serializable {
  129   
  130       /** For serialization. */
  131       private static final long serialVersionUID = 6000649414965887481L;
  132   
  133       /** The default item margin percentage. */
  134       public static final double DEFAULT_ITEM_MARGIN = 0.20;
  135   
  136       /**
  137        * Constant that controls the minimum width before a bar has an outline
  138        * drawn.
  139        */
  140       public static final double BAR_OUTLINE_WIDTH_THRESHOLD = 3.0;
  141   
  142       /** The margin between items (bars) within a category. */
  143       private double itemMargin;
  144   
  145       /** A flag that controls whether or not bar outlines are drawn. */
  146       private boolean drawBarOutline;
  147   
  148       /** The maximum bar width as a percentage of the available space. */
  149       private double maximumBarWidth;
  150   
  151       /** The minimum bar length (in Java2D units). */
  152       private double minimumBarLength;
  153   
  154       /**
  155        * An optional class used to transform gradient paint objects to fit each
  156        * bar.
  157        */
  158       private GradientPaintTransformer gradientPaintTransformer;
  159   
  160       /**
  161        * The fallback position if a positive item label doesn't fit inside the
  162        * bar.
  163        */
  164       private ItemLabelPosition positiveItemLabelPositionFallback;
  165   
  166       /**
  167        * The fallback position if a negative item label doesn't fit inside the
  168        * bar.
  169        */
  170       private ItemLabelPosition negativeItemLabelPositionFallback;
  171   
  172       /** The upper clip (axis) value for the axis. */
  173       private double upperClip;
  174       // TODO:  this needs to move into the renderer state
  175   
  176       /** The lower clip (axis) value for the axis. */
  177       private double lowerClip;
  178       // TODO:  this needs to move into the renderer state
  179   
  180       /** The base value for the bars (defaults to 0.0). */
  181       private double base;
  182   
  183       /**
  184        * A flag that controls whether the base value is included in the range
  185        * returned by the findRangeBounds() method.
  186        */
  187       private boolean includeBaseInRange;
  188   
  189       /**
  190        * Creates a new bar renderer with default settings.
  191        */
  192       public BarRenderer() {
  193           super();
  194           this.base = 0.0;
  195           this.includeBaseInRange = true;
  196           this.itemMargin = DEFAULT_ITEM_MARGIN;
  197           this.drawBarOutline = false;
  198           this.maximumBarWidth = 1.0;
  199               // 100 percent, so it will not apply unless changed
  200           this.positiveItemLabelPositionFallback = null;
  201           this.negativeItemLabelPositionFallback = null;
  202           this.gradientPaintTransformer = new StandardGradientPaintTransformer();
  203           this.minimumBarLength = 0.0;
  204       }
  205   
  206       /**
  207        * Returns the base value for the bars.  The default value is
  208        * <code>0.0</code>.
  209        *
  210        * @return The base value for the bars.
  211        *
  212        * @see #setBase(double)
  213        */
  214       public double getBase() {
  215           return this.base;
  216       }
  217   
  218       /**
  219        * Sets the base value for the bars and sends a {@link RendererChangeEvent}
  220        * to all registered listeners.
  221        *
  222        * @param base  the new base value.
  223        *
  224        * @see #getBase()
  225        */
  226       public void setBase(double base) {
  227           this.base = base;
  228           fireChangeEvent();
  229       }
  230   
  231       /**
  232        * Returns the item margin as a percentage of the available space for all
  233        * bars.
  234        *
  235        * @return The margin percentage (where 0.10 is ten percent).
  236        *
  237        * @see #setItemMargin(double)
  238        */
  239       public double getItemMargin() {
  240           return this.itemMargin;
  241       }
  242   
  243       /**
  244        * Sets the item margin and sends a {@link RendererChangeEvent} to all
  245        * registered listeners.  The value is expressed as a percentage of the
  246        * available width for plotting all the bars, with the resulting amount to
  247        * be distributed between all the bars evenly.
  248        *
  249        * @param percent  the margin (where 0.10 is ten percent).
  250        *
  251        * @see #getItemMargin()
  252        */
  253       public void setItemMargin(double percent) {
  254           this.itemMargin = percent;
  255           fireChangeEvent();
  256       }
  257   
  258       /**
  259        * Returns a flag that controls whether or not bar outlines are drawn.
  260        *
  261        * @return A boolean.
  262        *
  263        * @see #setDrawBarOutline(boolean)
  264        */
  265       public boolean isDrawBarOutline() {
  266           return this.drawBarOutline;
  267       }
  268   
  269       /**
  270        * Sets the flag that controls whether or not bar outlines are drawn and
  271        * sends a {@link RendererChangeEvent} to all registered listeners.
  272        *
  273        * @param draw  the flag.
  274        *
  275        * @see #isDrawBarOutline()
  276        */
  277       public void setDrawBarOutline(boolean draw) {
  278           this.drawBarOutline = draw;
  279           fireChangeEvent();
  280       }
  281   
  282       /**
  283        * Returns the maximum bar width, as a percentage of the available drawing
  284        * space.
  285        *
  286        * @return The maximum bar width.
  287        *
  288        * @see #setMaximumBarWidth(double)
  289        */
  290       public double getMaximumBarWidth() {
  291           return this.maximumBarWidth;
  292       }
  293   
  294       /**
  295        * Sets the maximum bar width, which is specified as a percentage of the
  296        * available space for all bars, and sends a {@link RendererChangeEvent} to
  297        * all registered listeners.
  298        *
  299        * @param percent  the percent (where 0.05 is five percent).
  300        *
  301        * @see #getMaximumBarWidth()
  302        */
  303       public void setMaximumBarWidth(double percent) {
  304           this.maximumBarWidth = percent;
  305           fireChangeEvent();
  306       }
  307   
  308       /**
  309        * Returns the minimum bar length (in Java2D units).  The default value is
  310        * 0.0.
  311        *
  312        * @return The minimum bar length.
  313        *
  314        * @see #setMinimumBarLength(double)
  315        */
  316       public double getMinimumBarLength() {
  317           return this.minimumBarLength;
  318       }
  319   
  320       /**
  321        * Sets the minimum bar length and sends a {@link RendererChangeEvent} to
  322        * all registered listeners.  The minimum bar length is specified in Java2D
  323        * units, and can be used to prevent bars that represent very small data
  324        * values from disappearing when drawn on the screen.  Typically you would
  325        * set this to (say) 0.5 or 1.0 Java 2D units.  Use this attribute with
  326        * caution, however, because setting it to a non-zero value will
  327        * artificially increase the length of bars representing small values,
  328        * which may misrepresent your data.
  329        *
  330        * @param min  the minimum bar length (in Java2D units, must be >= 0.0).
  331        *
  332        * @see #getMinimumBarLength()
  333        */
  334       public void setMinimumBarLength(double min) {
  335           if (min < 0.0) {
  336               throw new IllegalArgumentException("Requires 'min' >= 0.0");
  337           }
  338           this.minimumBarLength = min;
  339           fireChangeEvent();
  340       }
  341   
  342       /**
  343        * Returns the gradient paint transformer (an object used to transform
  344        * gradient paint objects to fit each bar).
  345        *
  346        * @return A transformer (<code>null</code> possible).
  347        *
  348        * @see #setGradientPaintTransformer(GradientPaintTransformer)
  349        */
  350       public GradientPaintTransformer getGradientPaintTransformer() {
  351           return this.gradientPaintTransformer;
  352       }
  353   
  354       /**
  355        * Sets the gradient paint transformer and sends a
  356        * {@link RendererChangeEvent} to all registered listeners.
  357        *
  358        * @param transformer  the transformer (<code>null</code> permitted).
  359        *
  360        * @see #getGradientPaintTransformer()
  361        */
  362       public void setGradientPaintTransformer(
  363               GradientPaintTransformer transformer) {
  364           this.gradientPaintTransformer = transformer;
  365           fireChangeEvent();
  366       }
  367   
  368       /**
  369        * Returns the fallback position for positive item labels that don't fit
  370        * within a bar.
  371        *
  372        * @return The fallback position (<code>null</code> possible).
  373        *
  374        * @see #setPositiveItemLabelPositionFallback(ItemLabelPosition)
  375        */
  376       public ItemLabelPosition getPositiveItemLabelPositionFallback() {
  377           return this.positiveItemLabelPositionFallback;
  378       }
  379   
  380       /**
  381        * Sets the fallback position for positive item labels that don't fit
  382        * within a bar, and sends a {@link RendererChangeEvent} to all registered
  383        * listeners.
  384        *
  385        * @param position  the position (<code>null</code> permitted).
  386        *
  387        * @see #getPositiveItemLabelPositionFallback()
  388        */
  389       public void setPositiveItemLabelPositionFallback(
  390               ItemLabelPosition position) {
  391           this.positiveItemLabelPositionFallback = position;
  392           fireChangeEvent();
  393       }
  394   
  395       /**
  396        * Returns the fallback position for negative item labels that don't fit
  397        * within a bar.
  398        *
  399        * @return The fallback position (<code>null</code> possible).
  400        *
  401        * @see #setPositiveItemLabelPositionFallback(ItemLabelPosition)
  402        */
  403       public ItemLabelPosition getNegativeItemLabelPositionFallback() {
  404           return this.negativeItemLabelPositionFallback;
  405       }
  406   
  407       /**
  408        * Sets the fallback position for negative item labels that don't fit
  409        * within a bar, and sends a {@link RendererChangeEvent} to all registered
  410        * listeners.
  411        *
  412        * @param position  the position (<code>null</code> permitted).
  413        *
  414        * @see #getNegativeItemLabelPositionFallback()
  415        */
  416       public void setNegativeItemLabelPositionFallback(
  417               ItemLabelPosition position) {
  418           this.negativeItemLabelPositionFallback = position;
  419           fireChangeEvent();
  420       }
  421   
  422       /**
  423        * Returns the flag that controls whether or not the base value for the
  424        * bars is included in the range calculated by
  425        * {@link #findRangeBounds(CategoryDataset)}.
  426        *
  427        * @return <code>true</code> if the base is included in the range, and
  428        *         <code>false</code> otherwise.
  429        *
  430        * @since 1.0.1
  431        *
  432        * @see #setIncludeBaseInRange(boolean)
  433        */
  434       public boolean getIncludeBaseInRange() {
  435           return this.includeBaseInRange;
  436       }
  437   
  438       /**
  439        * Sets the flag that controls whether or not the base value for the bars
  440        * is included in the range calculated by
  441        * {@link #findRangeBounds(CategoryDataset)}.  If the flag is changed,
  442        * a {@link RendererChangeEvent} is sent to all registered listeners.
  443        *
  444        * @param include  the new value for the flag.
  445        *
  446        * @since 1.0.1
  447        *
  448        * @see #getIncludeBaseInRange()
  449        */
  450       public void setIncludeBaseInRange(boolean include) {
  451           if (this.includeBaseInRange != include) {
  452               this.includeBaseInRange = include;
  453               fireChangeEvent();
  454           }
  455       }
  456   
  457       /**
  458        * Returns the lower clip value.  This value is recalculated in the
  459        * initialise() method.
  460        *
  461        * @return The value.
  462        */
  463       public double getLowerClip() {
  464           // TODO:  this attribute should be transferred to the renderer state.
  465           return this.lowerClip;
  466       }
  467   
  468       /**
  469        * Returns the upper clip value.  This value is recalculated in the
  470        * initialise() method.
  471        *
  472        * @return The value.
  473        */
  474       public double getUpperClip() {
  475           // TODO:  this attribute should be transferred to the renderer state.
  476           return this.upperClip;
  477       }
  478   
  479       /**
  480        * Initialises the renderer and returns a state object that will be passed
  481        * to subsequent calls to the drawItem method.  This method gets called
  482        * once at the start of the process of drawing a chart.
  483        *
  484        * @param g2  the graphics device.
  485        * @param dataArea  the area in which the data is to be plotted.
  486        * @param plot  the plot.
  487        * @param rendererIndex  the renderer index.
  488        * @param info  collects chart rendering information for return to caller.
  489        *
  490        * @return The renderer state.
  491        */
  492       public CategoryItemRendererState initialise(Graphics2D g2,
  493                                                   Rectangle2D dataArea,
  494                                                   CategoryPlot plot,
  495                                                   int rendererIndex,
  496                                                   PlotRenderingInfo info) {
  497   
  498           CategoryItemRendererState state = super.initialise(g2, dataArea, plot,
  499                   rendererIndex, info);
  500   
  501           // get the clipping values...
  502           ValueAxis rangeAxis = plot.getRangeAxisForDataset(rendererIndex);
  503           this.lowerClip = rangeAxis.getRange().getLowerBound();
  504           this.upperClip = rangeAxis.getRange().getUpperBound();
  505   
  506           // calculate the bar width
  507           calculateBarWidth(plot, dataArea, rendererIndex, state);
  508   
  509           return state;
  510   
  511       }
  512   
  513       /**
  514        * Calculates the bar width and stores it in the renderer state.
  515        *
  516        * @param plot  the plot.
  517        * @param dataArea  the data area.
  518        * @param rendererIndex  the renderer index.
  519        * @param state  the renderer state.
  520        */
  521       protected void calculateBarWidth(CategoryPlot plot,
  522                                        Rectangle2D dataArea,
  523                                        int rendererIndex,
  524                                        CategoryItemRendererState state) {
  525   
  526           CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
  527           CategoryDataset dataset = plot.getDataset(rendererIndex);
  528           if (dataset != null) {
  529               int columns = dataset.getColumnCount();
  530               int rows = dataset.getRowCount();
  531               double space = 0.0;
  532               PlotOrientation orientation = plot.getOrientation();
  533               if (orientation == PlotOrientation.HORIZONTAL) {
  534                   space = dataArea.getHeight();
  535               }
  536               else if (orientation == PlotOrientation.VERTICAL) {
  537                   space = dataArea.getWidth();
  538               }
  539               double maxWidth = space * getMaximumBarWidth();
  540               double categoryMargin = 0.0;
  541               double currentItemMargin = 0.0;
  542               if (columns > 1) {
  543                   categoryMargin = domainAxis.getCategoryMargin();
  544               }
  545               if (rows > 1) {
  546                   currentItemMargin = getItemMargin();
  547               }
  548               double used = space * (1 - domainAxis.getLowerMargin()
  549                                        - domainAxis.getUpperMargin()
  550                                        - categoryMargin - currentItemMargin);
  551               if ((rows * columns) > 0) {
  552                   state.setBarWidth(Math.min(used / (rows * columns), maxWidth));
  553               }
  554               else {
  555                   state.setBarWidth(Math.min(used, maxWidth));
  556               }
  557           }
  558       }
  559   
  560       /**
  561        * Calculates the coordinate of the first "side" of a bar.  This will be
  562        * the minimum x-coordinate for a vertical bar, and the minimum
  563        * y-coordinate for a horizontal bar.
  564        *
  565        * @param plot  the plot.
  566        * @param orientation  the plot orientation.
  567        * @param dataArea  the data area.
  568        * @param domainAxis  the domain axis.
  569        * @param state  the renderer state (has the bar width precalculated).
  570        * @param row  the row index.
  571        * @param column  the column index.
  572        *
  573        * @return The coordinate.
  574        */
  575       protected double calculateBarW0(CategoryPlot plot,
  576                                       PlotOrientation orientation,
  577                                       Rectangle2D dataArea,
  578                                       CategoryAxis domainAxis,
  579                                       CategoryItemRendererState state,
  580                                       int row,
  581                                       int column) {
  582           // calculate bar width...
  583           double space = 0.0;
  584           if (orientation == PlotOrientation.HORIZONTAL) {
  585               space = dataArea.getHeight();
  586           }
  587           else {
  588               space = dataArea.getWidth();
  589           }
  590           double barW0 = domainAxis.getCategoryStart(column, getColumnCount(),
  591                   dataArea, plot.getDomainAxisEdge());
  592           int seriesCount = getRowCount();
  593           int categoryCount = getColumnCount();
  594           if (seriesCount > 1) {
  595               double seriesGap = space * getItemMargin()
  596                                  / (categoryCount * (seriesCount - 1));
  597               double seriesW = calculateSeriesWidth(space, domainAxis,
  598                       categoryCount, seriesCount);
  599               barW0 = barW0 + row * (seriesW + seriesGap)
  600                             + (seriesW / 2.0) - (state.getBarWidth() / 2.0);
  601           }
  602           else {
  603               barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(),
  604                       dataArea, plot.getDomainAxisEdge()) - state.getBarWidth()
  605                       / 2.0;
  606           }
  607           return barW0;
  608       }
  609   
  610       /**
  611        * Calculates the coordinates for the length of a single bar.
  612        *
  613        * @param value  the value represented by the bar.
  614        *
  615        * @return The coordinates for each end of the bar (or <code>null</code> if
  616        *         the bar is not visible for the current axis range).
  617        */
  618       protected double[] calculateBarL0L1(double value) {
  619           double lclip = getLowerClip();
  620           double uclip = getUpperClip();
  621           double barLow = Math.min(this.base, value);
  622           double barHigh = Math.max(this.base, value);
  623           if (barHigh < lclip) {  // bar is not visible
  624               return null;
  625           }
  626           if (barLow > uclip) {   // bar is not visible
  627               return null;
  628           }
  629           barLow = Math.max(barLow, lclip);
  630           barHigh = Math.min(barHigh, uclip);
  631           return new double[] {barLow, barHigh};
  632       }
  633   
  634       /**
  635        * Returns the range of values the renderer requires to display all the
  636        * items from the specified dataset.  This takes into account the range
  637        * of values in the dataset, plus the flag that determines whether or not
  638        * the base value for the bars should be included in the range.
  639        *
  640        * @param dataset  the dataset (<code>null</code> permitted).
  641        *
  642        * @return The range (or <code>null</code> if the dataset is
  643        *         <code>null</code> or empty).
  644        */
  645       public Range findRangeBounds(CategoryDataset dataset) {
  646           Range result = DatasetUtilities.findRangeBounds(dataset);
  647           if (result != null) {
  648               if (this.includeBaseInRange) {
  649                   result = Range.expandToInclude(result, this.base);
  650               }
  651           }
  652           return result;
  653       }
  654   
  655       /**
  656        * Returns a legend item for a series.
  657        *
  658        * @param datasetIndex  the dataset index (zero-based).
  659        * @param series  the series index (zero-based).
  660        *
  661        * @return The legend item (possibly <code>null</code>).
  662        */
  663       public LegendItem getLegendItem(int datasetIndex, int series) {
  664   
  665           CategoryPlot cp = getPlot();
  666           if (cp == null) {
  667               return null;
  668           }
  669   
  670           // check that a legend item needs to be displayed...
  671           if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) {
  672               return null;
  673           }
  674   
  675           CategoryDataset dataset = cp.getDataset(datasetIndex);
  676           String label = getLegendItemLabelGenerator().generateLabel(dataset,
  677                   series);
  678           String description = label;
  679           String toolTipText = null;
  680           if (getLegendItemToolTipGenerator() != null) {
  681               toolTipText = getLegendItemToolTipGenerator().generateLabel(
  682                       dataset, series);
  683           }
  684           String urlText = null;
  685           if (getLegendItemURLGenerator() != null) {
  686               urlText = getLegendItemURLGenerator().generateLabel(dataset,
  687                       series);
  688           }
  689           Shape shape = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0);
  690           Paint paint = lookupSeriesPaint(series);
  691           Paint outlinePaint = lookupSeriesOutlinePaint(series);
  692           Stroke outlineStroke = lookupSeriesOutlineStroke(series);
  693   
  694           LegendItem result = new LegendItem(label, description, toolTipText,
  695                   urlText, true, shape, true, paint, isDrawBarOutline(),
  696                   outlinePaint, outlineStroke, false, new Line2D.Float(),
  697                   new BasicStroke(1.0f), Color.black);
  698           result.setDataset(dataset);
  699           result.setDatasetIndex(datasetIndex);
  700           result.setSeriesKey(dataset.getRowKey(series));
  701           result.setSeriesIndex(series);
  702           if (this.gradientPaintTransformer != null) {
  703               result.setFillPaintTransformer(this.gradientPaintTransformer);
  704           }
  705           return result;
  706       }
  707   
  708       /**
  709        * Draws the bar for a single (series, category) data item.
  710        *
  711        * @param g2  the graphics device.
  712        * @param state  the renderer state.
  713        * @param dataArea  the data area.
  714        * @param plot  the plot.
  715        * @param domainAxis  the domain axis.
  716        * @param rangeAxis  the range axis.
  717        * @param dataset  the dataset.
  718        * @param row  the row index (zero-based).
  719        * @param column  the column index (zero-based).
  720        * @param pass  the pass index.
  721        */
  722       public void drawItem(Graphics2D g2,
  723                            CategoryItemRendererState state,
  724                            Rectangle2D dataArea,
  725                            CategoryPlot plot,
  726                            CategoryAxis domainAxis,
  727                            ValueAxis rangeAxis,
  728                            CategoryDataset dataset,
  729                            int row,
  730                            int column,
  731                            int pass) {
  732   
  733           // nothing is drawn for null values...
  734           Number dataValue = dataset.getValue(row, column);
  735           if (dataValue == null) {
  736               return;
  737           }
  738   
  739           double value = dataValue.doubleValue();
  740           PlotOrientation orientation = plot.getOrientation();
  741           double barW0 = calculateBarW0(plot, orientation, dataArea, domainAxis,
  742                   state, row, column);
  743           double[] barL0L1 = calculateBarL0L1(value);
  744           if (barL0L1 == null) {
  745               return;  // the bar is not visible
  746           }
  747   
  748           RectangleEdge edge = plot.getRangeAxisEdge();
  749           double transL0 = rangeAxis.valueToJava2D(barL0L1[0], dataArea, edge);
  750           double transL1 = rangeAxis.valueToJava2D(barL0L1[1], dataArea, edge);
  751   
  752           // in the following code, barL0 is (in Java2D coordinates) the LEFT
  753           // end of the bar for a horizontal bar chart, and the TOP end of the
  754           // bar for a vertical bar chart.  Whether this is the BASE of the bar
  755           // or not depends also on (a) whether the data value is 'negative'
  756           // relative to the base value and (b) whether or not the range axis is
  757           // inverted.  This only matters if/when we apply the minimumBarLength
  758           // attribute, because we should extend the non-base end of the bar
  759           boolean positive = (value >= this.base);
  760           boolean inverted = rangeAxis.isInverted();
  761           double barL0 = Math.min(transL0, transL1);
  762           double barLength = Math.abs(transL1 - transL0);
  763           double barLengthAdj = 0.0;
  764           if (barLength > 0.0 && barLength < getMinimumBarLength()) {
  765               barLengthAdj = getMinimumBarLength() - barLength;
  766           }
  767           double barL0Adj = 0.0;
  768           if (orientation == PlotOrientation.HORIZONTAL) {
  769               if (positive && inverted || !positive && !inverted) {
  770                   barL0Adj = barLengthAdj;
  771               }
  772           }
  773           else {
  774               if (positive && !inverted || !positive && inverted) {
  775                   barL0Adj = barLengthAdj;
  776               }
  777           }
  778   
  779           // draw the bar...
  780           Rectangle2D bar = null;
  781           if (orientation == PlotOrientation.HORIZONTAL) {
  782               bar = new Rectangle2D.Double(barL0 - barL0Adj, barW0,
  783                       barLength + barLengthAdj, state.getBarWidth());
  784           }
  785           else {
  786               bar = new Rectangle2D.Double(barW0, barL0 - barL0Adj,
  787                       state.getBarWidth(), barLength + barLengthAdj);
  788           }
  789           Paint itemPaint = getItemPaint(row, column);
  790           GradientPaintTransformer t = getGradientPaintTransformer();
  791           if (t != null && itemPaint instanceof GradientPaint) {
  792               itemPaint = t.transform((GradientPaint) itemPaint, bar);
  793           }
  794           g2.setPaint(itemPaint);
  795           g2.fill(bar);
  796   
  797           // draw the outline...
  798           if (isDrawBarOutline()
  799                   && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
  800               Stroke stroke = getItemOutlineStroke(row, column);
  801               Paint paint = getItemOutlinePaint(row, column);
  802               if (stroke != null && paint != null) {
  803                   g2.setStroke(stroke);
  804                   g2.setPaint(paint);
  805                   g2.draw(bar);
  806               }
  807           }
  808   
  809           CategoryItemLabelGenerator generator
  810               = getItemLabelGenerator(row, column);
  811           if (generator != null && isItemLabelVisible(row, column)) {
  812               drawItemLabel(g2, dataset, row, column, plot, generator, bar,
  813                       (value < 0.0));
  814           }
  815   
  816           // add an item entity, if this information is being collected
  817           EntityCollection entities = state.getEntityCollection();
  818           if (entities != null) {
  819               addItemEntity(entities, dataset, row, column, bar);
  820           }
  821   
  822       }
  823   
  824       /**
  825        * Calculates the available space for each series.
  826        *
  827        * @param space  the space along the entire axis (in Java2D units).
  828        * @param axis  the category axis.
  829        * @param categories  the number of categories.
  830        * @param series  the number of series.
  831        *
  832        * @return The width of one series.
  833        */
  834       protected double calculateSeriesWidth(double space, CategoryAxis axis,
  835                                             int categories, int series) {
  836           double factor = 1.0 - getItemMargin() - axis.getLowerMargin()
  837                               - axis.getUpperMargin();
  838           if (categories > 1) {
  839               factor = factor - axis.getCategoryMargin();
  840           }
  841           return (space * factor) / (categories * series);
  842       }
  843   
  844       /**
  845        * Draws an item label.  This method is overridden so that the bar can be
  846        * used to calculate the label anchor point.
  847        *
  848        * @param g2  the graphics device.
  849        * @param data  the dataset.
  850        * @param row  the row.
  851        * @param column  the column.
  852        * @param plot  the plot.
  853        * @param generator  the label generator.
  854        * @param bar  the bar.
  855        * @param negative  a flag indicating a negative value.
  856        */
  857       protected void drawItemLabel(Graphics2D g2,
  858                                    CategoryDataset data,
  859                                    int row,
  860                                    int column,
  861                                    CategoryPlot plot,
  862                                    CategoryItemLabelGenerator generator,
  863                                    Rectangle2D bar,
  864                                    boolean negative) {
  865   
  866           String label = generator.generateLabel(data, row, column);
  867           if (label == null) {
  868               return;  // nothing to do
  869           }
  870   
  871           Font labelFont = getItemLabelFont(row, column);
  872           g2.setFont(labelFont);
  873           Paint paint = getItemLabelPaint(row, column);
  874           g2.setPaint(paint);
  875   
  876           // find out where to place the label...
  877           ItemLabelPosition position = null;
  878           if (!negative) {
  879               position = getPositiveItemLabelPosition(row, column);
  880           }
  881           else {
  882               position = getNegativeItemLabelPosition(row, column);
  883           }
  884   
  885           // work out the label anchor point...
  886           Point2D anchorPoint = calculateLabelAnchorPoint(
  887                   position.getItemLabelAnchor(), bar, plot.getOrientation());
  888   
  889           if (isInternalAnchor(position.getItemLabelAnchor())) {
  890               Shape bounds = TextUtilities.calculateRotatedStringBounds(label,
  891                       g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(),
  892                       position.getTextAnchor(), position.getAngle(),
  893                       position.getRotationAnchor());
  894   
  895               if (bounds != null) {
  896                   if (!bar.contains(bounds.getBounds2D())) {
  897                       if (!negative) {
  898                           position = getPositiveItemLabelPositionFallback();
  899                       }
  900                       else {
  901                           position = getNegativeItemLabelPositionFallback();
  902                       }
  903                       if (position != null) {
  904                           anchorPoint = calculateLabelAnchorPoint(
  905                                   position.getItemLabelAnchor(), bar,
  906                                   plot.getOrientation());
  907                       }
  908                   }
  909               }
  910   
  911           }
  912   
  913           if (position != null) {
  914               TextUtilities.drawRotatedString(label, g2,
  915                       (float) anchorPoint.getX(), (float) anchorPoint.getY(),
  916                       position.getTextAnchor(), position.getAngle(),
  917                       position.getRotationAnchor());
  918           }
  919       }
  920   
  921       /**
  922        * Calculates the item label anchor point.
  923        *
  924        * @param anchor  the anchor.
  925        * @param bar  the bar.
  926        * @param orientation  the plot orientation.
  927        *
  928        * @return The anchor point.
  929        */
  930       private Point2D calculateLabelAnchorPoint(ItemLabelAnchor anchor,
  931                                                 Rectangle2D bar,
  932                                                 PlotOrientation orientation) {
  933   
  934           Point2D result = null;
  935           double offset = getItemLabelAnchorOffset();
  936           double x0 = bar.getX() - offset;
  937           double x1 = bar.getX();
  938           double x2 = bar.getX() + offset;
  939           double x3 = bar.getCenterX();
  940           double x4 = bar.getMaxX() - offset;
  941           double x5 = bar.getMaxX();
  942           double x6 = bar.getMaxX() + offset;
  943   
  944           double y0 = bar.getMaxY() + offset;
  945           double y1 = bar.getMaxY();
  946           double y2 = bar.getMaxY() - offset;
  947           double y3 = bar.getCenterY();
  948           double y4 = bar.getMinY() + offset;
  949           double y5 = bar.getMinY();
  950           double y6 = bar.getMinY() - offset;
  951   
  952           if (anchor == ItemLabelAnchor.CENTER) {
  953               result = new Point2D.Double(x3, y3);
  954           }
  955           else if (anchor == ItemLabelAnchor.INSIDE1) {
  956               result = new Point2D.Double(x4, y4);
  957           }
  958           else if (anchor == ItemLabelAnchor.INSIDE2) {
  959               result = new Point2D.Double(x4, y4);
  960           }
  961           else if (anchor == ItemLabelAnchor.INSIDE3) {
  962               result = new Point2D.Double(x4, y3);
  963           }
  964           else if (anchor == ItemLabelAnchor.INSIDE4) {
  965               result = new Point2D.Double(x4, y2);
  966           }
  967           else if (anchor == ItemLabelAnchor.INSIDE5) {
  968               result = new Point2D.Double(x4, y2);
  969           }
  970           else if (anchor == ItemLabelAnchor.INSIDE6) {
  971               result = new Point2D.Double(x3, y2);
  972           }
  973           else if (anchor == ItemLabelAnchor.INSIDE7) {
  974               result = new Point2D.Double(x2, y2);
  975           }
  976           else if (anchor == ItemLabelAnchor.INSIDE8) {
  977               result = new Point2D.Double(x2, y2);
  978           }
  979           else if (anchor == ItemLabelAnchor.INSIDE9) {
  980               result = new Point2D.Double(x2, y3);
  981           }
  982           else if (anchor == ItemLabelAnchor.INSIDE10) {
  983               result = new Point2D.Double(x2, y4);
  984           }
  985           else if (anchor == ItemLabelAnchor.INSIDE11) {
  986               result = new Point2D.Double(x2, y4);
  987           }
  988           else if (anchor == ItemLabelAnchor.INSIDE12) {
  989               result = new Point2D.Double(x3, y4);
  990           }
  991           else if (anchor == ItemLabelAnchor.OUTSIDE1) {
  992               result = new Point2D.Double(x5, y6);
  993           }
  994           else if (anchor == ItemLabelAnchor.OUTSIDE2) {
  995               result = new Point2D.Double(x6, y5);
  996           }
  997           else if (anchor == ItemLabelAnchor.OUTSIDE3) {
  998               result = new Point2D.Double(x6, y3);
  999           }
 1000           else if (anchor == ItemLabelAnchor.OUTSIDE4) {
 1001               result = new Point2D.Double(x6, y1);
 1002           }
 1003           else if (anchor == ItemLabelAnchor.OUTSIDE5) {
 1004               result = new Point2D.Double(x5, y0);
 1005           }
 1006           else if (anchor == ItemLabelAnchor.OUTSIDE6) {
 1007               result = new Point2D.Double(x3, y0);
 1008           }
 1009           else if (anchor == ItemLabelAnchor.OUTSIDE7) {
 1010               result = new Point2D.Double(x1, y0);
 1011           }
 1012           else if (anchor == ItemLabelAnchor.OUTSIDE8) {
 1013               result = new Point2D.Double(x0, y1);
 1014           }
 1015           else if (anchor == ItemLabelAnchor.OUTSIDE9) {
 1016               result = new Point2D.Double(x0, y3);
 1017           }
 1018           else if (anchor == ItemLabelAnchor.OUTSIDE10) {
 1019               result = new Point2D.Double(x0, y5);
 1020           }
 1021           else if (anchor == ItemLabelAnchor.OUTSIDE11) {
 1022               result = new Point2D.Double(x1, y6);
 1023           }
 1024           else if (anchor == ItemLabelAnchor.OUTSIDE12) {
 1025               result = new Point2D.Double(x3, y6);
 1026           }
 1027   
 1028           return result;
 1029   
 1030       }
 1031   
 1032       /**
 1033        * Returns <code>true</code> if the specified anchor point is inside a bar.
 1034        *
 1035        * @param anchor  the anchor point.
 1036        *
 1037        * @return A boolean.
 1038        */
 1039       private boolean isInternalAnchor(ItemLabelAnchor anchor) {
 1040           return anchor == ItemLabelAnchor.CENTER
 1041                  || anchor == ItemLabelAnchor.INSIDE1
 1042                  || anchor == ItemLabelAnchor.INSIDE2
 1043                  || anchor == ItemLabelAnchor.INSIDE3
 1044                  || anchor == ItemLabelAnchor.INSIDE4
 1045                  || anchor == ItemLabelAnchor.INSIDE5
 1046                  || anchor == ItemLabelAnchor.INSIDE6
 1047                  || anchor == ItemLabelAnchor.INSIDE7
 1048                  || anchor == ItemLabelAnchor.INSIDE8
 1049                  || anchor == ItemLabelAnchor.INSIDE9
 1050                  || anchor == ItemLabelAnchor.INSIDE10
 1051                  || anchor == ItemLabelAnchor.INSIDE11
 1052                  || anchor == ItemLabelAnchor.INSIDE12;
 1053       }
 1054   
 1055       /**
 1056        * Tests this instance for equality with an arbitrary object.
 1057        *
 1058        * @param obj  the object (<code>null</code> permitted).
 1059        *
 1060        * @return A boolean.
 1061        */
 1062       public boolean equals(Object obj) {
 1063   
 1064           if (obj == this) {
 1065               return true;
 1066           }
 1067           if (!(obj instanceof BarRenderer)) {
 1068               return false;
 1069           }
 1070           if (!super.equals(obj)) {
 1071               return false;
 1072           }
 1073           BarRenderer that = (BarRenderer) obj;
 1074           if (this.base != that.base) {
 1075               return false;
 1076           }
 1077           if (this.itemMargin != that.itemMargin) {
 1078               return false;
 1079           }
 1080           if (this.drawBarOutline != that.drawBarOutline) {
 1081               return false;
 1082           }
 1083           if (this.maximumBarWidth != that.maximumBarWidth) {
 1084               return false;
 1085           }
 1086           if (this.minimumBarLength != that.minimumBarLength) {
 1087               return false;
 1088           }
 1089           if (!ObjectUtilities.equal(this.gradientPaintTransformer,
 1090                   that.gradientPaintTransformer)) {
 1091               return false;
 1092           }
 1093           if (!ObjectUtilities.equal(this.positiveItemLabelPositionFallback,
 1094               that.positiveItemLabelPositionFallback)) {
 1095               return false;
 1096           }
 1097           if (!ObjectUtilities.equal(this.negativeItemLabelPositionFallback,
 1098               that.negativeItemLabelPositionFallback)) {
 1099               return false;
 1100           }
 1101           return true;
 1102   
 1103       }
 1104   
 1105   }

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