    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
    006     *
    007     * Project Info:  http://www.jfree.org/jfreechart/index.html
    008     *
    009     * This library is free software; you can redistribute it and/or modify it
    010     * under the terms of the GNU Lesser General Public License as published by
    011     * the Free Software Foundation; either version 2.1 of the License, or
    012     * (at your option) any later version.
    013     *
    014     * This library is distributed in the hope that it will be useful, but
    015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017     * License for more details.
    018     *
    019     * You should have received a copy of the GNU Lesser General Public
    020     * License along with this library; if not, write to the Free Software
    021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022     * USA.
    023     *
    024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * ---------------------------
    028     * ClusteredXYBarRenderer.java
    029     * ---------------------------
    030     * (C) Copyright 2003-2006, by Paolo Cova and Contributors.
    031     *
    032     * Original Author:  Paolo Cova;
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *                   Christian W. Zuckschwerdt;
    035     *                   Matthias Rose;
    036     *
    037     * $Id: ClusteredXYBarRenderer.java,v 1.8.2.3 2006/12/11 15:31:33 mungady Exp $
    038     *
    039     * Changes
    040     * -------
    041     * 24-Jan-2003 : Version 1, contributed by Paolo Cova (DG);
    042     * 25-Mar-2003 : Implemented Serializable (DG);
    043     * 01-May-2003 : Modified drawItem() method signature (DG);
    044     * 30-Jul-2003 : Modified entity constructor (CZ);
    045     * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
    046     * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
    047     * 07-Oct-2003 : Added renderer state (DG);
    048     * 03-Nov-2003 : In draw method added state parameter and y==null value
    049     *               handling (MR);
    050     * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
    051     * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
    052     *               getYValue() (DG);
    053     * 01-Oct-2004 : Fixed bug where 'drawBarOutline' flag is ignored (DG);
    054     * 16-May-2005 : Fixed to used outline stroke for bar outlines.  Removed some
    055     *               redundant code with the result that the renderer now respects
    056     *               the 'base' setting from the super-class. Added an equals()
    057     *               method (DG);
    058     * 19-May-2005 : Added minimal item label implementation - needs improving (DG);
    059     * ------------- JFREECHART 1.0.x ---------------------------------------------
    060     * 11-Dec-2006 : Added support for GradientPaint (DG);
    061     *
    062     */
    063
    064    package org.jfree.chart.renderer.xy;
    065
    066    import java.awt.GradientPaint;
    067    import java.awt.Graphics2D;
    068    import java.awt.Paint;
    069    import java.awt.geom.Rectangle2D;
    070    import java.io.Serializable;
    071
    072    import org.jfree.chart.axis.ValueAxis;
    073    import org.jfree.chart.entity.EntityCollection;
    074    import org.jfree.chart.entity.XYItemEntity;
    075    import org.jfree.chart.labels.XYItemLabelGenerator;
    076    import org.jfree.chart.labels.XYToolTipGenerator;
    077    import org.jfree.chart.plot.CrosshairState;
    078    import org.jfree.chart.plot.PlotOrientation;
    079    import org.jfree.chart.plot.PlotRenderingInfo;
    080    import org.jfree.chart.plot.XYPlot;
    081    import org.jfree.data.xy.IntervalXYDataset;
    082    import org.jfree.data.xy.XYDataset;
    083    import org.jfree.ui.RectangleEdge;
    084    import org.jfree.util.PublicCloneable;
    085
    086    /**
    087     * An extension of {@link XYBarRenderer} that displays bars for different
    088     * series values at the same x next to each other. The assumption here is
    089     * that for each x (time or else) there is a y value for each series. If
    090     * this is not the case, there will be spaces between bars for a given x.
    091     * <P>
    092     * This renderer does not include code to calculate the crosshair point for the
    093     * plot.
    094     */
    095    public class ClusteredXYBarRenderer extends XYBarRenderer
    096                                        implements Cloneable, PublicCloneable,
    097                                                   Serializable {
    098
    099        /** For serialization. */
    100        private static final long serialVersionUID = 5864462149177133147L;
    101
    102        /** Determines whether bar center should be interval start. */
    103        private boolean centerBarAtStartValue;
    104
    105        /**
    106         * Default constructor. Bar margin is set to 0.0.
    107        */
    108        public ClusteredXYBarRenderer() {
    109            this(0.0, false);
    110        }
    111
    112        /**
    113        * Constructs a new XY clustered bar renderer.
    114        *
    115        * @param margin  the percentage amount to trim from the width of each bar.
    116        * @param centerBarAtStartValue  if true, bars will be centered on the start
    117        *                               of the time period.
    118        */
    119        public ClusteredXYBarRenderer(double margin,
    120                                      boolean centerBarAtStartValue) {
    121            super(margin);
    122            this.centerBarAtStartValue = centerBarAtStartValue;
    123        }
    124
    125        /**
    126         * Draws the visual representation of a single data item. This method
    127         * is mostly copied from the superclass, the change is that in the
    128         * calculated space for a singe bar we draw bars for each series next to
    129         * each other. The width of each bar is the available width divided by
    130         * the number of series. Bars for each series are drawn in order left to
    131         * right.
    132         *
    133         * @param g2  the graphics device.
    134         * @param state  the renderer state.
    135         * @param dataArea  the area within which the plot is being drawn.
    136         * @param info  collects information about the drawing.
    137         * @param plot  the plot (can be used to obtain standard color
    138         *              information etc).
    139         * @param domainAxis  the domain axis.
    140         * @param rangeAxis  the range axis.
    141         * @param dataset  the dataset.
    142         * @param series  the series index.
    143         * @param item  the item index.
    144         * @param crosshairState  crosshair information for the plot
    145         *                        (<code>null</code> permitted).
    146         * @param pass  the pass index.
    147         */
    148        public void drawItem(Graphics2D g2,
    149                             XYItemRendererState state,
    150                             Rectangle2D dataArea,
    151                             PlotRenderingInfo info,
    152                             XYPlot plot,
    153                             ValueAxis domainAxis,
    154                             ValueAxis rangeAxis,
    155                             XYDataset dataset, int series, int item,
    156                             CrosshairState crosshairState,
    157                             int pass) {
    158
    159            IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;
    160
    161            double value0;
    162            double value1;
    163            if (getUseYInterval()) {
    164                value0 = intervalDataset.getStartYValue(series, item);
    165                value1 = intervalDataset.getEndYValue(series, item);
    166            }
    167            else {
    168                value0 = getBase();
    169                value1 = intervalDataset.getYValue(series, item);
    170            }
    171            if (Double.isNaN(value0) || Double.isNaN(value1)) {
    172                return;
    173            }
    174
    175            double translatedValue0 = rangeAxis.valueToJava2D(value0, dataArea,
    176                    plot.getRangeAxisEdge());
    177            double translatedValue1 = rangeAxis.valueToJava2D(value1, dataArea,
    178                    plot.getRangeAxisEdge());
    179
    180            RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
    181            double x1 = intervalDataset.getStartXValue(series, item);
    182            double translatedX1 = domainAxis.valueToJava2D(x1, dataArea,
    183                    xAxisLocation);
    184
    185            double x2 = intervalDataset.getEndXValue(series, item);
    186            double translatedX2 = domainAxis.valueToJava2D(x2, dataArea,
    187                    xAxisLocation);
    188
    189            double translatedWidth = Math.max(1, Math.abs(translatedX2
    190                    - translatedX1));
    191            double translatedHeight = Math.abs(translatedValue0 - translatedValue1);
    192
    193            if (this.centerBarAtStartValue) {
    194                translatedX1 -= translatedWidth / 2;
    195            }
    196
    197            PlotOrientation orientation = plot.getOrientation();
    198            double m = getMargin();
    199            if (m > 0.0) {
    200                double cut = translatedWidth * getMargin();
    201                translatedWidth = translatedWidth - cut;
    202                if (orientation == PlotOrientation.HORIZONTAL)
    203                  translatedX1 = translatedX1 - cut / 2;
    204                else
    205                  translatedX1 = translatedX1 + cut / 2;
    206            }
    207
    208            int numSeries = dataset.getSeriesCount();
    209            double seriesBarWidth = translatedWidth / numSeries;
    210
    211            Rectangle2D bar = null;
    212            if (orientation == PlotOrientation.HORIZONTAL) {
    213                bar = new Rectangle2D.Double(Math.min(translatedValue0,
    214                        translatedValue1), translatedX1 - seriesBarWidth
    215                        * (numSeries - series), translatedHeight, seriesBarWidth);
    216            }
    217            else if (orientation == PlotOrientation.VERTICAL) {
    218                bar = new Rectangle2D.Double(translatedX1 + seriesBarWidth * series,
    219                        Math.min(translatedValue0, translatedValue1),
    220                        seriesBarWidth, translatedHeight);
    221            }
    222            Paint itemPaint = getItemPaint(series, item);
    223            if (getGradientPaintTransformer()
    224                    != null && itemPaint instanceof GradientPaint) {
    225                GradientPaint gp = (GradientPaint) itemPaint;
    226                itemPaint = getGradientPaintTransformer().transform(gp, bar);
    227            }
    228            g2.setPaint(itemPaint);
    229
    230            g2.fill(bar);
    231            if (isDrawBarOutline() && Math.abs(translatedX2 - translatedX1) > 3) {
    232                g2.setStroke(getItemOutlineStroke(series, item));
    233                g2.setPaint(getItemOutlinePaint(series, item));
    234                g2.draw(bar);
    235            }
    236
    237            if (isItemLabelVisible(series, item)) {
    238                XYItemLabelGenerator generator = getItemLabelGenerator(series,
    239                        item);
    240                drawItemLabel(g2, dataset, series, item, plot, generator, bar,
    241                        value1 < 0.0);
    242            }
    243
    244            // add an entity for the item...
    245            if (info != null) {
    246                EntityCollection entities = info.getOwner().getEntityCollection();
    247                if (entities != null) {
    248                    String tip = null;
    249                    XYToolTipGenerator generator
    250                        = getToolTipGenerator(series, item);
    251                    if (generator != null) {
    252                        tip = generator.generateToolTip(dataset, series, item);
    253                    }
    254                    String url = null;
    255                    if (getURLGenerator() != null) {
    256                        url = getURLGenerator().generateURL(dataset, series, item);
    257                    }
    258                    XYItemEntity entity = new XYItemEntity(bar, dataset, series,
    259                            item, tip, url);
    260                    entities.add(entity);
    261                }
    262            }
    263
    264        }
    265
    266        /**
    267         * Tests this renderer for equality with an arbitrary object, returning
    268         * <code>true</code> if <code>obj</code> is a
    269         * <code>ClusteredXYBarRenderer</code> with the same settings as this
    270         * renderer, and <code>false</code> otherwise.
    271         *
    272         * @param obj  the object (<code>null</code> permitted).
    273         *
    274         * @return A boolean.
    275         */
    276        public boolean equals(Object obj) {
    277            if (obj == this) {
    278                return true;
    279            }
    280            if (!(obj instanceof ClusteredXYBarRenderer)) {
    281                return false;
    282            }
    283            if (!super.equals(obj)) {
    284                return false;
    285            }
    286            ClusteredXYBarRenderer that = (ClusteredXYBarRenderer) obj;
    287            if (this.centerBarAtStartValue != that.centerBarAtStartValue) {
    288                return false;
    289            }
    290            return true;
    291        }
    292
    293        /**
    294         * Returns a clone of the renderer.
    295         *
    296         * @return A clone.
    297         *
    298         * @throws CloneNotSupportedException  if the renderer cannot be cloned.
    299         */
    300        public Object clone() throws CloneNotSupportedException {
    301            return super.clone();
    302        }
    303
    304    }