001/* RectangularShape.java -- a rectangular frame for several generic shapes
002   Copyright (C) 2000, 2002 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
038
039package java.awt.geom;
040
041import java.awt.Rectangle;
042import 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 */
058public 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