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 * DialCap.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.Stroke; 044import java.awt.geom.Ellipse2D; 045import java.awt.geom.Rectangle2D; 046import java.io.IOException; 047import java.io.ObjectInputStream; 048import java.io.ObjectOutputStream; 049import java.io.Serializable; 050 051import org.jfree.chart.HashUtils; 052import org.jfree.chart.util.PaintUtils; 053import org.jfree.chart.util.Args; 054import org.jfree.chart.util.PublicCloneable; 055import org.jfree.chart.util.SerialUtils; 056 057/** 058 * A regular dial layer that can be used to draw a cap over the center of 059 * the dial (the base of the dial pointer(s)). 060 */ 061public class DialCap extends AbstractDialLayer implements DialLayer, Cloneable, 062 PublicCloneable, Serializable { 063 064 /** For serialization. */ 065 static final long serialVersionUID = -2929484264982524463L; 066 067 /** 068 * The radius of the cap, as a percentage of the framing rectangle. 069 */ 070 private double radius; 071 072 /** 073 * The fill paint. This field is transient because it requires special 074 * handling for serialization. 075 */ 076 private transient Paint fillPaint; 077 078 /** 079 * The paint used to draw the cap outline (this should never be 080 * {@code null}). This field is transient because it requires 081 * special handling for serialization. 082 */ 083 private transient Paint outlinePaint; 084 085 /** 086 * The stroke used to draw the cap outline (this should never be 087 * {@code null}). This field is transient because it requires 088 * special handling for serialization. 089 */ 090 private transient Stroke outlineStroke; 091 092 /** 093 * Creates a new instance of {@code StandardDialBackground}. The 094 * default background paint is {@code Color.WHITE}. 095 */ 096 public DialCap() { 097 this.radius = 0.05; 098 this.fillPaint = Color.WHITE; 099 this.outlinePaint = Color.BLACK; 100 this.outlineStroke = new BasicStroke(2.0f); 101 } 102 103 /** 104 * Returns the radius of the cap, as a percentage of the dial's framing 105 * rectangle. 106 * 107 * @return The radius. 108 * 109 * @see #setRadius(double) 110 */ 111 public double getRadius() { 112 return this.radius; 113 } 114 115 /** 116 * Sets the radius of the cap, as a percentage of the dial's framing 117 * rectangle, and sends a {@link DialLayerChangeEvent} to all registered 118 * listeners. 119 * 120 * @param radius the radius (must be greater than zero). 121 * 122 * @see #getRadius() 123 */ 124 public void setRadius(double radius) { 125 if (radius <= 0.0) { 126 throw new IllegalArgumentException("Requires radius > 0.0."); 127 } 128 this.radius = radius; 129 notifyListeners(new DialLayerChangeEvent(this)); 130 } 131 132 /** 133 * Returns the paint used to fill the cap. 134 * 135 * @return The paint (never {@code null}). 136 * 137 * @see #setFillPaint(Paint) 138 */ 139 public Paint getFillPaint() { 140 return this.fillPaint; 141 } 142 143 /** 144 * Sets the paint for the cap background and sends a 145 * {@link DialLayerChangeEvent} to all registered listeners. 146 * 147 * @param paint the paint ({@code null} not permitted). 148 * 149 * @see #getFillPaint() 150 */ 151 public void setFillPaint(Paint paint) { 152 Args.nullNotPermitted(paint, "paint"); 153 this.fillPaint = paint; 154 notifyListeners(new DialLayerChangeEvent(this)); 155 } 156 157 /** 158 * Returns the paint used to draw the outline of the cap. 159 * 160 * @return The paint (never {@code null}). 161 * 162 * @see #setOutlinePaint(Paint) 163 */ 164 public Paint getOutlinePaint() { 165 return this.outlinePaint; 166 } 167 168 /** 169 * Sets the paint used to draw the outline of the cap and sends a 170 * {@link DialLayerChangeEvent} to all registered listeners. 171 * 172 * @param paint the paint ({@code null} not permitted). 173 * 174 * @see #getOutlinePaint() 175 */ 176 public void setOutlinePaint(Paint paint) { 177 Args.nullNotPermitted(paint, "paint"); 178 this.outlinePaint = paint; 179 notifyListeners(new DialLayerChangeEvent(this)); 180 } 181 182 /** 183 * Returns the stroke used to draw the outline of the cap. 184 * 185 * @return The stroke (never {@code null}). 186 * 187 * @see #setOutlineStroke(Stroke) 188 */ 189 public Stroke getOutlineStroke() { 190 return this.outlineStroke; 191 } 192 193 /** 194 * Sets the stroke used to draw the outline of the cap and sends a 195 * {@link DialLayerChangeEvent} to all registered listeners. 196 * 197 * @param stroke the stroke ({@code null} not permitted). 198 * 199 * @see #getOutlineStroke() 200 */ 201 public void setOutlineStroke(Stroke stroke) { 202 Args.nullNotPermitted(stroke, "stroke"); 203 this.outlineStroke = stroke; 204 notifyListeners(new DialLayerChangeEvent(this)); 205 } 206 207 /** 208 * Returns {@code true} to indicate that this layer should be 209 * clipped within the dial window. 210 * 211 * @return {@code true}. 212 */ 213 @Override 214 public boolean isClippedToWindow() { 215 return true; 216 } 217 218 /** 219 * Draws the background to the specified graphics device. If the dial 220 * frame specifies a window, the clipping region will already have been 221 * set to this window before this method is called. 222 * 223 * @param g2 the graphics device ({@code null} not permitted). 224 * @param plot the plot (ignored here). 225 * @param frame the dial frame (ignored here). 226 * @param view the view rectangle ({@code null} not permitted). 227 */ 228 @Override 229 public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, 230 Rectangle2D view) { 231 232 g2.setPaint(this.fillPaint); 233 234 Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius, 235 this.radius); 236 Ellipse2D e = new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), 237 f.getHeight()); 238 g2.fill(e); 239 g2.setPaint(this.outlinePaint); 240 g2.setStroke(this.outlineStroke); 241 g2.draw(e); 242 243 } 244 245 /** 246 * Tests this instance for equality with an arbitrary object. 247 * 248 * @param obj the object ({@code null} permitted). 249 * 250 * @return A boolean. 251 */ 252 @Override 253 public boolean equals(Object obj) { 254 if (obj == this) { 255 return true; 256 } 257 if (!(obj instanceof DialCap)) { 258 return false; 259 } 260 DialCap that = (DialCap) obj; 261 if (this.radius != that.radius) { 262 return false; 263 } 264 if (!PaintUtils.equal(this.fillPaint, that.fillPaint)) { 265 return false; 266 } 267 if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { 268 return false; 269 } 270 if (!this.outlineStroke.equals(that.outlineStroke)) { 271 return false; 272 } 273 return super.equals(obj); 274 } 275 276 /** 277 * Returns a hash code for this instance. 278 * 279 * @return The hash code. 280 */ 281 @Override 282 public int hashCode() { 283 int result = 193; 284 result = 37 * result + HashUtils.hashCodeForPaint(this.fillPaint); 285 result = 37 * result + HashUtils.hashCodeForPaint( 286 this.outlinePaint); 287 result = 37 * result + this.outlineStroke.hashCode(); 288 return result; 289 } 290 291 /** 292 * Returns a clone of this instance. 293 * 294 * @return A clone. 295 * 296 * @throws CloneNotSupportedException if some attribute of the cap cannot 297 * be cloned. 298 */ 299 @Override 300 public Object clone() throws CloneNotSupportedException { 301 return super.clone(); 302 } 303 304 /** 305 * Provides serialization support. 306 * 307 * @param stream the output stream. 308 * 309 * @throws IOException if there is an I/O error. 310 */ 311 private void writeObject(ObjectOutputStream stream) throws IOException { 312 stream.defaultWriteObject(); 313 SerialUtils.writePaint(this.fillPaint, stream); 314 SerialUtils.writePaint(this.outlinePaint, stream); 315 SerialUtils.writeStroke(this.outlineStroke, stream); 316 } 317 318 /** 319 * Provides serialization support. 320 * 321 * @param stream the input stream. 322 * 323 * @throws IOException if there is an I/O error. 324 * @throws ClassNotFoundException if there is a classpath problem. 325 */ 326 private void readObject(ObjectInputStream stream) 327 throws IOException, ClassNotFoundException { 328 stream.defaultReadObject(); 329 this.fillPaint = SerialUtils.readPaint(stream); 330 this.outlinePaint = SerialUtils.readPaint(stream); 331 this.outlineStroke = SerialUtils.readStroke(stream); 332 } 333 334} 335