001 /* RoundRectangle2D.java -- represents a rectangle with rounded corners
002 Copyright (C) 2000, 2002, 2003, 2004, 2006, Free Software Foundation
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038 package java.awt.geom;
039
040
041
042 /** This class implements a rectangle with rounded corners.
043 * @author Tom Tromey (tromey@cygnus.com)
044 * @date December 3, 2000
045 */
046 public abstract class RoundRectangle2D extends RectangularShape
047 {
048 /**
049 * Return the arc height of this round rectangle. The arc height and width
050 * control the roundness of the corners of the rectangle.
051 *
052 * @return The arc height.
053 *
054 * @see #getArcWidth()
055 */
056 public abstract double getArcHeight();
057
058 /**
059 * Return the arc width of this round rectangle. The arc width and height
060 * control the roundness of the corners of the rectangle.
061 *
062 * @return The arc width.
063 *
064 * @see #getArcHeight()
065 */
066 public abstract double getArcWidth();
067
068 /**
069 * Set the values of this round rectangle.
070 *
071 * @param x The x coordinate
072 * @param y The y coordinate
073 * @param w The width
074 * @param h The height
075 * @param arcWidth The arc width
076 * @param arcHeight The arc height
077 */
078 public abstract void setRoundRect(double x, double y, double w, double h,
079 double arcWidth, double arcHeight);
080
081 /**
082 * Create a RoundRectangle2D. This is protected because this class
083 * is abstract and cannot be instantiated.
084 */
085 protected RoundRectangle2D()
086 {
087 }
088
089 /**
090 * Return true if this object contains the specified point.
091 * @param x The x coordinate
092 * @param y The y coordinate
093 */
094 public boolean contains(double x, double y)
095 {
096 double mx = getX();
097 double mw = getWidth();
098 if (x < mx || x >= mx + mw)
099 return false;
100 double my = getY();
101 double mh = getHeight();
102 if (y < my || y >= my + mh)
103 return false;
104
105 // Now check to see if the point is in range of an arc.
106 double dy = Math.min(Math.abs(my - y), Math.abs(my + mh - y));
107 double dx = Math.min(Math.abs(mx - x), Math.abs(mx + mw - x));
108
109 // The arc dimensions are that of the corresponding ellipse
110 // thus a 90 degree segment is half of that.
111 double aw = getArcWidth() / 2.0;
112 double ah = getArcHeight() / 2.0;
113 if (dx > aw || dy > ah)
114 return true;
115
116 // At this point DX represents the distance from the nearest edge
117 // of the rectangle. But we want to transform it to represent the
118 // scaled distance from the center of the ellipse that forms the
119 // arc. Hence this code:
120 dy = (ah - dy) / ah;
121 dx = (aw - dx) / aw;
122
123 return dx * dx + dy * dy <= 1.0;
124 }
125
126 /**
127 * Return true if this object contains the specified rectangle
128 * @param x The x coordinate
129 * @param y The y coordinate
130 * @param w The width
131 * @param h The height
132 */
133 public boolean contains(double x, double y, double w, double h)
134 {
135 // We have to check all four points here (for ordinary rectangles
136 // we can just check opposing corners).
137 return (contains(x, y) && contains(x, y + h) && contains(x + w, y + h)
138 && contains(x + w, y));
139 }
140
141 /**
142 * Return a new path iterator which iterates over this rectangle.
143 *
144 * @param at An affine transform to apply to the object
145 */
146 public PathIterator getPathIterator(final AffineTransform at)
147 {
148 double arcW = Math.min(getArcWidth(), getWidth());
149 double arcH = Math.min(getArcHeight(), getHeight());
150
151 // check for special cases...
152 if (arcW <= 0 || arcH <= 0)
153 {
154 Rectangle2D r = new Rectangle2D.Double(getX(), getY(), getWidth(),
155 getHeight());
156 return r.getPathIterator(at);
157 }
158 else if (arcW >= getWidth() && arcH >= getHeight())
159 {
160 Ellipse2D e = new Ellipse2D.Double(getX(), getY(), getWidth(),
161 getHeight());
162 return e.getPathIterator(at);
163 }
164
165 // otherwise return the standard case...
166 return new PathIterator()
167 {
168 double x = getX();
169 double y = getY();
170 double w = getWidth();
171 double h = getHeight();
172 double arcW = Math.min(getArcWidth(), w);
173 double arcH = Math.min(getArcHeight(), h);
174 Arc2D.Double arc = new Arc2D.Double();
175 PathIterator corner;
176 int step = -1;
177
178 public int currentSegment(double[] coords)
179 {
180 if (corner != null) // steps 1, 3, 5 and 7
181 {
182 int r = corner.currentSegment(coords);
183 if (r == SEG_MOVETO)
184 r = SEG_LINETO;
185 return r;
186 }
187 if (step == -1)
188 {
189 // move to the start position
190 coords[0] = x + w - arcW / 2;
191 coords[1] = y;
192 }
193 else if (step == 0)
194 {
195 // top line
196 coords[0] = x + arcW / 2;
197 coords[1] = y;
198 }
199 else if (step == 2)
200 {
201 // left line
202 coords[0] = x;
203 coords[1] = y + h - arcH / 2;
204 }
205 else if (step == 4)
206 {
207 // bottom line
208 coords[0] = x + w - arcW / 2;
209 coords[1] = y + h;
210 }
211 else if (step == 6)
212 {
213 // right line
214 coords[0] = x + w;
215 coords[1] = y + arcH / 2;
216 }
217 if (at != null)
218 at.transform(coords, 0, coords, 0, 1);
219 return step == -1 ? SEG_MOVETO : SEG_LINETO;
220 }
221
222 public int currentSegment(float[] coords) {
223 if (corner != null) // steps 1, 3, 5 and 7
224 {
225 int r = corner.currentSegment(coords);
226 if (r == SEG_MOVETO)
227 r = SEG_LINETO;
228 return r;
229 }
230 if (step == -1)
231 {
232 // move to the start position
233 coords[0] = (float) (x + w - arcW / 2);
234 coords[1] = (float) y;
235 }
236 else if (step == 0)
237 {
238 // top line
239 coords[0] = (float) (x + arcW / 2);
240 coords[1] = (float) y;
241 }
242 else if (step == 2)
243 {
244 // left line
245 coords[0] = (float) x;
246 coords[1] = (float) (y + h - arcH / 2);
247 }
248 else if (step == 4)
249 {
250 // bottom line
251 coords[0] = (float) (x + w - arcW / 2);
252 coords[1] = (float) (y + h);
253 }
254 else if (step == 6)
255 {
256 // right line
257 coords[0] = (float) (x + w);
258 coords[1] = (float) (y + arcH / 2);
259 }
260 if (at != null)
261 at.transform(coords, 0, coords, 0, 1);
262 return step == -1 ? SEG_MOVETO : SEG_LINETO;
263 }
264
265 public int getWindingRule() {
266 return WIND_NON_ZERO;
267 }
268
269 public boolean isDone() {
270 return step >= 8;
271 }
272
273 public void next()
274 {
275 if (corner != null)
276 {
277 corner.next();
278 if (corner.isDone())
279 {
280 corner = null;
281 step++;
282 }
283 }
284 else
285 {
286 step++;
287 if (step == 1)
288 {
289 // create top left corner
290 arc.setArc(x, y, arcW, arcH, 90, 90, Arc2D.OPEN);
291 corner = arc.getPathIterator(at);
292 }
293 else if (step == 3)
294 {
295 // create bottom left corner
296 arc.setArc(x, y + h - arcH, arcW, arcH, 180, 90,
297 Arc2D.OPEN);
298 corner = arc.getPathIterator(at);
299 }
300 else if (step == 5)
301 {
302 // create bottom right corner
303 arc.setArc(x + w - arcW, y + h - arcH, arcW, arcH, 270, 90,
304 Arc2D.OPEN);
305 corner = arc.getPathIterator(at);
306 }
307 else if (step == 7)
308 {
309 // create top right corner
310 arc.setArc(x + w - arcW, y, arcW, arcH, 0, 90, Arc2D.OPEN);
311 corner = arc.getPathIterator(at);
312 }
313 }
314 }
315 };
316 }
317
318 /**
319 * Return true if the given rectangle intersects this shape.
320 * @param x The x coordinate
321 * @param y The y coordinate
322 * @param w The width
323 * @param h The height
324 */
325 public boolean intersects(double x, double y, double w, double h)
326 {
327 // Check if any corner is within the rectangle
328 return (contains(x, y) || contains(x, y + h) || contains(x + w, y + h)
329 || contains(x + w, y));
330 }
331
332 /**
333 * Set the boundary of this round rectangle.
334 * @param x The x coordinate
335 * @param y The y coordinate
336 * @param w The width
337 * @param h The height
338 */
339 public void setFrame(double x, double y, double w, double h)
340 {
341 // This is a bit lame.
342 setRoundRect(x, y, w, h, getArcWidth(), getArcHeight());
343 }
344
345 /**
346 * Set the values of this round rectangle to be the same as those
347 * of the argument.
348 * @param rr The round rectangle to copy
349 */
350 public void setRoundRect(RoundRectangle2D rr)
351 {
352 setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(),
353 rr.getArcWidth(), rr.getArcHeight());
354 }
355
356 /**
357 * A subclass of RoundRectangle which keeps its parameters as
358 * doubles.
359 */
360 public static class Double extends RoundRectangle2D
361 {
362 /** The height of the corner arc. */
363 public double archeight;
364
365 /** The width of the corner arc. */
366 public double arcwidth;
367
368 /** The x coordinate of this object. */
369 public double x;
370
371 /** The y coordinate of this object. */
372 public double y;
373
374 /** The width of this object. */
375 public double width;
376
377 /** The height of this object. */
378 public double height;
379
380 /**
381 * Construct a new instance, with all parameters set to 0.
382 */
383 public Double()
384 {
385 }
386
387 /**
388 * Construct a new instance with the given arguments.
389 * @param x The x coordinate
390 * @param y The y coordinate
391 * @param w The width
392 * @param h The height
393 * @param arcWidth The arc width
394 * @param arcHeight The arc height
395 */
396 public Double(double x, double y, double w, double h, double arcWidth,
397 double arcHeight)
398 {
399 this.x = x;
400 this.y = y;
401 this.width = w;
402 this.height = h;
403 this.arcwidth = arcWidth;
404 this.archeight = arcHeight;
405 }
406
407 public double getArcHeight()
408 {
409 return archeight;
410 }
411
412 public double getArcWidth()
413 {
414 return arcwidth;
415 }
416
417 public Rectangle2D getBounds2D()
418 {
419 return new Rectangle2D.Double(x, y, width, height);
420 }
421
422 public double getX()
423 {
424 return x;
425 }
426
427 public double getY()
428 {
429 return y;
430 }
431
432 public double getWidth()
433 {
434 return width;
435 }
436
437 public double getHeight()
438 {
439 return height;
440 }
441
442 public boolean isEmpty()
443 {
444 return width <= 0 || height <= 0;
445 }
446
447 public void setRoundRect(double x, double y, double w, double h,
448 double arcWidth, double arcHeight)
449 {
450 this.x = x;
451 this.y = y;
452 this.width = w;
453 this.height = h;
454 this.arcwidth = arcWidth;
455 this.archeight = arcHeight;
456 }
457 } // class Double
458
459 /**
460 * A subclass of RoundRectangle which keeps its parameters as
461 * floats.
462 */
463 public static class Float extends RoundRectangle2D
464 {
465 /** The height of the corner arc. */
466 public float archeight;
467
468 /** The width of the corner arc. */
469 public float arcwidth;
470
471 /** The x coordinate of this object. */
472 public float x;
473
474 /** The y coordinate of this object. */
475 public float y;
476
477 /** The width of this object. */
478 public float width;
479
480 /** The height of this object. */
481 public float height;
482
483 /**
484 * Construct a new instance, with all parameters set to 0.
485 */
486 public Float()
487 {
488 }
489
490 /**
491 * Construct a new instance with the given arguments.
492 * @param x The x coordinate
493 * @param y The y coordinate
494 * @param w The width
495 * @param h The height
496 * @param arcWidth The arc width
497 * @param arcHeight The arc height
498 */
499 public Float(float x, float y, float w, float h, float arcWidth,
500 float arcHeight)
501 {
502 this.x = x;
503 this.y = y;
504 this.width = w;
505 this.height = h;
506 this.arcwidth = arcWidth;
507 this.archeight = arcHeight;
508 }
509
510 public double getArcHeight()
511 {
512 return archeight;
513 }
514
515 public double getArcWidth()
516 {
517 return arcwidth;
518 }
519
520 public Rectangle2D getBounds2D()
521 {
522 return new Rectangle2D.Float(x, y, width, height);
523 }
524
525 public double getX()
526 {
527 return x;
528 }
529
530 public double getY()
531 {
532 return y;
533 }
534
535 public double getWidth()
536 {
537 return width;
538 }
539
540 public double getHeight()
541 {
542 return height;
543 }
544
545 public boolean isEmpty()
546 {
547 return width <= 0 || height <= 0;
548 }
549
550 /**
551 * Sets the dimensions for this rounded rectangle.
552 *
553 * @param x the x-coordinate of the top left corner.
554 * @param y the y-coordinate of the top left corner.
555 * @param w the width of the rectangle.
556 * @param h the height of the rectangle.
557 * @param arcWidth the arc width.
558 * @param arcHeight the arc height.
559 *
560 * @see #setRoundRect(double, double, double, double, double, double)
561 */
562 public void setRoundRect(float x, float y, float w, float h,
563 float arcWidth, float arcHeight)
564 {
565 this.x = x;
566 this.y = y;
567 this.width = w;
568 this.height = h;
569 this.arcwidth = arcWidth;
570 this.archeight = arcHeight;
571 }
572
573 public void setRoundRect(double x, double y, double w, double h,
574 double arcWidth, double arcHeight)
575 {
576 this.x = (float) x;
577 this.y = (float) y;
578 this.width = (float) w;
579 this.height = (float) h;
580 this.arcwidth = (float) arcWidth;
581 this.archeight = (float) arcHeight;
582 }
583 } // class Float
584 } // class RoundRectangle2D