001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2007, 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: * CompassPlot.java
029: * ----------------
030: * (C) Copyright 2002-2007, by the Australian Antarctic Division and
031: * Contributors.
032: *
033: * Original Author: Bryan Scott (for the Australian Antarctic Division);
034: * Contributor(s): David Gilbert (for Object Refinery Limited);
035: * Arnaud Lelievre;
036: *
037: * $Id: CompassPlot.java,v 1.11.2.6 2007/03/20 21:52:16 mungady Exp $
038: *
039: * Changes:
040: * --------
041: * 25-Sep-2002 : Version 1, contributed by Bryan Scott (DG);
042: * 23-Jan-2003 : Removed one constructor (DG);
043: * 26-Mar-2003 : Implemented Serializable (DG);
044: * 27-Mar-2003 : Changed MeterDataset to ValueDataset (DG);
045: * 21-Aug-2003 : Implemented Cloneable (DG);
046: * 08-Sep-2003 : Added internationalization via use of properties
047: * resourceBundle (RFE 690236) (AL);
048: * 09-Sep-2003 : Changed Color --> Paint (DG);
049: * 15-Sep-2003 : Added null data value check (bug report 805009) (DG);
050: * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
051: * 16-Mar-2004 : Added support for revolutionDistance to enable support for
052: * other units than degrees.
053: * 16-Mar-2004 : Enabled LongNeedle to rotate about center.
054: * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
055: * 17-Apr-2005 : Fixed bug in clone() method (DG);
056: * 05-May-2005 : Updated draw() method parameters (DG);
057: * 08-Jun-2005 : Fixed equals() method to handle GradientPaint (DG);
058: * 16-Jun-2005 : Renamed getData() --> getDatasets() and
059: * addData() --> addDataset() (DG);
060: * ------------- JFREECHART 1.0.x ---------------------------------------------
061: * 20-Mar-2007 : Fixed serialization (DG);
062: *
063: */
064:
065: package org.jfree.chart.plot;
066:
067: import java.awt.BasicStroke;
068: import java.awt.Color;
069: import java.awt.Font;
070: import java.awt.Graphics2D;
071: import java.awt.Paint;
072: import java.awt.Polygon;
073: import java.awt.Stroke;
074: import java.awt.geom.Area;
075: import java.awt.geom.Ellipse2D;
076: import java.awt.geom.Point2D;
077: import java.awt.geom.Rectangle2D;
078: import java.io.IOException;
079: import java.io.ObjectInputStream;
080: import java.io.ObjectOutputStream;
081: import java.io.Serializable;
082: import java.util.Arrays;
083: import java.util.ResourceBundle;
084:
085: import org.jfree.chart.LegendItemCollection;
086: import org.jfree.chart.event.PlotChangeEvent;
087: import org.jfree.chart.needle.ArrowNeedle;
088: import org.jfree.chart.needle.LineNeedle;
089: import org.jfree.chart.needle.LongNeedle;
090: import org.jfree.chart.needle.MeterNeedle;
091: import org.jfree.chart.needle.MiddlePinNeedle;
092: import org.jfree.chart.needle.PinNeedle;
093: import org.jfree.chart.needle.PlumNeedle;
094: import org.jfree.chart.needle.PointerNeedle;
095: import org.jfree.chart.needle.ShipNeedle;
096: import org.jfree.chart.needle.WindNeedle;
097: import org.jfree.data.general.DefaultValueDataset;
098: import org.jfree.data.general.ValueDataset;
099: import org.jfree.io.SerialUtilities;
100: import org.jfree.ui.RectangleInsets;
101: import org.jfree.util.ObjectUtilities;
102: import org.jfree.util.PaintUtilities;
103:
104: /**
105: * A specialised plot that draws a compass to indicate a direction based on the
106: * value from a {@link ValueDataset}.
107: */
108: public class CompassPlot extends Plot implements Cloneable,
109: Serializable {
110:
111: /** For serialization. */
112: private static final long serialVersionUID = 6924382802125527395L;
113:
114: /** The default label font. */
115: public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif",
116: Font.BOLD, 10);
117:
118: /** A constant for the label type. */
119: public static final int NO_LABELS = 0;
120:
121: /** A constant for the label type. */
122: public static final int VALUE_LABELS = 1;
123:
124: /** The label type (NO_LABELS, VALUE_LABELS). */
125: private int labelType;
126:
127: /** The label font. */
128: private Font labelFont;
129:
130: /** A flag that controls whether or not a border is drawn. */
131: private boolean drawBorder = false;
132:
133: /** The rose highlight paint. */
134: private transient Paint roseHighlightPaint = Color.black;
135:
136: /** The rose paint. */
137: private transient Paint rosePaint = Color.yellow;
138:
139: /** The rose center paint. */
140: private transient Paint roseCenterPaint = Color.white;
141:
142: /** The compass font. */
143: private Font compassFont = new Font("Arial", Font.PLAIN, 10);
144:
145: /** A working shape. */
146: private transient Ellipse2D circle1;
147:
148: /** A working shape. */
149: private transient Ellipse2D circle2;
150:
151: /** A working area. */
152: private transient Area a1;
153:
154: /** A working area. */
155: private transient Area a2;
156:
157: /** A working shape. */
158: private transient Rectangle2D rect1;
159:
160: /** An array of value datasets. */
161: private ValueDataset[] datasets = new ValueDataset[1];
162:
163: /** An array of needles. */
164: private MeterNeedle[] seriesNeedle = new MeterNeedle[1];
165:
166: /** The resourceBundle for the localization. */
167: protected static ResourceBundle localizationResources = ResourceBundle
168: .getBundle("org.jfree.chart.plot.LocalizationBundle");
169:
170: /**
171: * The count to complete one revolution. Can be arbitrarily set
172: * For degrees (the default) it is 360, for radians this is 2*Pi, etc
173: */
174: protected double revolutionDistance = 360;
175:
176: /**
177: * Default constructor.
178: */
179: public CompassPlot() {
180: this (new DefaultValueDataset());
181: }
182:
183: /**
184: * Constructs a new compass plot.
185: *
186: * @param dataset the dataset for the plot (<code>null</code> permitted).
187: */
188: public CompassPlot(ValueDataset dataset) {
189: super ();
190: if (dataset != null) {
191: this .datasets[0] = dataset;
192: dataset.addChangeListener(this );
193: }
194: this .circle1 = new Ellipse2D.Double();
195: this .circle2 = new Ellipse2D.Double();
196: this .rect1 = new Rectangle2D.Double();
197: setSeriesNeedle(0);
198: }
199:
200: /**
201: * Returns the label type. Defined by the constants: {@link #NO_LABELS}
202: * and {@link #VALUE_LABELS}.
203: *
204: * @return The label type.
205: *
206: * @see #setLabelType(int)
207: */
208: public int getLabelType() {
209: // FIXME: this attribute is never used - deprecate?
210: return this .labelType;
211: }
212:
213: /**
214: * Sets the label type (either {@link #NO_LABELS} or {@link #VALUE_LABELS}.
215: *
216: * @param type the type.
217: *
218: * @see #getLabelType()
219: */
220: public void setLabelType(int type) {
221: // FIXME: this attribute is never used - deprecate?
222: if ((type != NO_LABELS) && (type != VALUE_LABELS)) {
223: throw new IllegalArgumentException(
224: "MeterPlot.setLabelType(int): unrecognised type.");
225: }
226: if (this .labelType != type) {
227: this .labelType = type;
228: notifyListeners(new PlotChangeEvent(this ));
229: }
230: }
231:
232: /**
233: * Returns the label font.
234: *
235: * @return The label font.
236: *
237: * @see #setLabelFont(Font)
238: */
239: public Font getLabelFont() {
240: // FIXME: this attribute is not used - deprecate?
241: return this .labelFont;
242: }
243:
244: /**
245: * Sets the label font and sends a {@link PlotChangeEvent} to all
246: * registered listeners.
247: *
248: * @param font the new label font.
249: *
250: * @see #getLabelFont()
251: */
252: public void setLabelFont(Font font) {
253: // FIXME: this attribute is not used - deprecate?
254: if (font == null) {
255: throw new IllegalArgumentException(
256: "Null 'font' not allowed.");
257: }
258: this .labelFont = font;
259: notifyListeners(new PlotChangeEvent(this ));
260: }
261:
262: /**
263: * Returns the paint used to fill the outer circle of the compass.
264: *
265: * @return The paint (never <code>null</code>).
266: *
267: * @see #setRosePaint(Paint)
268: */
269: public Paint getRosePaint() {
270: return this .rosePaint;
271: }
272:
273: /**
274: * Sets the paint used to fill the outer circle of the compass,
275: * and sends a {@link PlotChangeEvent} to all registered listeners.
276: *
277: * @param paint the paint (<code>null</code> not permitted).
278: *
279: * @see #getRosePaint()
280: */
281: public void setRosePaint(Paint paint) {
282: if (paint == null) {
283: throw new IllegalArgumentException("Null 'paint' argument.");
284: }
285: this .rosePaint = paint;
286: notifyListeners(new PlotChangeEvent(this ));
287: }
288:
289: /**
290: * Returns the paint used to fill the inner background area of the
291: * compass.
292: *
293: * @return The paint (never <code>null</code>).
294: *
295: * @see #setRoseCenterPaint(Paint)
296: */
297: public Paint getRoseCenterPaint() {
298: return this .roseCenterPaint;
299: }
300:
301: /**
302: * Sets the paint used to fill the inner background area of the compass,
303: * and sends a {@link PlotChangeEvent} to all registered listeners.
304: *
305: * @param paint the paint (<code>null</code> not permitted).
306: *
307: * @see #getRoseCenterPaint()
308: */
309: public void setRoseCenterPaint(Paint paint) {
310: if (paint == null) {
311: throw new IllegalArgumentException("Null 'paint' argument.");
312: }
313: this .roseCenterPaint = paint;
314: notifyListeners(new PlotChangeEvent(this ));
315: }
316:
317: /**
318: * Returns the paint used to draw the circles, symbols and labels on the
319: * compass.
320: *
321: * @return The paint (never <code>null</code>).
322: *
323: * @see #setRoseHighlightPaint(Paint)
324: */
325: public Paint getRoseHighlightPaint() {
326: return this .roseHighlightPaint;
327: }
328:
329: /**
330: * Sets the paint used to draw the circles, symbols and labels of the
331: * compass, and sends a {@link PlotChangeEvent} to all registered listeners.
332: *
333: * @param paint the paint (<code>null</code> not permitted).
334: *
335: * @see #getRoseHighlightPaint()
336: */
337: public void setRoseHighlightPaint(Paint paint) {
338: if (paint == null) {
339: throw new IllegalArgumentException("Null 'paint' argument.");
340: }
341: this .roseHighlightPaint = paint;
342: notifyListeners(new PlotChangeEvent(this ));
343: }
344:
345: /**
346: * Returns a flag that controls whether or not a border is drawn.
347: *
348: * @return The flag.
349: *
350: * @see #setDrawBorder(boolean)
351: */
352: public boolean getDrawBorder() {
353: return this .drawBorder;
354: }
355:
356: /**
357: * Sets a flag that controls whether or not a border is drawn.
358: *
359: * @param status the flag status.
360: *
361: * @see #getDrawBorder()
362: */
363: public void setDrawBorder(boolean status) {
364: this .drawBorder = status;
365: notifyListeners(new PlotChangeEvent(this ));
366: }
367:
368: /**
369: * Sets the series paint.
370: *
371: * @param series the series index.
372: * @param paint the paint.
373: *
374: * @see #setSeriesOutlinePaint(int, Paint)
375: */
376: public void setSeriesPaint(int series, Paint paint) {
377: // super.setSeriesPaint(series, paint);
378: if ((series >= 0) && (series < this .seriesNeedle.length)) {
379: this .seriesNeedle[series].setFillPaint(paint);
380: }
381: }
382:
383: /**
384: * Sets the series outline paint.
385: *
386: * @param series the series index.
387: * @param p the paint.
388: *
389: * @see #setSeriesPaint(int, Paint)
390: */
391: public void setSeriesOutlinePaint(int series, Paint p) {
392:
393: if ((series >= 0) && (series < this .seriesNeedle.length)) {
394: this .seriesNeedle[series].setOutlinePaint(p);
395: }
396:
397: }
398:
399: /**
400: * Sets the series outline stroke.
401: *
402: * @param series the series index.
403: * @param stroke the stroke.
404: *
405: * @see #setSeriesOutlinePaint(int, Paint)
406: */
407: public void setSeriesOutlineStroke(int series, Stroke stroke) {
408:
409: if ((series >= 0) && (series < this .seriesNeedle.length)) {
410: this .seriesNeedle[series].setOutlineStroke(stroke);
411: }
412:
413: }
414:
415: /**
416: * Sets the needle type.
417: *
418: * @param type the type.
419: *
420: * @see #setSeriesNeedle(int, int)
421: */
422: public void setSeriesNeedle(int type) {
423: setSeriesNeedle(0, type);
424: }
425:
426: /**
427: * Sets the needle for a series. The needle type is one of the following:
428: * <ul>
429: * <li>0 = {@link ArrowNeedle};</li>
430: * <li>1 = {@link LineNeedle};</li>
431: * <li>2 = {@link LongNeedle};</li>
432: * <li>3 = {@link PinNeedle};</li>
433: * <li>4 = {@link PlumNeedle};</li>
434: * <li>5 = {@link PointerNeedle};</li>
435: * <li>6 = {@link ShipNeedle};</li>
436: * <li>7 = {@link WindNeedle};</li>
437: * <li>8 = {@link ArrowNeedle};</li>
438: * <li>9 = {@link MiddlePinNeedle};</li>
439: * </ul>
440: * @param index the series index.
441: * @param type the needle type.
442: *
443: * @see #setSeriesNeedle(int)
444: */
445: public void setSeriesNeedle(int index, int type) {
446: switch (type) {
447: case 0:
448: setSeriesNeedle(index, new ArrowNeedle(true));
449: setSeriesPaint(index, Color.red);
450: this .seriesNeedle[index].setHighlightPaint(Color.white);
451: break;
452: case 1:
453: setSeriesNeedle(index, new LineNeedle());
454: break;
455: case 2:
456: MeterNeedle longNeedle = new LongNeedle();
457: longNeedle.setRotateY(0.5);
458: setSeriesNeedle(index, longNeedle);
459: break;
460: case 3:
461: setSeriesNeedle(index, new PinNeedle());
462: break;
463: case 4:
464: setSeriesNeedle(index, new PlumNeedle());
465: break;
466: case 5:
467: setSeriesNeedle(index, new PointerNeedle());
468: break;
469: case 6:
470: setSeriesPaint(index, null);
471: setSeriesOutlineStroke(index, new BasicStroke(3));
472: setSeriesNeedle(index, new ShipNeedle());
473: break;
474: case 7:
475: setSeriesPaint(index, Color.blue);
476: setSeriesNeedle(index, new WindNeedle());
477: break;
478: case 8:
479: setSeriesNeedle(index, new ArrowNeedle(true));
480: break;
481: case 9:
482: setSeriesNeedle(index, new MiddlePinNeedle());
483: break;
484:
485: default:
486: throw new IllegalArgumentException("Unrecognised type.");
487: }
488:
489: }
490:
491: /**
492: * Sets the needle for a series and sends a {@link PlotChangeEvent} to all
493: * registered listeners.
494: *
495: * @param index the series index.
496: * @param needle the needle.
497: */
498: public void setSeriesNeedle(int index, MeterNeedle needle) {
499:
500: if ((needle != null) && (index < this .seriesNeedle.length)) {
501: this .seriesNeedle[index] = needle;
502: }
503: notifyListeners(new PlotChangeEvent(this ));
504:
505: }
506:
507: /**
508: * Returns an array of dataset references for the plot.
509: *
510: * @return The dataset for the plot, cast as a ValueDataset.
511: *
512: * @see #addDataset(ValueDataset)
513: */
514: public ValueDataset[] getDatasets() {
515: return this .datasets;
516: }
517:
518: /**
519: * Adds a dataset to the compass.
520: *
521: * @param dataset the new dataset (<code>null</code> ignored).
522: *
523: * @see #addDataset(ValueDataset, MeterNeedle)
524: */
525: public void addDataset(ValueDataset dataset) {
526: addDataset(dataset, null);
527: }
528:
529: /**
530: * Adds a dataset to the compass.
531: *
532: * @param dataset the new dataset (<code>null</code> ignored).
533: * @param needle the needle (<code>null</code> permitted).
534: */
535: public void addDataset(ValueDataset dataset, MeterNeedle needle) {
536:
537: if (dataset != null) {
538: int i = this .datasets.length + 1;
539: ValueDataset[] t = new ValueDataset[i];
540: MeterNeedle[] p = new MeterNeedle[i];
541: i = i - 2;
542: for (; i >= 0; --i) {
543: t[i] = this .datasets[i];
544: p[i] = this .seriesNeedle[i];
545: }
546: i = this .datasets.length;
547: t[i] = dataset;
548: p[i] = ((needle != null) ? needle : p[i - 1]);
549:
550: ValueDataset[] a = this .datasets;
551: MeterNeedle[] b = this .seriesNeedle;
552: this .datasets = t;
553: this .seriesNeedle = p;
554:
555: for (--i; i >= 0; --i) {
556: a[i] = null;
557: b[i] = null;
558: }
559: dataset.addChangeListener(this );
560: }
561: }
562:
563: /**
564: * Draws the plot on a Java 2D graphics device (such as the screen or a
565: * printer).
566: *
567: * @param g2 the graphics device.
568: * @param area the area within which the plot should be drawn.
569: * @param anchor the anchor point (<code>null</code> permitted).
570: * @param parentState the state from the parent plot, if there is one.
571: * @param info collects info about the drawing.
572: */
573: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
574: PlotState parentState, PlotRenderingInfo info) {
575:
576: int outerRadius = 0;
577: int innerRadius = 0;
578: int x1, y1, x2, y2;
579: double a;
580:
581: if (info != null) {
582: info.setPlotArea(area);
583: }
584:
585: // adjust for insets...
586: RectangleInsets insets = getInsets();
587: insets.trim(area);
588:
589: // draw the background
590: if (this .drawBorder) {
591: drawBackground(g2, area);
592: }
593:
594: int midX = (int) (area.getWidth() / 2);
595: int midY = (int) (area.getHeight() / 2);
596: int radius = midX;
597: if (midY < midX) {
598: radius = midY;
599: }
600: --radius;
601: int diameter = 2 * radius;
602:
603: midX += (int) area.getMinX();
604: midY += (int) area.getMinY();
605:
606: this .circle1.setFrame(midX - radius, midY - radius, diameter,
607: diameter);
608: this .circle2.setFrame(midX - radius + 15, midY - radius + 15,
609: diameter - 30, diameter - 30);
610: g2.setPaint(this .rosePaint);
611: this .a1 = new Area(this .circle1);
612: this .a2 = new Area(this .circle2);
613: this .a1.subtract(this .a2);
614: g2.fill(this .a1);
615:
616: g2.setPaint(this .roseCenterPaint);
617: x1 = diameter - 30;
618: g2.fillOval(midX - radius + 15, midY - radius + 15, x1, x1);
619: g2.setPaint(this .roseHighlightPaint);
620: g2.drawOval(midX - radius, midY - radius, diameter, diameter);
621: x1 = diameter - 20;
622: g2.drawOval(midX - radius + 10, midY - radius + 10, x1, x1);
623: x1 = diameter - 30;
624: g2.drawOval(midX - radius + 15, midY - radius + 15, x1, x1);
625: x1 = diameter - 80;
626: g2.drawOval(midX - radius + 40, midY - radius + 40, x1, x1);
627:
628: outerRadius = radius - 20;
629: innerRadius = radius - 32;
630: for (int w = 0; w < 360; w += 15) {
631: a = Math.toRadians(w);
632: x1 = midX - ((int) (Math.sin(a) * innerRadius));
633: x2 = midX - ((int) (Math.sin(a) * outerRadius));
634: y1 = midY - ((int) (Math.cos(a) * innerRadius));
635: y2 = midY - ((int) (Math.cos(a) * outerRadius));
636: g2.drawLine(x1, y1, x2, y2);
637: }
638:
639: g2.setPaint(this .roseHighlightPaint);
640: innerRadius = radius - 26;
641: outerRadius = 7;
642: for (int w = 45; w < 360; w += 90) {
643: a = Math.toRadians(w);
644: x1 = midX - ((int) (Math.sin(a) * innerRadius));
645: y1 = midY - ((int) (Math.cos(a) * innerRadius));
646: g2.fillOval(x1 - outerRadius, y1 - outerRadius,
647: 2 * outerRadius, 2 * outerRadius);
648: }
649:
650: /// Squares
651: for (int w = 0; w < 360; w += 90) {
652: a = Math.toRadians(w);
653: x1 = midX - ((int) (Math.sin(a) * innerRadius));
654: y1 = midY - ((int) (Math.cos(a) * innerRadius));
655:
656: Polygon p = new Polygon();
657: p.addPoint(x1 - outerRadius, y1);
658: p.addPoint(x1, y1 + outerRadius);
659: p.addPoint(x1 + outerRadius, y1);
660: p.addPoint(x1, y1 - outerRadius);
661: g2.fillPolygon(p);
662: }
663:
664: /// Draw N, S, E, W
665: innerRadius = radius - 42;
666: Font f = getCompassFont(radius);
667: g2.setFont(f);
668: g2.drawString("N", midX - 5, midY - innerRadius + f.getSize());
669: g2.drawString("S", midX - 5, midY + innerRadius - 5);
670: g2.drawString("W", midX - innerRadius + 5, midY + 5);
671: g2.drawString("E", midX + innerRadius - f.getSize(), midY + 5);
672:
673: // plot the data (unless the dataset is null)...
674: y1 = radius / 2;
675: x1 = radius / 6;
676: Rectangle2D needleArea = new Rectangle2D.Double((midX - x1),
677: (midY - y1), (2 * x1), (2 * y1));
678: int x = this .seriesNeedle.length;
679: int current = 0;
680: double value = 0;
681: int i = (this .datasets.length - 1);
682: for (; i >= 0; --i) {
683: ValueDataset data = this .datasets[i];
684:
685: if (data != null && data.getValue() != null) {
686: value = (data.getValue().doubleValue())
687: % this .revolutionDistance;
688: value = value / this .revolutionDistance * 360;
689: current = i % x;
690: this .seriesNeedle[current].draw(g2, needleArea, value);
691: }
692: }
693:
694: if (this .drawBorder) {
695: drawOutline(g2, area);
696: }
697:
698: }
699:
700: /**
701: * Returns a short string describing the type of plot.
702: *
703: * @return A string describing the plot.
704: */
705: public String getPlotType() {
706: return localizationResources.getString("Compass_Plot");
707: }
708:
709: /**
710: * Returns the legend items for the plot. For now, no legend is available
711: * - this method returns null.
712: *
713: * @return The legend items.
714: */
715: public LegendItemCollection getLegendItems() {
716: return null;
717: }
718:
719: /**
720: * No zooming is implemented for compass plot, so this method is empty.
721: *
722: * @param percent the zoom amount.
723: */
724: public void zoom(double percent) {
725: // no zooming possible
726: }
727:
728: /**
729: * Returns the font for the compass, adjusted for the size of the plot.
730: *
731: * @param radius the radius.
732: *
733: * @return The font.
734: */
735: protected Font getCompassFont(int radius) {
736: float fontSize = radius / 10.0f;
737: if (fontSize < 8) {
738: fontSize = 8;
739: }
740: Font newFont = this .compassFont.deriveFont(fontSize);
741: return newFont;
742: }
743:
744: /**
745: * Tests an object for equality with this plot.
746: *
747: * @param obj the object (<code>null</code> permitted).
748: *
749: * @return A boolean.
750: */
751: public boolean equals(Object obj) {
752: if (obj == this ) {
753: return true;
754: }
755: if (!(obj instanceof CompassPlot)) {
756: return false;
757: }
758: if (!super .equals(obj)) {
759: return false;
760: }
761: CompassPlot that = (CompassPlot) obj;
762: if (this .labelType != that.labelType) {
763: return false;
764: }
765: if (!ObjectUtilities.equal(this .labelFont, that.labelFont)) {
766: return false;
767: }
768: if (this .drawBorder != that.drawBorder) {
769: return false;
770: }
771: if (!PaintUtilities.equal(this .roseHighlightPaint,
772: that.roseHighlightPaint)) {
773: return false;
774: }
775: if (!PaintUtilities.equal(this .rosePaint, that.rosePaint)) {
776: return false;
777: }
778: if (!PaintUtilities.equal(this .roseCenterPaint,
779: that.roseCenterPaint)) {
780: return false;
781: }
782: if (!ObjectUtilities.equal(this .compassFont, that.compassFont)) {
783: return false;
784: }
785: if (!Arrays.equals(this .seriesNeedle, that.seriesNeedle)) {
786: return false;
787: }
788: if (getRevolutionDistance() != that.getRevolutionDistance()) {
789: return false;
790: }
791: return true;
792:
793: }
794:
795: /**
796: * Returns a clone of the plot.
797: *
798: * @return A clone.
799: *
800: * @throws CloneNotSupportedException this class will not throw this
801: * exception, but subclasses (if any) might.
802: */
803: public Object clone() throws CloneNotSupportedException {
804:
805: CompassPlot clone = (CompassPlot) super .clone();
806: if (this .circle1 != null) {
807: clone.circle1 = (Ellipse2D) this .circle1.clone();
808: }
809: if (this .circle2 != null) {
810: clone.circle2 = (Ellipse2D) this .circle2.clone();
811: }
812: if (this .a1 != null) {
813: clone.a1 = (Area) this .a1.clone();
814: }
815: if (this .a2 != null) {
816: clone.a2 = (Area) this .a2.clone();
817: }
818: if (this .rect1 != null) {
819: clone.rect1 = (Rectangle2D) this .rect1.clone();
820: }
821: clone.datasets = (ValueDataset[]) this .datasets.clone();
822: clone.seriesNeedle = (MeterNeedle[]) this .seriesNeedle.clone();
823:
824: // clone share data sets => add the clone as listener to the dataset
825: for (int i = 0; i < this .datasets.length; ++i) {
826: if (clone.datasets[i] != null) {
827: clone.datasets[i].addChangeListener(clone);
828: }
829: }
830: return clone;
831:
832: }
833:
834: /**
835: * Sets the count to complete one revolution. Can be arbitrarily set
836: * For degrees (the default) it is 360, for radians this is 2*Pi, etc
837: *
838: * @param size the count to complete one revolution.
839: *
840: * @see #getRevolutionDistance()
841: */
842: public void setRevolutionDistance(double size) {
843: if (size > 0) {
844: this .revolutionDistance = size;
845: }
846: }
847:
848: /**
849: * Gets the count to complete one revolution.
850: *
851: * @return The count to complete one revolution.
852: *
853: * @see #setRevolutionDistance(double)
854: */
855: public double getRevolutionDistance() {
856: return this .revolutionDistance;
857: }
858:
859: /**
860: * Provides serialization support.
861: *
862: * @param stream the output stream.
863: *
864: * @throws IOException if there is an I/O error.
865: */
866: private void writeObject(ObjectOutputStream stream)
867: throws IOException {
868: stream.defaultWriteObject();
869: SerialUtilities.writePaint(this .rosePaint, stream);
870: SerialUtilities.writePaint(this .roseCenterPaint, stream);
871: SerialUtilities.writePaint(this .roseHighlightPaint, stream);
872: }
873:
874: /**
875: * Provides serialization support.
876: *
877: * @param stream the input stream.
878: *
879: * @throws IOException if there is an I/O error.
880: * @throws ClassNotFoundException if there is a classpath problem.
881: */
882: private void readObject(ObjectInputStream stream)
883: throws IOException, ClassNotFoundException {
884: stream.defaultReadObject();
885: this.rosePaint = SerialUtilities.readPaint(stream);
886: this.roseCenterPaint = SerialUtilities.readPaint(stream);
887: this.roseHighlightPaint = SerialUtilities.readPaint(stream);
888: }
889:
890: }
|