001/* RoundRectangle2D.java -- represents a rectangle with rounded corners
002   Copyright (C) 2000, 2002, 2003, 2004, 2006, Free Software Foundation
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package 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 */
046public 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