1:
50:
51: package ;
52:
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62: import ;
63: import ;
64: import ;
65:
66: import ;
67: import ;
68: import ;
69: import ;
70: import ;
71: import ;
72: import ;
73: import ;
74: import ;
75: import ;
76: import ;
77:
78:
91: public class XYPointerAnnotation extends XYTextAnnotation
92: implements Cloneable, PublicCloneable,
93: Serializable {
94:
95:
96: private static final long serialVersionUID = -4031161445009858551L;
97:
98:
99: public static final double DEFAULT_TIP_RADIUS = 10.0;
100:
101:
102: public static final double DEFAULT_BASE_RADIUS = 30.0;
103:
104:
105: public static final double DEFAULT_LABEL_OFFSET = 3.0;
106:
107:
108: public static final double DEFAULT_ARROW_LENGTH = 5.0;
109:
110:
111: public static final double DEFAULT_ARROW_WIDTH = 3.0;
112:
113:
114: private double angle;
115:
116:
120: private double tipRadius;
121:
122:
126: private double baseRadius;
127:
128:
129: private double arrowLength;
130:
131:
132: private double arrowWidth;
133:
134:
135: private transient Stroke arrowStroke;
136:
137:
138: private transient Paint arrowPaint;
139:
140:
141: private double labelOffset;
142:
143:
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:
172: public double getAngle() {
173: return this.angle;
174: }
175:
176:
183: public void setAngle(double angle) {
184: this.angle = angle;
185: }
186:
187:
194: public double getTipRadius() {
195: return this.tipRadius;
196: }
197:
198:
205: public void setTipRadius(double radius) {
206: this.tipRadius = radius;
207: }
208:
209:
216: public double getBaseRadius() {
217: return this.baseRadius;
218: }
219:
220:
227: public void setBaseRadius(double radius) {
228: this.baseRadius = radius;
229: }
230:
231:
238: public double getLabelOffset() {
239: return this.labelOffset;
240: }
241:
242:
250: public void setLabelOffset(double offset) {
251: this.labelOffset = offset;
252: }
253:
254:
261: public double getArrowLength() {
262: return this.arrowLength;
263: }
264:
265:
272: public void setArrowLength(double length) {
273: this.arrowLength = length;
274: }
275:
276:
283: public double getArrowWidth() {
284: return this.arrowWidth;
285: }
286:
287:
294: public void setArrowWidth(double width) {
295: this.arrowWidth = width;
296: }
297:
298:
305: public Stroke getArrowStroke() {
306: return this.arrowStroke;
307: }
308:
309:
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:
330: public Paint getArrowPaint() {
331: return this.arrowPaint;
332: }
333:
334:
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:
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:
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:
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:
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:
501: public Object clone() throws CloneNotSupportedException {
502: return super.clone();
503: }
504:
505:
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:
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: }