1:
52:
53: package ;
54:
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62: import ;
63: import ;
64: import ;
65: import ;
66: import ;
67:
68: import ;
69: import ;
70: import ;
71: import ;
72: import ;
73: import ;
74: import ;
75: import ;
76: import ;
77: import ;
78: import ;
79:
80:
93: public class XYPointerAnnotation extends XYTextAnnotation
94: implements Cloneable, PublicCloneable,
95: Serializable {
96:
97:
98: private static final long serialVersionUID = -4031161445009858551L;
99:
100:
101: public static final double DEFAULT_TIP_RADIUS = 10.0;
102:
103:
104: public static final double DEFAULT_BASE_RADIUS = 30.0;
105:
106:
107: public static final double DEFAULT_LABEL_OFFSET = 3.0;
108:
109:
110: public static final double DEFAULT_ARROW_LENGTH = 5.0;
111:
112:
113: public static final double DEFAULT_ARROW_WIDTH = 3.0;
114:
115:
116: private double angle;
117:
118:
122: private double tipRadius;
123:
124:
128: private double baseRadius;
129:
130:
131: private double arrowLength;
132:
133:
134: private double arrowWidth;
135:
136:
137: private transient Stroke arrowStroke;
138:
139:
140: private transient Paint arrowPaint;
141:
142:
143: private double labelOffset;
144:
145:
153: public XYPointerAnnotation(String label, double x, double y, double angle) {
154:
155: super(label, x, y);
156: this.angle = angle;
157: this.tipRadius = DEFAULT_TIP_RADIUS;
158: this.baseRadius = DEFAULT_BASE_RADIUS;
159: this.arrowLength = DEFAULT_ARROW_LENGTH;
160: this.arrowWidth = DEFAULT_ARROW_WIDTH;
161: this.labelOffset = DEFAULT_LABEL_OFFSET;
162: this.arrowStroke = new BasicStroke(1.0f);
163: this.arrowPaint = Color.black;
164:
165: }
166:
167:
174: public double getAngle() {
175: return this.angle;
176: }
177:
178:
185: public void setAngle(double angle) {
186: this.angle = angle;
187: }
188:
189:
196: public double getTipRadius() {
197: return this.tipRadius;
198: }
199:
200:
207: public void setTipRadius(double radius) {
208: this.tipRadius = radius;
209: }
210:
211:
218: public double getBaseRadius() {
219: return this.baseRadius;
220: }
221:
222:
229: public void setBaseRadius(double radius) {
230: this.baseRadius = radius;
231: }
232:
233:
240: public double getLabelOffset() {
241: return this.labelOffset;
242: }
243:
244:
252: public void setLabelOffset(double offset) {
253: this.labelOffset = offset;
254: }
255:
256:
263: public double getArrowLength() {
264: return this.arrowLength;
265: }
266:
267:
274: public void setArrowLength(double length) {
275: this.arrowLength = length;
276: }
277:
278:
285: public double getArrowWidth() {
286: return this.arrowWidth;
287: }
288:
289:
296: public void setArrowWidth(double width) {
297: this.arrowWidth = width;
298: }
299:
300:
307: public Stroke getArrowStroke() {
308: return this.arrowStroke;
309: }
310:
311:
318: public void setArrowStroke(Stroke stroke) {
319: if (stroke == null) {
320: throw new IllegalArgumentException("Null 'stroke' not permitted.");
321: }
322: this.arrowStroke = stroke;
323: }
324:
325:
332: public Paint getArrowPaint() {
333: return this.arrowPaint;
334: }
335:
336:
343: public void setArrowPaint(Paint paint) {
344: if (paint == null) {
345: throw new IllegalArgumentException("Null 'paint' argument.");
346: }
347: this.arrowPaint = paint;
348: }
349:
350:
361: public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
362: ValueAxis domainAxis, ValueAxis rangeAxis,
363: int rendererIndex,
364: PlotRenderingInfo info) {
365:
366: PlotOrientation orientation = plot.getOrientation();
367: RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
368: plot.getDomainAxisLocation(), orientation);
369: RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
370: plot.getRangeAxisLocation(), orientation);
371: double j2DX = domainAxis.valueToJava2D(getX(), dataArea, domainEdge);
372: double j2DY = rangeAxis.valueToJava2D(getY(), dataArea, rangeEdge);
373: if (orientation == PlotOrientation.HORIZONTAL) {
374: double temp = j2DX;
375: j2DX = j2DY;
376: j2DY = temp;
377: }
378: double startX = j2DX + Math.cos(this.angle) * this.baseRadius;
379: double startY = j2DY + Math.sin(this.angle) * this.baseRadius;
380:
381: double endX = j2DX + Math.cos(this.angle) * this.tipRadius;
382: double endY = j2DY + Math.sin(this.angle) * this.tipRadius;
383:
384: double arrowBaseX = endX + Math.cos(this.angle) * this.arrowLength;
385: double arrowBaseY = endY + Math.sin(this.angle) * this.arrowLength;
386:
387: double arrowLeftX = arrowBaseX
388: + Math.cos(this.angle + Math.PI / 2.0) * this.arrowWidth;
389: double arrowLeftY = arrowBaseY
390: + Math.sin(this.angle + Math.PI / 2.0) * this.arrowWidth;
391:
392: double arrowRightX = arrowBaseX
393: - Math.cos(this.angle + Math.PI / 2.0) * this.arrowWidth;
394: double arrowRightY = arrowBaseY
395: - Math.sin(this.angle + Math.PI / 2.0) * this.arrowWidth;
396:
397: GeneralPath arrow = new GeneralPath();
398: arrow.moveTo((float) endX, (float) endY);
399: arrow.lineTo((float) arrowLeftX, (float) arrowLeftY);
400: arrow.lineTo((float) arrowRightX, (float) arrowRightY);
401: arrow.closePath();
402:
403: g2.setStroke(this.arrowStroke);
404: g2.setPaint(this.arrowPaint);
405: Line2D line = new Line2D.Double(startX, startY, endX, endY);
406: g2.draw(line);
407: g2.fill(arrow);
408:
409:
410: g2.setFont(getFont());
411: g2.setPaint(getPaint());
412: double labelX = j2DX
413: + Math.cos(this.angle) * (this.baseRadius + this.labelOffset);
414: double labelY = j2DY
415: + Math.sin(this.angle) * (this.baseRadius + this.labelOffset);
416: Rectangle2D hotspot = TextUtilities.drawAlignedString(getText(),
417: g2, (float) labelX, (float) labelY, getTextAnchor());
418:
419: String toolTip = getToolTipText();
420: String url = getURL();
421: if (toolTip != null || url != null) {
422: addEntity(info, hotspot, rendererIndex, toolTip, url);
423: }
424:
425: }
426:
427:
434: public boolean equals(Object obj) {
435: if (obj == this) {
436: return true;
437: }
438: if (!(obj instanceof XYPointerAnnotation)) {
439: return false;
440: }
441: if (!super.equals(obj)) {
442: return false;
443: }
444: XYPointerAnnotation that = (XYPointerAnnotation) obj;
445: if (this.angle != that.angle) {
446: return false;
447: }
448: if (this.tipRadius != that.tipRadius) {
449: return false;
450: }
451: if (this.baseRadius != that.baseRadius) {
452: return false;
453: }
454: if (this.arrowLength != that.arrowLength) {
455: return false;
456: }
457: if (this.arrowWidth != that.arrowWidth) {
458: return false;
459: }
460: if (!this.arrowPaint.equals(that.arrowPaint)) {
461: return false;
462: }
463: if (!ObjectUtilities.equal(this.arrowStroke, that.arrowStroke)) {
464: return false;
465: }
466: if (this.labelOffset != that.labelOffset) {
467: return false;
468: }
469: return true;
470: }
471:
472:
477: public int hashCode() {
478: int result = super.hashCode();
479: long temp = Double.doubleToLongBits(this.angle);
480: result = 37 * result + (int) (temp ^ (temp >>> 32));
481: temp = Double.doubleToLongBits(this.tipRadius);
482: result = 37 * result + (int) (temp ^ (temp >>> 32));
483: temp = Double.doubleToLongBits(this.baseRadius);
484: result = 37 * result + (int) (temp ^ (temp >>> 32));
485: temp = Double.doubleToLongBits(this.arrowLength);
486: result = 37 * result + (int) (temp ^ (temp >>> 32));
487: temp = Double.doubleToLongBits(this.arrowWidth);
488: result = 37 * result + (int) (temp ^ (temp >>> 32));
489: result = result * 37 + HashUtilities.hashCodeForPaint(this.arrowPaint);
490: result = result * 37 + this.arrowStroke.hashCode();
491: temp = Double.doubleToLongBits(this.labelOffset);
492: result = 37 * result + (int) (temp ^ (temp >>> 32));
493: return super.hashCode();
494: }
495:
496:
503: public Object clone() throws CloneNotSupportedException {
504: return super.clone();
505: }
506:
507:
514: private void writeObject(ObjectOutputStream stream) throws IOException {
515: stream.defaultWriteObject();
516: SerialUtilities.writePaint(this.arrowPaint, stream);
517: SerialUtilities.writeStroke(this.arrowStroke, stream);
518: }
519:
520:
528: private void readObject(ObjectInputStream stream)
529: throws IOException, ClassNotFoundException {
530: stream.defaultReadObject();
531: this.arrowPaint = SerialUtilities.readPaint(stream);
532: this.arrowStroke = SerialUtilities.readStroke(stream);
533: }
534:
535: }