Save This Page
Home » jcommon-1.0.13 » org.jfree » chart » annotations » [javadoc | source]
    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    * XYPointerAnnotation.java
   29    * ------------------------
   30    * (C) Copyright 2003-2007, by Object Refinery Limited.
   31    *
   32    * Original Author:  David Gilbert (for Object Refinery Limited);
   33    * Contributor(s):   -;
   34    *
   35    * Changes:
   36    * --------
   37    * 21-May-2003 : Version 1 (DG);
   38    * 10-Jun-2003 : Changed BoundsAnchor to TextAnchor (DG);
   39    * 02-Jul-2003 : Added accessor methods and simplified constructor (DG);
   40    * 19-Aug-2003 : Implemented Cloneable (DG);
   41    * 13-Oct-2003 : Fixed bug where arrow paint is not set correctly (DG);
   42    * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
   43    * 29-Sep-2004 : Changes to draw() method signature (DG);
   44    * ------------- JFREECHART 1.0.x ---------------------------------------------
   45    * 20-Feb-2006 : Correction for equals() method (fixes bug 1435160) (DG);
   46    * 12-Jul-2006 : Fix drawing for PlotOrientation.HORIZONTAL, thanks to 
   47    *               Skunk (DG);
   48    *
   49    */
   50   
   51   package org.jfree.chart.annotations;
   52   
   53   import java.awt.BasicStroke;
   54   import java.awt.Color;
   55   import java.awt.Graphics2D;
   56   import java.awt.Paint;
   57   import java.awt.Stroke;
   58   import java.awt.geom.GeneralPath;
   59   import java.awt.geom.Line2D;
   60   import java.awt.geom.Rectangle2D;
   61   import java.io.IOException;
   62   import java.io.ObjectInputStream;
   63   import java.io.ObjectOutputStream;
   64   import java.io.Serializable;
   65   
   66   import org.jfree.chart.HashUtilities;
   67   import org.jfree.chart.axis.ValueAxis;
   68   import org.jfree.chart.plot.Plot;
   69   import org.jfree.chart.plot.PlotOrientation;
   70   import org.jfree.chart.plot.PlotRenderingInfo;
   71   import org.jfree.chart.plot.XYPlot;
   72   import org.jfree.io.SerialUtilities;
   73   import org.jfree.text.TextUtilities;
   74   import org.jfree.ui.RectangleEdge;
   75   import org.jfree.util.ObjectUtilities;
   76   import org.jfree.util.PublicCloneable;
   77   
   78   /**
   79    * An arrow and label that can be placed on an 
   80    * {@link org.jfree.chart.plot.XYPlot}.  The arrow is drawn at a user-definable 
   81    * angle so that it points towards the (x, y) location for the annotation.  
   82    * <p>
   83    * The arrow length (and its offset from the (x, y) location) is controlled by 
   84    * the tip radius and the base radius attributes.  Imagine two circles around 
   85    * the (x, y) coordinate: the inner circle defined by the tip radius, and the 
   86    * outer circle defined by the base radius.  Now, draw the arrow starting at 
   87    * some point on the outer circle (the point is determined by the angle), with 
   88    * the arrow tip being drawn at a corresponding point on the inner circle.
   89    *
   90    */
   91   public class XYPointerAnnotation extends XYTextAnnotation 
   92                                    implements Cloneable, PublicCloneable, 
   93                                               Serializable {
   94   
   95       /** For serialization. */
   96       private static final long serialVersionUID = -4031161445009858551L;
   97       
   98       /** The default tip radius (in Java2D units). */
   99       public static final double DEFAULT_TIP_RADIUS = 10.0;
  100       
  101       /** The default base radius (in Java2D units). */
  102       public static final double DEFAULT_BASE_RADIUS = 30.0;
  103       
  104       /** The default label offset (in Java2D units). */
  105       public static final double DEFAULT_LABEL_OFFSET = 3.0;
  106       
  107       /** The default arrow length (in Java2D units). */
  108       public static final double DEFAULT_ARROW_LENGTH = 5.0;
  109   
  110       /** The default arrow width (in Java2D units). */
  111       public static final double DEFAULT_ARROW_WIDTH = 3.0;
  112       
  113       /** The angle of the arrow's line (in radians). */
  114       private double angle;
  115   
  116       /** 
  117        * The radius from the (x, y) point to the tip of the arrow (in Java2D 
  118        * units). 
  119        */
  120       private double tipRadius;
  121   
  122       /** 
  123        * The radius from the (x, y) point to the start of the arrow line (in 
  124        * Java2D units). 
  125        */
  126       private double baseRadius;
  127   
  128       /** The length of the arrow head (in Java2D units). */
  129       private double arrowLength;
  130   
  131       /** The arrow width (in Java2D units, per side). */
  132       private double arrowWidth;
  133       
  134       /** The arrow stroke. */
  135       private transient Stroke arrowStroke;
  136   
  137       /** The arrow paint. */
  138       private transient Paint arrowPaint;
  139       
  140       /** The radius from the base point to the anchor point for the label. */
  141       private double labelOffset;
  142   
  143       /**
  144        * Creates a new label and arrow annotation.
  145        *
  146        * @param label  the label (<code>null</code> permitted).
  147        * @param x  the x-coordinate (measured against the chart's domain axis).
  148        * @param y  the y-coordinate (measured against the chart's range axis).
  149        * @param angle  the angle of the arrow's line (in radians).
  150        */
  151       public XYPointerAnnotation(String label, double x, double y, double angle) {
  152   
  153           super(label, x, y);
  154           this.angle = angle;
  155           this.tipRadius = DEFAULT_TIP_RADIUS;
  156           this.baseRadius = DEFAULT_BASE_RADIUS;
  157           this.arrowLength = DEFAULT_ARROW_LENGTH;
  158           this.arrowWidth = DEFAULT_ARROW_WIDTH;
  159           this.labelOffset = DEFAULT_LABEL_OFFSET;
  160           this.arrowStroke = new BasicStroke(1.0f);
  161           this.arrowPaint = Color.black;
  162   
  163       }
  164       
  165       /**
  166        * Returns the angle of the arrow.
  167        * 
  168        * @return The angle (in radians).
  169        * 
  170        * @see #setAngle(double)
  171        */
  172       public double getAngle() {
  173           return this.angle;
  174       }
  175       
  176       /**
  177        * Sets the angle of the arrow.
  178        * 
  179        * @param angle  the angle (in radians).
  180        * 
  181        * @see #getAngle()
  182        */
  183       public void setAngle(double angle) {
  184           this.angle = angle;
  185       }
  186       
  187       /**
  188        * Returns the tip radius.
  189        * 
  190        * @return The tip radius (in Java2D units).
  191        * 
  192        * @see #setTipRadius(double)
  193        */
  194       public double getTipRadius() {
  195           return this.tipRadius;
  196       }
  197       
  198       /**
  199        * Sets the tip radius.
  200        * 
  201        * @param radius  the radius (in Java2D units).
  202        * 
  203        * @see #getTipRadius()
  204        */
  205       public void setTipRadius(double radius) {
  206           this.tipRadius = radius;
  207       }
  208       
  209       /**
  210        * Returns the base radius.
  211        * 
  212        * @return The base radius (in Java2D units).
  213        * 
  214        * @see #setBaseRadius(double)
  215        */
  216       public double getBaseRadius() {
  217           return this.baseRadius;
  218       }
  219       
  220       /**
  221        * Sets the base radius.
  222        * 
  223        * @param radius  the radius (in Java2D units).
  224        * 
  225        * @see #getBaseRadius()
  226        */
  227       public void setBaseRadius(double radius) {
  228           this.baseRadius = radius;
  229       }
  230   
  231       /**
  232        * Returns the label offset.
  233        * 
  234        * @return The label offset (in Java2D units).
  235        * 
  236        * @see #setLabelOffset(double)
  237        */
  238       public double getLabelOffset() {
  239           return this.labelOffset;
  240       }
  241       
  242       /**
  243        * Sets the label offset (from the arrow base, continuing in a straight 
  244        * line, in Java2D units).
  245        * 
  246        * @param offset  the offset (in Java2D units).
  247        * 
  248        * @see #getLabelOffset()
  249        */
  250       public void setLabelOffset(double offset) {
  251           this.labelOffset = offset;
  252       }
  253       
  254       /**
  255        * Returns the arrow length.
  256        * 
  257        * @return The arrow length.
  258        * 
  259        * @see #setArrowLength(double)
  260        */
  261       public double getArrowLength() {
  262           return this.arrowLength;
  263       }
  264       
  265       /**
  266        * Sets the arrow length.
  267        * 
  268        * @param length  the length.
  269        * 
  270        * @see #getArrowLength()
  271        */
  272       public void setArrowLength(double length) {
  273           this.arrowLength = length;
  274       }
  275   
  276       /**
  277        * Returns the arrow width.
  278        * 
  279        * @return The arrow width (in Java2D units).
  280        * 
  281        * @see #setArrowWidth(double)
  282        */
  283       public double getArrowWidth() {
  284           return this.arrowWidth;
  285       }
  286       
  287       /**
  288        * Sets the arrow width.
  289        * 
  290        * @param width  the width (in Java2D units).
  291        * 
  292        * @see #getArrowWidth()
  293        */
  294       public void setArrowWidth(double width) {
  295           this.arrowWidth = width;
  296       }
  297       
  298       /** 
  299        * Returns the stroke used to draw the arrow line.
  300        * 
  301        * @return The arrow stroke (never <code>null</code>).
  302        * 
  303        * @see #setArrowStroke(Stroke)
  304        */
  305       public Stroke getArrowStroke() {
  306           return this.arrowStroke;
  307       }
  308   
  309       /** 
  310        * Sets the stroke used to draw the arrow line.
  311        * 
  312        * @param stroke  the stroke (<code>null</code> not permitted).
  313        * 
  314        * @see #getArrowStroke()
  315        */
  316       public void setArrowStroke(Stroke stroke) {
  317           if (stroke == null) {
  318               throw new IllegalArgumentException("Null 'stroke' not permitted.");
  319           }
  320           this.arrowStroke = stroke;
  321       }
  322       
  323       /**
  324        * Returns the paint used for the arrow.
  325        * 
  326        * @return The arrow paint (never <code>null</code>).
  327        * 
  328        * @see #setArrowPaint(Paint)
  329        */
  330       public Paint getArrowPaint() {
  331           return this.arrowPaint;
  332       }
  333       
  334       /**
  335        * Sets the paint used for the arrow.
  336        * 
  337        * @param paint  the arrow paint (<code>null</code> not permitted).
  338        * 
  339        * @see #getArrowPaint()
  340        */
  341       public void setArrowPaint(Paint paint) {
  342           if (paint == null) {
  343               throw new IllegalArgumentException("Null 'paint' argument.");
  344           }
  345           this.arrowPaint = paint;
  346       }
  347       
  348       /**
  349        * Draws the annotation.
  350        *
  351        * @param g2  the graphics device.
  352        * @param plot  the plot.
  353        * @param dataArea  the data area.
  354        * @param domainAxis  the domain axis.
  355        * @param rangeAxis  the range axis.
  356        * @param rendererIndex  the renderer index.
  357        * @param info  the plot rendering info.
  358        */
  359       public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
  360                        ValueAxis domainAxis, ValueAxis rangeAxis, 
  361                        int rendererIndex,
  362                        PlotRenderingInfo info) {
  363   
  364           PlotOrientation orientation = plot.getOrientation();
  365           RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
  366                   plot.getDomainAxisLocation(), orientation);
  367           RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
  368                   plot.getRangeAxisLocation(), orientation);
  369           double j2DX = domainAxis.valueToJava2D(getX(), dataArea, domainEdge);
  370           double j2DY = rangeAxis.valueToJava2D(getY(), dataArea, rangeEdge);
  371           if (orientation == PlotOrientation.HORIZONTAL) {
  372               double temp = j2DX;
  373               j2DX = j2DY;
  374               j2DY = temp;
  375           }
  376           double startX = j2DX + Math.cos(this.angle) * this.baseRadius;
  377           double startY = j2DY + Math.sin(this.angle) * this.baseRadius;
  378   
  379           double endX = j2DX + Math.cos(this.angle) * this.tipRadius;
  380           double endY = j2DY + Math.sin(this.angle) * this.tipRadius;
  381   
  382           double arrowBaseX = endX + Math.cos(this.angle) * this.arrowLength;
  383           double arrowBaseY = endY + Math.sin(this.angle) * this.arrowLength;
  384   
  385           double arrowLeftX = arrowBaseX 
  386               + Math.cos(this.angle + Math.PI / 2.0) * this.arrowWidth;
  387           double arrowLeftY = arrowBaseY 
  388               + Math.sin(this.angle + Math.PI / 2.0) * this.arrowWidth;
  389   
  390           double arrowRightX = arrowBaseX 
  391               - Math.cos(this.angle + Math.PI / 2.0) * this.arrowWidth;
  392           double arrowRightY = arrowBaseY 
  393               - Math.sin(this.angle + Math.PI / 2.0) * this.arrowWidth;
  394   
  395           GeneralPath arrow = new GeneralPath();
  396           arrow.moveTo((float) endX, (float) endY);
  397           arrow.lineTo((float) arrowLeftX, (float) arrowLeftY);
  398           arrow.lineTo((float) arrowRightX, (float) arrowRightY);
  399           arrow.closePath();
  400   
  401           g2.setStroke(this.arrowStroke);
  402           g2.setPaint(this.arrowPaint);
  403           Line2D line = new Line2D.Double(startX, startY, endX, endY);
  404           g2.draw(line);
  405           g2.fill(arrow);
  406   
  407           // draw the label
  408           g2.setFont(getFont());
  409           g2.setPaint(getPaint());
  410           double labelX = j2DX 
  411               + Math.cos(this.angle) * (this.baseRadius + this.labelOffset);
  412           double labelY = j2DY 
  413               + Math.sin(this.angle) * (this.baseRadius + this.labelOffset);
  414           Rectangle2D hotspot = TextUtilities.drawAlignedString(getText(), 
  415                   g2, (float) labelX, (float) labelY, getTextAnchor());
  416   
  417           String toolTip = getToolTipText();
  418           String url = getURL();
  419           if (toolTip != null || url != null) {
  420               addEntity(info, hotspot, rendererIndex, toolTip, url);
  421           }
  422           
  423       }
  424       
  425       /**
  426        * Tests this annotation for equality with an arbitrary object.
  427        * 
  428        * @param obj  the object (<code>null</code> permitted).
  429        * 
  430        * @return <code>true</code> or <code>false</code>.
  431        */
  432       public boolean equals(Object obj) {
  433           if (obj == this) {
  434               return true;
  435           }
  436           if (!(obj instanceof XYPointerAnnotation)) {
  437               return false;
  438           }
  439           if (!super.equals(obj)) {
  440               return false;
  441           }
  442           XYPointerAnnotation that = (XYPointerAnnotation) obj;
  443           if (this.angle != that.angle) {
  444               return false;
  445           }
  446           if (this.tipRadius != that.tipRadius) {
  447               return false;
  448           }
  449           if (this.baseRadius != that.baseRadius) {
  450               return false;
  451           }
  452           if (this.arrowLength != that.arrowLength) {
  453               return false;
  454           }
  455           if (this.arrowWidth != that.arrowWidth) {
  456               return false;
  457           }
  458           if (!this.arrowPaint.equals(that.arrowPaint)) {
  459               return false;
  460           }
  461           if (!ObjectUtilities.equal(this.arrowStroke, that.arrowStroke)) {
  462               return false;
  463           }
  464           if (this.labelOffset != that.labelOffset) {
  465               return false;
  466           }
  467           return true;
  468       }
  469       
  470       /**
  471        * Returns a hash code for this instance.
  472        * 
  473        * @return A hash code.
  474        */
  475       public int hashCode() {
  476           int result = super.hashCode();
  477           long temp = Double.doubleToLongBits(this.angle);
  478           result = 37 * result + (int) (temp ^ (temp >>> 32));
  479           temp = Double.doubleToLongBits(this.tipRadius);
  480           result = 37 * result + (int) (temp ^ (temp >>> 32));
  481           temp = Double.doubleToLongBits(this.baseRadius);
  482           result = 37 * result + (int) (temp ^ (temp >>> 32));
  483           temp = Double.doubleToLongBits(this.arrowLength);
  484           result = 37 * result + (int) (temp ^ (temp >>> 32));
  485           temp = Double.doubleToLongBits(this.arrowWidth);
  486           result = 37 * result + (int) (temp ^ (temp >>> 32));
  487           result = result * 37 + HashUtilities.hashCodeForPaint(this.arrowPaint);
  488           result = result * 37 + this.arrowStroke.hashCode();
  489           temp = Double.doubleToLongBits(this.labelOffset);
  490           result = 37 * result + (int) (temp ^ (temp >>> 32));
  491           return super.hashCode();
  492       }
  493       
  494       /**
  495        * Returns a clone of the annotation.
  496        * 
  497        * @return A clone.
  498        * 
  499        * @throws CloneNotSupportedException  if the annotation can't be cloned.
  500        */
  501       public Object clone() throws CloneNotSupportedException {
  502           return super.clone();
  503       }
  504   
  505       /**
  506        * Provides serialization support.
  507        *
  508        * @param stream  the output stream.
  509        *
  510        * @throws IOException if there is an I/O error.
  511        */
  512       private void writeObject(ObjectOutputStream stream) throws IOException {
  513           stream.defaultWriteObject();
  514           SerialUtilities.writePaint(this.arrowPaint, stream);
  515           SerialUtilities.writeStroke(this.arrowStroke, stream);
  516       }
  517   
  518       /**
  519        * Provides serialization support.
  520        *
  521        * @param stream  the input stream.
  522        *
  523        * @throws IOException  if there is an I/O error.
  524        * @throws ClassNotFoundException  if there is a classpath problem.
  525        */
  526       private void readObject(ObjectInputStream stream) 
  527           throws IOException, ClassNotFoundException {
  528           stream.defaultReadObject();
  529           this.arrowPaint = SerialUtilities.readPaint(stream);
  530           this.arrowStroke = SerialUtilities.readStroke(stream);
  531       }
  532   
  533   }

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