001    /* RectangularShape.java -- a rectangular frame for several generic shapes
002       Copyright (C) 2000, 2002 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    
039    package java.awt.geom;
040    
041    import java.awt.Rectangle;
042    import java.awt.Shape;
043    
044    /**
045     * This class provides a generic framework, and several helper methods, for
046     * subclasses which represent geometric objects inside a rectangular frame.
047     * This does not specify any geometry except for the bounding box.
048     *
049     * @author Tom Tromey (tromey@cygnus.com)
050     * @author Eric Blake (ebb9@email.byu.edu)
051     * @since 1.2
052     * @see Arc2D
053     * @see Ellipse2D
054     * @see Rectangle2D
055     * @see RoundRectangle2D
056     * @status updated to 1.4
057     */
058    public abstract class RectangularShape implements Shape, Cloneable
059    {
060      /**
061       * Default constructor.
062       */
063      protected RectangularShape()
064      {
065      }
066    
067      /**
068       * Get the x coordinate of the upper-left corner of the framing rectangle.
069       *
070       * @return the x coordinate
071       */
072      public abstract double getX();
073    
074      /**
075       * Get the y coordinate of the upper-left corner of the framing rectangle.
076       *
077       * @return the y coordinate
078       */
079      public abstract double getY();
080    
081      /**
082       * Get the width of the framing rectangle.
083       *
084       * @return the width
085       */
086      public abstract double getWidth();
087    
088      /**
089       * Get the height of the framing rectangle.
090       *
091       * @return the height
092       */
093      public abstract double getHeight();
094    
095      /**
096       * Get the minimum x coordinate in the frame. This is misnamed, or else
097       * Sun has a bug, because the implementation returns getX() even when
098       * getWidth() is negative.
099       *
100       * @return the minimum x coordinate
101       */
102      public double getMinX()
103      {
104        return getX();
105      }
106    
107      /**
108       * Get the minimum y coordinate in the frame. This is misnamed, or else
109       * Sun has a bug, because the implementation returns getY() even when
110       * getHeight() is negative.
111       *
112       * @return the minimum y coordinate
113       */
114      public double getMinY()
115      {
116        return getY();
117      }
118    
119      /**
120       * Get the maximum x coordinate in the frame. This is misnamed, or else
121       * Sun has a bug, because the implementation returns getX()+getWidth() even
122       * when getWidth() is negative.
123       *
124       * @return the maximum x coordinate
125       */
126      public double getMaxX()
127      {
128        return getX() + getWidth();
129      }
130    
131      /**
132       * Get the maximum y coordinate in the frame. This is misnamed, or else
133       * Sun has a bug, because the implementation returns getY()+getHeight() even
134       * when getHeight() is negative.
135       *
136       * @return the maximum y coordinate
137       */
138      public double getMaxY()
139      {
140        return getY() + getHeight();
141      }
142    
143      /**
144       * Return the x coordinate of the center point of the framing rectangle.
145       *
146       * @return the central x coordinate
147       */
148      public double getCenterX()
149      {
150        return getX() + getWidth() / 2;
151      }
152    
153      /**
154       * Return the y coordinate of the center point of the framing rectangle.
155       *
156       * @return the central y coordinate
157       */
158      public double getCenterY()
159      {
160        return getY() + getHeight() / 2;
161      }
162    
163      /**
164       * Return the frame around this object. Note that this may be a looser
165       * bounding box than getBounds2D.
166       *
167       * @return the frame, in double precision
168       * @see #setFrame(double, double, double, double)
169       */
170      public Rectangle2D getFrame()
171      {
172        return new Rectangle2D.Double(getX(), getY(), getWidth(), getHeight());
173      }
174    
175      /**
176       * Test if the shape is empty, meaning that no points are inside it.
177       *
178       * @return true if the shape is empty
179       */
180      public abstract boolean isEmpty();
181    
182      /**
183       * Set the framing rectangle of this shape to the given coordinate and size.
184       *
185       * @param x the new x coordinate
186       * @param y the new y coordinate
187       * @param w the new width
188       * @param h the new height
189       * @see #getFrame()
190       */
191      public abstract void setFrame(double x, double y, double w, double h);
192    
193      /**
194       * Set the framing rectangle of this shape to the given coordinate and size.
195       *
196       * @param p the new point
197       * @param d the new dimension
198       * @throws NullPointerException if p or d is null
199       * @see #getFrame()
200       */
201      public void setFrame(Point2D p, Dimension2D d)
202      {
203        setFrame(p.getX(), p.getY(), d.getWidth(), d.getHeight());
204      }
205    
206      /**
207       * Set the framing rectangle of this shape to the given rectangle.
208       *
209       * @param r the new framing rectangle
210       * @throws NullPointerException if r is null
211       * @see #getFrame()
212       */
213      public void setFrame(Rectangle2D r)
214      {
215        setFrame(r.getX(), r.getY(), r.getWidth(), r.getHeight());
216      }
217    
218      /**
219       * Set the framing rectangle of this shape using two points on a diagonal.
220       * The area will be positive.
221       *
222       * @param x1 the first x coordinate
223       * @param y1 the first y coordinate
224       * @param x2 the second x coordinate
225       * @param y2 the second y coordinate
226       */
227      public void setFrameFromDiagonal(double x1, double y1, double x2, double y2)
228      {
229        if (x1 > x2)
230          {
231            double t = x2;
232            x2 = x1;
233            x1 = t;
234          }
235        if (y1 > y2)
236          {
237            double t = y2;
238            y2 = y1;
239            y1 = t;
240          }
241        setFrame(x1, y1, x2 - x1, y2 - y1);
242      }
243    
244      /**
245       * Set the framing rectangle of this shape using two points on a diagonal.
246       * The area will be positive.
247       *
248       * @param p1 the first point
249       * @param p2 the second point
250       * @throws NullPointerException if either point is null
251       */
252      public void setFrameFromDiagonal(Point2D p1, Point2D p2)
253      {
254        setFrameFromDiagonal(p1.getX(), p1.getY(), p2.getX(), p2.getY());
255      }
256    
257      /**
258       * Set the framing rectangle of this shape using the center of the frame,
259       * and one of the four corners. The area will be positive.
260       *
261       * @param centerX the x coordinate at the center
262       * @param centerY the y coordinate at the center
263       * @param cornerX the x coordinate at a corner
264       * @param cornerY the y coordinate at a corner
265       */
266      public void setFrameFromCenter(double centerX, double centerY,
267                                     double cornerX, double cornerY)
268      {
269        double halfw = Math.abs(cornerX - centerX);
270        double halfh = Math.abs(cornerY - centerY);
271        setFrame(centerX - halfw, centerY - halfh, halfw + halfw, halfh + halfh);
272      }
273    
274      /**
275       * Set the framing rectangle of this shape using the center of the frame,
276       * and one of the four corners. The area will be positive.
277       *
278       * @param center the center point
279       * @param corner a corner point
280       * @throws NullPointerException if either point is null
281       */
282      public void setFrameFromCenter(Point2D center, Point2D corner)
283      {
284        setFrameFromCenter(center.getX(), center.getY(),
285                           corner.getX(), corner.getY());
286      }
287    
288      /**
289       * Tests if a point is inside the boundary of the shape.
290       *
291       * @param p the point to test
292       * @return true if the point is inside the shape
293       * @throws NullPointerException if p is null
294       * @see #contains(double, double)
295       */
296      public boolean contains(Point2D p)
297      {
298        return contains(p.getX(), p.getY());
299      }
300    
301      /**
302       * Tests if a rectangle and this shape share common internal points.
303       *
304       * @param r the rectangle to test
305       * @return true if the rectangle intersects this shpae
306       * @throws NullPointerException if r is null
307       * @see #intersects(double, double, double, double)
308       */
309      public boolean intersects(Rectangle2D r)
310      {
311        return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
312      }
313    
314      /**
315       * Tests if the shape completely contains the given rectangle.
316       *
317       * @param r the rectangle to test
318       * @return true if r is contained in this shape
319       * @throws NullPointerException if r is null
320       * @see #contains(double, double, double, double)
321       */
322      public boolean contains(Rectangle2D r)
323      {
324        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
325      }
326    
327      /**
328       * Returns a bounding box for this shape, in integer format. Notice that you
329       * may get a tighter bound with getBounds2D.
330       *
331       * @return a bounding box
332       */
333      public Rectangle getBounds()
334      {
335        double x = getX();
336        double y = getY();
337        double maxx = Math.ceil(x + getWidth());
338        double maxy = Math.ceil(y + getHeight());
339        x = Math.floor(x);
340        y = Math.floor(y);
341        return new Rectangle((int) x, (int) y, (int) (maxx - x), (int) (maxy - y));
342      }
343    
344      /**
345       * Return an iterator along the shape boundary. If the optional transform
346       * is provided, the iterator is transformed accordingly. The path is
347       * flattened until all segments differ from the curve by at most the value
348       * of the flatness parameter, within the limits of the default interpolation
349       * recursion limit of 1024 segments between actual points. Each call
350       * returns a new object, independent from others in use. The result is
351       * threadsafe if and only if the iterator returned by
352       * {@link #getPathIterator(AffineTransform)} is as well.
353       *
354       * @param at an optional transform to apply to the iterator
355       * @param flatness the desired flatness
356       * @return a new iterator over the boundary
357       * @throws IllegalArgumentException if flatness is invalid
358       * @since 1.2
359       */
360      public PathIterator getPathIterator(AffineTransform at, double flatness)
361      {
362        return new FlatteningPathIterator(getPathIterator(at), flatness);
363      }
364    
365      /**
366       * Create a new shape of the same run-time type with the same contents as
367       * this one.
368       *
369       * @return the clone
370       */
371      public Object clone()
372      {
373        try
374          {
375            return super.clone();
376          }
377        catch (CloneNotSupportedException e)
378          {
379            throw (Error) new InternalError().initCause(e); // Impossible
380          }
381      }
382    } // class RectangularShape