Source for org.jfree.chart.renderer.xy.VectorRenderer

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, 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:  * VectorRenderer.java
  29:  * -------------------
  30:  * (C) Copyright 2007, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * Changes
  36:  * -------
  37:  * 30-Jan-2007 : Version 1 (DG);
  38:  * 24-May-2007 : Updated for method name changes (DG);
  39:  * 25-May-2007 : Moved from experimental to the main source tree (DG);
  40:  * 
  41:  */
  42: 
  43: package org.jfree.chart.renderer.xy;
  44: 
  45: import java.awt.Graphics2D;
  46: import java.awt.geom.GeneralPath;
  47: import java.awt.geom.Line2D;
  48: import java.awt.geom.Rectangle2D;
  49: import java.io.Serializable;
  50: 
  51: import org.jfree.chart.axis.ValueAxis;
  52: import org.jfree.chart.plot.CrosshairState;
  53: import org.jfree.chart.plot.PlotOrientation;
  54: import org.jfree.chart.plot.PlotRenderingInfo;
  55: import org.jfree.chart.plot.XYPlot;
  56: import org.jfree.data.Range;
  57: import org.jfree.data.xy.VectorXYDataset;
  58: import org.jfree.data.xy.XYDataset;
  59: 
  60: /**
  61:  * A renderer that represents data from an {@link VectorXYDataset} by drawing a
  62:  * line with an arrow at each (x, y) point.
  63:  * 
  64:  * @since 1.0.6
  65:  */
  66: public class VectorRenderer extends AbstractXYItemRenderer 
  67:         implements XYItemRenderer, Cloneable, Serializable {
  68:     
  69:     /** The length of the base. */
  70:     private double baseLength = 0.10;
  71:     
  72:     /** The length of the head. */
  73:     private double headLength = 0.14;
  74:     
  75:     
  76:     /**
  77:      * Creates a new <code>XYBlockRenderer</code> instance with default 
  78:      * attributes.
  79:      */
  80:     public VectorRenderer() {
  81:     }
  82:     
  83:     /**
  84:      * Returns the lower and upper bounds (range) of the x-values in the 
  85:      * specified dataset.
  86:      * 
  87:      * @param dataset  the dataset (<code>null</code> permitted).
  88:      * 
  89:      * @return The range (<code>null</code> if the dataset is <code>null</code>
  90:      *         or empty).
  91:      */
  92:     public Range findDomainBounds(XYDataset dataset) {
  93:         if (dataset == null) {
  94:             throw new IllegalArgumentException("Null 'dataset' argument.");   
  95:         }
  96:         double minimum = Double.POSITIVE_INFINITY;
  97:         double maximum = Double.NEGATIVE_INFINITY;
  98:         int seriesCount = dataset.getSeriesCount();
  99:         double lvalue;
 100:         double uvalue;
 101:         if (dataset instanceof VectorXYDataset) {
 102:             VectorXYDataset vdataset = (VectorXYDataset) dataset;
 103:             for (int series = 0; series < seriesCount; series++) {
 104:                 int itemCount = dataset.getItemCount(series);
 105:                 for (int item = 0; item < itemCount; item++) {
 106:                     double delta = vdataset.getVectorXValue(series, item);
 107:                     if (delta < 0.0) {
 108:                         uvalue = vdataset.getXValue(series, item);
 109:                         lvalue = uvalue + delta;
 110:                     }
 111:                     else {
 112:                         lvalue = vdataset.getXValue(series, item);
 113:                         uvalue = lvalue + delta;
 114:                     }
 115:                     minimum = Math.min(minimum, lvalue);
 116:                     maximum = Math.max(maximum, uvalue);
 117:                 }
 118:             }
 119:         }
 120:         else {
 121:             for (int series = 0; series < seriesCount; series++) {
 122:                 int itemCount = dataset.getItemCount(series);
 123:                 for (int item = 0; item < itemCount; item++) {
 124:                     lvalue = dataset.getXValue(series, item);
 125:                     uvalue = lvalue;
 126:                     minimum = Math.min(minimum, lvalue);
 127:                     maximum = Math.max(maximum, uvalue);
 128:                 }
 129:             }
 130:         }
 131:         if (minimum > maximum) {
 132:             return null;
 133:         }
 134:         else {
 135:             return new Range(minimum, maximum);
 136:         }
 137:     }
 138:     
 139:     /**
 140:      * Returns the range of values the renderer requires to display all the 
 141:      * items from the specified dataset.
 142:      * 
 143:      * @param dataset  the dataset (<code>null</code> permitted).
 144:      * 
 145:      * @return The range (<code>null</code> if the dataset is <code>null</code> 
 146:      *         or empty).
 147:      */
 148:     public Range findRangeBounds(XYDataset dataset) {
 149:         if (dataset == null) {
 150:             throw new IllegalArgumentException("Null 'dataset' argument.");   
 151:         }
 152:         double minimum = Double.POSITIVE_INFINITY;
 153:         double maximum = Double.NEGATIVE_INFINITY;
 154:         int seriesCount = dataset.getSeriesCount();
 155:         double lvalue;
 156:         double uvalue;
 157:         if (dataset instanceof VectorXYDataset) {
 158:             VectorXYDataset vdataset = (VectorXYDataset) dataset;
 159:             for (int series = 0; series < seriesCount; series++) {
 160:                 int itemCount = dataset.getItemCount(series);
 161:                 for (int item = 0; item < itemCount; item++) {
 162:                     double delta = vdataset.getVectorYValue(series, item);
 163:                     if (delta < 0.0) {
 164:                         uvalue = vdataset.getYValue(series, item);
 165:                         lvalue = uvalue + delta;
 166:                     }
 167:                     else {
 168:                         lvalue = vdataset.getYValue(series, item);
 169:                         uvalue = lvalue + delta;
 170:                     }
 171:                     minimum = Math.min(minimum, lvalue);
 172:                     maximum = Math.max(maximum, uvalue);
 173:                 }
 174:             }
 175:         }
 176:         else {
 177:             for (int series = 0; series < seriesCount; series++) {
 178:                 int itemCount = dataset.getItemCount(series);
 179:                 for (int item = 0; item < itemCount; item++) {
 180:                     lvalue = dataset.getYValue(series, item);
 181:                     uvalue = lvalue;
 182:                     minimum = Math.min(minimum, lvalue);
 183:                     maximum = Math.max(maximum, uvalue);
 184:                 }
 185:             }
 186:         }
 187:         if (minimum > maximum) {
 188:             return null;
 189:         }
 190:         else {
 191:             return new Range(minimum, maximum);
 192:         }
 193:     }
 194:     
 195:     /**
 196:      * Draws the block representing the specified item.
 197:      * 
 198:      * @param g2  the graphics device.
 199:      * @param state  the state.
 200:      * @param dataArea  the data area.
 201:      * @param info  the plot rendering info.
 202:      * @param plot  the plot.
 203:      * @param domainAxis  the x-axis.
 204:      * @param rangeAxis  the y-axis.
 205:      * @param dataset  the dataset.
 206:      * @param series  the series index.
 207:      * @param item  the item index.
 208:      * @param crosshairState  the crosshair state.
 209:      * @param pass  the pass index.
 210:      */
 211:     public void drawItem(Graphics2D g2, XYItemRendererState state, 
 212:             Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, 
 213:             ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, 
 214:             int series, int item, CrosshairState crosshairState, int pass) {
 215:         
 216:         double x = dataset.getXValue(series, item);
 217:         double y = dataset.getYValue(series, item);
 218:         double dx = 0.0;
 219:         double dy = 0.0;
 220:         if (dataset instanceof VectorXYDataset) {
 221:             dx = ((VectorXYDataset) dataset).getVectorXValue(series, item);
 222:             dy = ((VectorXYDataset) dataset).getVectorYValue(series, item);
 223:         }
 224:         double xx0 = domainAxis.valueToJava2D(x, dataArea, 
 225:                 plot.getDomainAxisEdge());
 226:         double yy0 = rangeAxis.valueToJava2D(y, dataArea, 
 227:                 plot.getRangeAxisEdge());
 228:         double xx1 = domainAxis.valueToJava2D(x + dx, dataArea, 
 229:                 plot.getDomainAxisEdge());
 230:         double yy1 = rangeAxis.valueToJava2D(y + dy, dataArea, 
 231:                 plot.getRangeAxisEdge());
 232:         Line2D line;
 233:         PlotOrientation orientation = plot.getOrientation();
 234:         if (orientation.equals(PlotOrientation.HORIZONTAL)) {
 235:             line = new Line2D.Double(yy0, xx0, yy1, xx1);
 236:         }
 237:         else {
 238:             line = new Line2D.Double(xx0, yy0, xx1, yy1);
 239:         }
 240:         g2.setPaint(getItemPaint(series, item));
 241:         g2.setStroke(getItemStroke(series, item));
 242:         g2.draw(line);
 243:         
 244:         // calculate the arrow head and draw it...
 245:         double dxx = (xx1 - xx0);
 246:         double dyy = (yy1 - yy0);
 247:         double bx = xx0 + (1.0 - this.baseLength) * dxx;
 248:         double by = yy0 + (1.0 - this.baseLength) * dyy;
 249:         
 250:         double cx = xx0 + (1.0 - this.headLength) * dxx;
 251:         double cy = yy0 + (1.0 - this.headLength) * dyy;
 252:  
 253:         double angle = 0.0;
 254:         if (dxx != 0.0) {
 255:             angle = Math.PI / 2.0 - Math.atan(dyy / dxx);
 256:         }
 257:         double deltaX = 2.0 * Math.cos(angle);
 258:         double deltaY = 2.0 * Math.sin(angle);
 259:         
 260:         double leftx = cx + deltaX;
 261:         double lefty = cy - deltaY;
 262:         double rightx = cx - deltaX;
 263:         double righty = cy + deltaY;
 264:         
 265:         GeneralPath p = new GeneralPath();
 266:         p.moveTo((float) xx1, (float) yy1);
 267:         p.lineTo((float) rightx, (float) righty);
 268:         p.lineTo((float) bx, (float) by);
 269:         p.lineTo((float) leftx, (float) lefty);
 270:         p.closePath();
 271:         g2.draw(p);
 272:         
 273:         
 274:     }
 275:     
 276:     /**
 277:      * Tests this <code>VectorRenderer</code> for equality with an arbitrary
 278:      * object.  This method returns <code>true</code> if and only if:
 279:      * <ul>
 280:      * <li><code>obj</code> is an instance of <code>VectorRenderer</code> (not
 281:      *     <code>null</code>);</li>
 282:      * <li><code>obj</code> has the same field values as this 
 283:      *     <code>VectorRenderer</code>;</li>
 284:      * </ul>
 285:      * 
 286:      * @param obj  the object (<code>null</code> permitted).
 287:      * 
 288:      * @return A boolean.
 289:      */
 290:     public boolean equals(Object obj) {
 291:         if (obj == this) {
 292:             return true;
 293:         }
 294:         if (!(obj instanceof VectorRenderer)) {
 295:             return false;
 296:         }
 297:         VectorRenderer that = (VectorRenderer) obj;
 298:         if (this.baseLength != that.baseLength) {
 299:             return false;
 300:         }
 301:         if (this.headLength != that.headLength) {
 302:             return false;
 303:         }
 304:         return super.equals(obj);
 305:     }
 306:     
 307:     /**
 308:      * Returns a clone of this renderer.
 309:      * 
 310:      * @return A clone of this renderer.
 311:      * 
 312:      * @throws CloneNotSupportedException if there is a problem creating the 
 313:      *     clone.
 314:      */
 315:     public Object clone() throws CloneNotSupportedException {
 316:         return super.clone();
 317:     }
 318: 
 319: }