001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ---------------------- 028 * StandardDialFrame.java 029 * ---------------------- 030 * (C) Copyright 2006-present, by David Gilbert. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): -; 034 * 035 */ 036 037package org.jfree.chart.plot.dial; 038 039import java.awt.BasicStroke; 040import java.awt.Color; 041import java.awt.Graphics2D; 042import java.awt.Paint; 043import java.awt.Shape; 044import java.awt.Stroke; 045import java.awt.geom.Area; 046import java.awt.geom.Ellipse2D; 047import java.awt.geom.Rectangle2D; 048import java.io.IOException; 049import java.io.ObjectInputStream; 050import java.io.ObjectOutputStream; 051import java.io.Serializable; 052 053import org.jfree.chart.HashUtils; 054import org.jfree.chart.util.PaintUtils; 055import org.jfree.chart.util.Args; 056import org.jfree.chart.util.PublicCloneable; 057import org.jfree.chart.util.SerialUtils; 058 059/** 060 * A simple circular frame for the {@link DialPlot} class. 061 */ 062public class StandardDialFrame extends AbstractDialLayer implements DialFrame, 063 Cloneable, PublicCloneable, Serializable { 064 065 /** For serialization. */ 066 static final long serialVersionUID = 1016585407507121596L; 067 068 /** The outer radius, relative to the framing rectangle. */ 069 private double radius; 070 071 /** 072 * The color used for the front of the panel. This field is transient 073 * because it requires special handling for serialization. 074 */ 075 private transient Paint backgroundPaint; 076 077 /** 078 * The color used for the border around the window. This field is transient 079 * because it requires special handling for serialization. 080 */ 081 private transient Paint foregroundPaint; 082 083 /** 084 * The stroke for drawing the frame outline. This field is transient 085 * because it requires special handling for serialization. 086 */ 087 private transient Stroke stroke; 088 089 /** 090 * Creates a new instance of {@code StandardDialFrame}. 091 */ 092 public StandardDialFrame() { 093 this.backgroundPaint = Color.GRAY; 094 this.foregroundPaint = Color.BLACK; 095 this.stroke = new BasicStroke(2.0f); 096 this.radius = 0.95; 097 } 098 099 /** 100 * Returns the radius, relative to the framing rectangle. 101 * 102 * @return The radius. 103 * 104 * @see #setRadius(double) 105 */ 106 public double getRadius() { 107 return this.radius; 108 } 109 110 /** 111 * Sets the radius and sends a {@link DialLayerChangeEvent} to all 112 * registered listeners. 113 * 114 * @param radius the radius (must be positive). 115 * 116 * @see #getRadius() 117 */ 118 public void setRadius(double radius) { 119 if (radius <= 0) { 120 throw new IllegalArgumentException( 121 "The 'radius' must be positive."); 122 } 123 this.radius = radius; 124 notifyListeners(new DialLayerChangeEvent(this)); 125 } 126 127 /** 128 * Returns the background paint. 129 * 130 * @return The background paint (never {@code null}). 131 * 132 * @see #setBackgroundPaint(Paint) 133 */ 134 public Paint getBackgroundPaint() { 135 return this.backgroundPaint; 136 } 137 138 /** 139 * Sets the background paint and sends a {@link DialLayerChangeEvent} to 140 * all registered listeners. 141 * 142 * @param paint the paint ({@code null} not permitted). 143 * 144 * @see #getBackgroundPaint() 145 */ 146 public void setBackgroundPaint(Paint paint) { 147 Args.nullNotPermitted(paint, "paint"); 148 this.backgroundPaint = paint; 149 notifyListeners(new DialLayerChangeEvent(this)); 150 } 151 152 /** 153 * Returns the foreground paint. 154 * 155 * @return The foreground paint (never {@code null}). 156 * 157 * @see #setForegroundPaint(Paint) 158 */ 159 public Paint getForegroundPaint() { 160 return this.foregroundPaint; 161 } 162 163 /** 164 * Sets the foreground paint and sends a {@link DialLayerChangeEvent} to 165 * all registered listeners. 166 * 167 * @param paint the paint ({@code null} not permitted). 168 * 169 * @see #getForegroundPaint() 170 */ 171 public void setForegroundPaint(Paint paint) { 172 Args.nullNotPermitted(paint, "paint"); 173 this.foregroundPaint = paint; 174 notifyListeners(new DialLayerChangeEvent(this)); 175 } 176 177 /** 178 * Returns the stroke for the frame. 179 * 180 * @return The stroke (never {@code null}). 181 * 182 * @see #setStroke(Stroke) 183 */ 184 public Stroke getStroke() { 185 return this.stroke; 186 } 187 188 /** 189 * Sets the stroke and sends a {@link DialLayerChangeEvent} to all 190 * registered listeners. 191 * 192 * @param stroke the stroke ({@code null} not permitted). 193 * 194 * @see #getStroke() 195 */ 196 public void setStroke(Stroke stroke) { 197 Args.nullNotPermitted(stroke, "stroke"); 198 this.stroke = stroke; 199 notifyListeners(new DialLayerChangeEvent(this)); 200 } 201 202 /** 203 * Returns the shape for the window for this dial. Some dial layers will 204 * request that their drawing be clipped within this window. 205 * 206 * @param frame the reference frame ({@code null} not permitted). 207 * 208 * @return The shape of the dial's window. 209 */ 210 @Override 211 public Shape getWindow(Rectangle2D frame) { 212 Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius, 213 this.radius); 214 return new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), 215 f.getHeight()); 216 } 217 218 /** 219 * Returns {@code false} to indicate that this dial layer is not 220 * clipped to the dial window. 221 * 222 * @return A boolean. 223 */ 224 @Override 225 public boolean isClippedToWindow() { 226 return false; 227 } 228 229 /** 230 * Draws the frame. This method is called by the {@link DialPlot} class, 231 * you shouldn't need to call it directly. 232 * 233 * @param g2 the graphics target ({@code null} not permitted). 234 * @param plot the plot ({@code null} not permitted). 235 * @param frame the frame ({@code null} not permitted). 236 * @param view the view ({@code null} not permitted). 237 */ 238 @Override 239 public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, 240 Rectangle2D view) { 241 242 Shape window = getWindow(frame); 243 244 Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius + 0.02, 245 this.radius + 0.02); 246 Ellipse2D e = new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), 247 f.getHeight()); 248 249 Area area = new Area(e); 250 Area area2 = new Area(window); 251 area.subtract(area2); 252 g2.setPaint(this.backgroundPaint); 253 g2.fill(area); 254 255 g2.setStroke(this.stroke); 256 g2.setPaint(this.foregroundPaint); 257 g2.draw(window); 258 g2.draw(e); 259 } 260 261 /** 262 * Tests this instance for equality with an arbitrary object. 263 * 264 * @param obj the object ({@code null} permitted). 265 * 266 * @return A boolean. 267 */ 268 @Override 269 public boolean equals(Object obj) { 270 if (obj == this) { 271 return true; 272 } 273 if (!(obj instanceof StandardDialFrame)) { 274 return false; 275 } 276 StandardDialFrame that = (StandardDialFrame) obj; 277 if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { 278 return false; 279 } 280 if (!PaintUtils.equal(this.foregroundPaint, that.foregroundPaint)) { 281 return false; 282 } 283 if (this.radius != that.radius) { 284 return false; 285 } 286 if (!this.stroke.equals(that.stroke)) { 287 return false; 288 } 289 return super.equals(obj); 290 } 291 292 /** 293 * Returns a hash code for this instance. 294 * 295 * @return The hash code. 296 */ 297 @Override 298 public int hashCode() { 299 int result = 193; 300 long temp = Double.doubleToLongBits(this.radius); 301 result = 37 * result + (int) (temp ^ (temp >>> 32)); 302 result = 37 * result + HashUtils.hashCodeForPaint( 303 this.backgroundPaint); 304 result = 37 * result + HashUtils.hashCodeForPaint( 305 this.foregroundPaint); 306 result = 37 * result + this.stroke.hashCode(); 307 return result; 308 } 309 310 /** 311 * Returns a clone of this instance. 312 * 313 * @return A clone. 314 * 315 * @throws CloneNotSupportedException if any of the frame's attributes 316 * cannot be cloned. 317 */ 318 @Override 319 public Object clone() throws CloneNotSupportedException { 320 return super.clone(); 321 } 322 323 /** 324 * Provides serialization support. 325 * 326 * @param stream the output stream. 327 * 328 * @throws IOException if there is an I/O error. 329 */ 330 private void writeObject(ObjectOutputStream stream) throws IOException { 331 stream.defaultWriteObject(); 332 SerialUtils.writePaint(this.backgroundPaint, stream); 333 SerialUtils.writePaint(this.foregroundPaint, stream); 334 SerialUtils.writeStroke(this.stroke, stream); 335 } 336 337 /** 338 * Provides serialization support. 339 * 340 * @param stream the input stream. 341 * 342 * @throws IOException if there is an I/O error. 343 * @throws ClassNotFoundException if there is a classpath problem. 344 */ 345 private void readObject(ObjectInputStream stream) 346 throws IOException, ClassNotFoundException { 347 stream.defaultReadObject(); 348 this.backgroundPaint = SerialUtils.readPaint(stream); 349 this.foregroundPaint = SerialUtils.readPaint(stream); 350 this.stroke = SerialUtils.readStroke(stream); 351 } 352 353}