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: * StandardDialFrame.java
029: * ----------------------
030: * (C) Copyright 2006, 2007, by Object Refinery Limited.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): -;
034: *
035: * $Id: StandardDialFrame.java,v 1.1.2.4 2007/03/08 16:51:07 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 03-Nov-2006 : Version 1 (DG);
040: * 08-Mar-2007 : Fix in hashCode() (DG);
041: *
042: */
043:
044: package org.jfree.experimental.chart.plot.dial;
045:
046: import java.awt.BasicStroke;
047: import java.awt.Color;
048: import java.awt.Graphics2D;
049: import java.awt.Paint;
050: import java.awt.Shape;
051: import java.awt.Stroke;
052: import java.awt.geom.Arc2D;
053: import java.awt.geom.Area;
054: import java.awt.geom.GeneralPath;
055: import java.awt.geom.Point2D;
056: import java.awt.geom.Rectangle2D;
057: import java.io.IOException;
058: import java.io.ObjectInputStream;
059: import java.io.ObjectOutputStream;
060: import java.io.Serializable;
061:
062: import org.jfree.chart.HashUtilities;
063: import org.jfree.io.SerialUtilities;
064: import org.jfree.util.PaintUtilities;
065: import org.jfree.util.PublicCloneable;
066:
067: /**
068: * A standard frame for the {@link DialPlot} class.
069: */
070: public class StandardDialFrame extends AbstractDialLayer implements
071: DialFrame, Cloneable, PublicCloneable, Serializable {
072:
073: /**
074: * The color used for the front of the panel. This field is transient
075: * because it requires special handling for serialization.
076: */
077: private transient Paint backgroundPaint;
078:
079: /**
080: * The color used for the border around the window. This field is transient
081: * because it requires special handling for serialization.
082: */
083: private transient Paint foregroundPaint;
084:
085: /**
086: * The stroke for drawing the frame outline. This field is transient
087: * because it requires special handling for serialization.
088: */
089: private transient Stroke stroke;
090:
091: /**
092: * The start angle.
093: */
094: private double startAngle;
095:
096: /**
097: * The end angle.
098: */
099: private double extent;
100:
101: /** The inner radius, relative to the framing rectangle. */
102: private double innerRadius;
103:
104: /** The outer radius, relative to the framing rectangle. */
105: private double outerRadius;
106:
107: /**
108: * Creates a new instance of <code>StandardDialFrame</code> that spans
109: * 180 degrees.
110: */
111: public StandardDialFrame() {
112: this (0, 180);
113: }
114:
115: /**
116: * Creates a new instance of <code>StandardDialFrame</code> that spans
117: * the arc specified.
118: *
119: * @param startAngle the startAngle (in degrees).
120: * @param extent the extent of the arc (in degrees, counter-clockwise).
121: */
122: public StandardDialFrame(double startAngle, double extent) {
123: this .backgroundPaint = Color.gray;
124: this .foregroundPaint = new Color(100, 100, 150);
125: this .stroke = new BasicStroke(2.0f);
126: this .innerRadius = 0.25;
127: this .outerRadius = 0.75;
128: this .startAngle = startAngle;
129: this .extent = extent;
130: }
131:
132: /**
133: * Returns the background paint (never <code>null</code>).
134: *
135: * @return The background paint.
136: *
137: * @see #setBackgroundPaint(Paint)
138: */
139: public Paint getBackgroundPaint() {
140: return this .backgroundPaint;
141: }
142:
143: /**
144: * Sets the background paint.
145: *
146: * @param paint the paint (<code>null</code> not permitted).
147: *
148: * @see #getBackgroundPaint()
149: */
150: public void setBackgroundPaint(Paint paint) {
151: if (paint == null) {
152: throw new IllegalArgumentException("Null 'paint' argument.");
153: }
154: this .backgroundPaint = paint;
155: notifyListeners(new DialLayerChangeEvent(this ));
156: }
157:
158: /**
159: * Returns the foreground paint.
160: *
161: * @return The foreground paint (never <code>null</code>).
162: *
163: * @see #setForegroundPaint(Paint)
164: */
165: public Paint getForegroundPaint() {
166: return this .foregroundPaint;
167: }
168:
169: /**
170: * Sets the foreground paint.
171: *
172: * @param paint the paint (<code>null</code> not permitted).
173: *
174: * @see #getForegroundPaint()
175: */
176: public void setForegroundPaint(Paint paint) {
177: if (paint == null) {
178: throw new IllegalArgumentException("Null 'paint' argument.");
179: }
180: this .foregroundPaint = paint;
181: notifyListeners(new DialLayerChangeEvent(this ));
182: }
183:
184: /**
185: * Returns the stroke.
186: *
187: * @return The stroke (never <code>null</code>).
188: *
189: * @see #setStroke(Stroke)
190: */
191: public Stroke getStroke() {
192: return this .stroke;
193: }
194:
195: /**
196: * Sets the stroke.
197: *
198: * @param stroke the stroke (<code>null</code> not permitted).
199: *
200: * @see #getStroke()
201: */
202: public void setStroke(Stroke stroke) {
203: if (stroke == null) {
204: throw new IllegalArgumentException(
205: "Null 'stroke' argument.");
206: }
207: this .stroke = stroke;
208: notifyListeners(new DialLayerChangeEvent(this ));
209: }
210:
211: /**
212: * Returns the inner radius, relative to the framing rectangle.
213: *
214: * @return The inner radius.
215: *
216: * @see #setInnerRadius(double)
217: */
218: public double getInnerRadius() {
219: return this .innerRadius;
220: }
221:
222: /**
223: * Sets the inner radius.
224: *
225: * @param radius the inner radius.
226: *
227: * @see #getInnerRadius()
228: */
229: public void setInnerRadius(double radius) {
230: // TODO: validation
231: this .innerRadius = radius;
232: notifyListeners(new DialLayerChangeEvent(this ));
233: }
234:
235: /**
236: * Returns the outer radius, relative to the framing rectangle.
237: *
238: * @return The outer radius.
239: *
240: * @see #setOuterRadius(double)
241: */
242: public double getOuterRadius() {
243: return this .outerRadius;
244: }
245:
246: /**
247: * Sets the outer radius.
248: *
249: * @param radius the outer radius.
250: *
251: * @see #getOuterRadius()
252: */
253: public void setOuterRadius(double radius) {
254: // TODO: validation
255: this .outerRadius = radius;
256: notifyListeners(new DialLayerChangeEvent(this ));
257: }
258:
259: /**
260: * Returns the start angle.
261: *
262: * @return The start angle.
263: *
264: * @see #setStartAngle(double)
265: */
266: public double getStartAngle() {
267: return this .startAngle;
268: }
269:
270: /**
271: * Sets the start angle.
272: *
273: * @param angle the angle.
274: *
275: * @see #getStartAngle()
276: */
277: public void setStartAngle(double angle) {
278: this .startAngle = angle;
279: notifyListeners(new DialLayerChangeEvent(this ));
280: }
281:
282: /**
283: * Returns the extent.
284: *
285: * @return The extent.
286: *
287: * @see #setExtent(double)
288: */
289: public double getExtent() {
290: return this .extent;
291: }
292:
293: /**
294: * Sets the extent.
295: *
296: * @param extent the extent.
297: *
298: * @see #getExtent()
299: */
300: public void setExtent(double extent) {
301: this .extent = extent;
302: notifyListeners(new DialLayerChangeEvent(this ));
303: }
304:
305: /**
306: * Returns the shape for the window for this dial. Some dial layers will
307: * request that their drawing be clipped within this window.
308: *
309: * @param frame the reference frame (<code>null</code> not permitted).
310: *
311: * @return The shape of the dial's window.
312: */
313: public Shape getWindow(Rectangle2D frame) {
314:
315: Rectangle2D innerFrame = DialPlot.rectangleByRadius(frame,
316: this .innerRadius, this .innerRadius);
317: Rectangle2D outerFrame = DialPlot.rectangleByRadius(frame,
318: this .outerRadius, this .outerRadius);
319: Arc2D inner = new Arc2D.Double(innerFrame, this .startAngle,
320: this .extent, Arc2D.OPEN);
321: Arc2D outer = new Arc2D.Double(outerFrame, this .startAngle
322: + this .extent, -this .extent, Arc2D.OPEN);
323: GeneralPath p = new GeneralPath();
324: Point2D point1 = inner.getStartPoint();
325: p.moveTo((float) point1.getX(), (float) point1.getY());
326: p.append(inner, true);
327: p.append(outer, true);
328: p.closePath();
329: return p;
330:
331: }
332:
333: protected Shape getOuterWindow(Rectangle2D frame) {
334: double radiusMargin = 0.02;
335: double angleMargin = 1.5;
336: Rectangle2D innerFrame = DialPlot.rectangleByRadius(frame,
337: this .innerRadius - radiusMargin, this .innerRadius
338: - radiusMargin);
339: Rectangle2D outerFrame = DialPlot.rectangleByRadius(frame,
340: this .outerRadius + radiusMargin, this .outerRadius
341: + radiusMargin);
342: Arc2D inner = new Arc2D.Double(innerFrame, this .startAngle
343: - angleMargin, this .extent + 2 * angleMargin,
344: Arc2D.OPEN);
345: Arc2D outer = new Arc2D.Double(outerFrame, this .startAngle
346: + angleMargin + this .extent, -this .extent - 2
347: * angleMargin, Arc2D.OPEN);
348: GeneralPath p = new GeneralPath();
349: Point2D point1 = inner.getStartPoint();
350: p.moveTo((float) point1.getX(), (float) point1.getY());
351: p.append(inner, true);
352: p.append(outer, true);
353: p.closePath();
354: return p;
355:
356: }
357:
358: /**
359: * Draws the frame.
360: *
361: * @param g2 the graphics target.
362: * @param plot the plot.
363: * @param frame the dial's reference frame.
364: * @param view the dial's view rectangle.
365: */
366: public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame,
367: Rectangle2D view) {
368:
369: Shape window = getWindow(frame);
370: Shape outerWindow = getOuterWindow(frame);
371:
372: Area area1 = new Area(outerWindow);
373: Area area2 = new Area(window);
374: area1.subtract(area2);
375: g2.setPaint(Color.lightGray);
376: g2.fill(area1);
377:
378: g2.setStroke(this .stroke);
379: g2.setPaint(this .foregroundPaint);
380: g2.draw(window);
381: g2.draw(outerWindow);
382:
383: }
384:
385: /**
386: * Returns <code>false</code> to indicate that this dial layer is not
387: * clipped to the dial window.
388: *
389: * @return <code>false</code>.
390: */
391: public boolean isClippedToWindow() {
392: return false;
393: }
394:
395: /**
396: * Tests this instance for equality with an arbitrary object.
397: *
398: * @param obj the object (<code>null</code> permitted).
399: *
400: * @return A boolean.
401: */
402: public boolean equals(Object obj) {
403: if (obj == this ) {
404: return true;
405: }
406: if (!(obj instanceof StandardDialFrame)) {
407: return false;
408: }
409: StandardDialFrame that = (StandardDialFrame) obj;
410: if (!PaintUtilities.equal(this .backgroundPaint,
411: that.backgroundPaint)) {
412: return false;
413: }
414: if (!PaintUtilities.equal(this .foregroundPaint,
415: that.foregroundPaint)) {
416: return false;
417: }
418: if (this .startAngle != that.startAngle) {
419: return false;
420: }
421: if (this .extent != that.extent) {
422: return false;
423: }
424: if (this .innerRadius != that.innerRadius) {
425: return false;
426: }
427: if (this .outerRadius != that.outerRadius) {
428: return false;
429: }
430: if (!this .stroke.equals(that.stroke)) {
431: return false;
432: }
433: return true;
434: }
435:
436: /**
437: * Returns a hash code for this instance.
438: *
439: * @return The hash code.
440: */
441: public int hashCode() {
442: int result = 193;
443: long temp = Double.doubleToLongBits(this .startAngle);
444: result = 37 * result + (int) (temp ^ (temp >>> 32));
445: temp = Double.doubleToLongBits(this .extent);
446: result = 37 * result + (int) (temp ^ (temp >>> 32));
447: temp = Double.doubleToLongBits(this .innerRadius);
448: result = 37 * result + (int) (temp ^ (temp >>> 32));
449: temp = Double.doubleToLongBits(this .outerRadius);
450: result = 37 * result + (int) (temp ^ (temp >>> 32));
451: result = 37 * result
452: + HashUtilities.hashCodeForPaint(this .backgroundPaint);
453: result = 37 * result
454: + HashUtilities.hashCodeForPaint(this .foregroundPaint);
455: result = 37 * result + this .stroke.hashCode();
456: return result;
457: }
458:
459: /**
460: * Returns a clone of this instance.
461: *
462: * @return A clone.
463: *
464: * @throws CloneNotSupportedException if any attribute of this instance
465: * cannot be cloned.
466: */
467: public Object clone() throws CloneNotSupportedException {
468: return super .clone();
469: }
470:
471: /**
472: * Provides serialization support.
473: *
474: * @param stream the output stream.
475: *
476: * @throws IOException if there is an I/O error.
477: */
478: private void writeObject(ObjectOutputStream stream)
479: throws IOException {
480: stream.defaultWriteObject();
481: SerialUtilities.writePaint(this .backgroundPaint, stream);
482: SerialUtilities.writePaint(this .foregroundPaint, stream);
483: SerialUtilities.writeStroke(this .stroke, stream);
484: }
485:
486: /**
487: * Provides serialization support.
488: *
489: * @param stream the input stream.
490: *
491: * @throws IOException if there is an I/O error.
492: * @throws ClassNotFoundException if there is a classpath problem.
493: */
494: private void readObject(ObjectInputStream stream)
495: throws IOException, ClassNotFoundException {
496: stream.defaultReadObject();
497: this.backgroundPaint = SerialUtilities.readPaint(stream);
498: this.foregroundPaint = SerialUtilities.readPaint(stream);
499: this.stroke = SerialUtilities.readStroke(stream);
500: }
501:
502: }
|