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