RectF.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.graphics;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.util.FloatMath;
22import com.android.internal.util.FastMath;
23
24/**
25 * RectF holds four float coordinates for a rectangle. The rectangle is
26 * represented by the coordinates of its 4 edges (left, top, right bottom).
27 * These fields can be accessed directly. Use width() and height() to retrieve
28 * the rectangle's width and height. Note: most methods do not check to see that
29 * the coordinates are sorted correctly (i.e. left <= right and top <= bottom).
30 */
31public class RectF implements Parcelable {
32    public float left;
33    public float top;
34    public float right;
35    public float bottom;
36
37    /**
38     * Create a new empty RectF. All coordinates are initialized to 0.
39     */
40    public RectF() {}
41
42    /**
43     * Create a new rectangle with the specified coordinates. Note: no range
44     * checking is performed, so the caller must ensure that left <= right and
45     * top <= bottom.
46     *
47     * @param left   The X coordinate of the left side of the rectagle
48     * @param top    The Y coordinate of the top of the rectangle
49     * @param right  The X coordinate of the right side of the rectagle
50     * @param bottom The Y coordinate of the bottom of the rectangle
51     */
52    public RectF(float left, float top, float right, float bottom) {
53        this.left = left;
54        this.top = top;
55        this.right = right;
56        this.bottom = bottom;
57    }
58
59    /**
60     * Create a new rectangle, initialized with the values in the specified
61     * rectangle (which is left unmodified).
62     *
63     * @param r The rectangle whose coordinates are copied into the new
64     *          rectangle.
65     */
66    public RectF(RectF r) {
67        left = r.left;
68        top = r.top;
69        right = r.right;
70        bottom = r.bottom;
71    }
72
73    public RectF(Rect r) {
74        left = r.left;
75        top = r.top;
76        right = r.right;
77        bottom = r.bottom;
78    }
79
80    public String toString() {
81        return "RectF(" + left + ", " + top + ", "
82                      + right + ", " + bottom + ")";
83    }
84
85    /**
86     * Returns true if the rectangle is empty (left >= right or top >= bottom)
87     */
88    public final boolean isEmpty() {
89        return left >= right || top >= bottom;
90    }
91
92    /**
93     * @return the rectangle's width. This does not check for a valid rectangle
94     * (i.e. left <= right) so the result may be negative.
95     */
96    public final float width() {
97        return right - left;
98    }
99
100    /**
101     * @return the rectangle's height. This does not check for a valid rectangle
102     * (i.e. top <= bottom) so the result may be negative.
103     */
104    public final float height() {
105        return bottom - top;
106    }
107
108    /**
109     * @return the horizontal center of the rectangle. This does not check for
110     * a valid rectangle (i.e. left <= right)
111     */
112    public final float centerX() {
113        return (left + right) * 0.5f;
114    }
115
116    /**
117     * @return the vertical center of the rectangle. This does not check for
118     * a valid rectangle (i.e. top <= bottom)
119     */
120    public final float centerY() {
121        return (top + bottom) * 0.5f;
122    }
123
124    /**
125     * Set the rectangle to (0,0,0,0)
126     */
127    public void setEmpty() {
128        left = right = top = bottom = 0;
129    }
130
131    /**
132     * Set the rectangle's coordinates to the specified values. Note: no range
133     * checking is performed, so it is up to the caller to ensure that
134     * left <= right and top <= bottom.
135     *
136     * @param left   The X coordinate of the left side of the rectagle
137     * @param top    The Y coordinate of the top of the rectangle
138     * @param right  The X coordinate of the right side of the rectagle
139     * @param bottom The Y coordinate of the bottom of the rectangle
140     */
141    public void set(float left, float top, float right, float bottom) {
142        this.left   = left;
143        this.top    = top;
144        this.right  = right;
145        this.bottom = bottom;
146    }
147
148    /**
149     * Copy the coordinates from src into this rectangle.
150     *
151     * @param src The rectangle whose coordinates are copied into this
152     *           rectangle.
153     */
154    public void set(RectF src) {
155        this.left   = src.left;
156        this.top    = src.top;
157        this.right  = src.right;
158        this.bottom = src.bottom;
159    }
160
161    /**
162     * Copy the coordinates from src into this rectangle.
163     *
164     * @param src The rectangle whose coordinates are copied into this
165     *           rectangle.
166     */
167    public void set(Rect src) {
168        this.left   = src.left;
169        this.top    = src.top;
170        this.right  = src.right;
171        this.bottom = src.bottom;
172    }
173
174    /**
175     * Offset the rectangle by adding dx to its left and right coordinates, and
176     * adding dy to its top and bottom coordinates.
177     *
178     * @param dx The amount to add to the rectangle's left and right coordinates
179     * @param dy The amount to add to the rectangle's top and bottom coordinates
180     */
181    public void offset(float dx, float dy) {
182        left    += dx;
183        top     += dy;
184        right   += dx;
185        bottom  += dy;
186    }
187
188    /**
189     * Offset the rectangle to a specific (left, top) position,
190     * keeping its width and height the same.
191     *
192     * @param newLeft   The new "left" coordinate for the rectangle
193     * @param newTop    The new "top" coordinate for the rectangle
194     */
195    public void offsetTo(float newLeft, float newTop) {
196        right += newLeft - left;
197        bottom += newTop - top;
198        left = newLeft;
199        top = newTop;
200    }
201
202    /**
203     * Inset the rectangle by (dx,dy). If dx is positive, then the sides are
204     * moved inwards, making the rectangle narrower. If dx is negative, then the
205     * sides are moved outwards, making the rectangle wider. The same holds true
206     * for dy and the top and bottom.
207     *
208     * @param dx The amount to add(subtract) from the rectangle's left(right)
209     * @param dy The amount to add(subtract) from the rectangle's top(bottom)
210     */
211    public void inset(float dx, float dy) {
212        left    += dx;
213        top     += dy;
214        right   -= dx;
215        bottom  -= dy;
216    }
217
218    /**
219     * Returns true if (x,y) is inside the rectangle. The left and top are
220     * considered to be inside, while the right and bottom are not. This means
221     * that for a x,y to be contained: left <= x < right and top <= y < bottom.
222     * An empty rectangle never contains any point.
223     *
224     * @param x The X coordinate of the point being tested for containment
225     * @param y The Y coordinate of the point being tested for containment
226     * @return true iff (x,y) are contained by the rectangle, where containment
227     *              means left <= x < right and top <= y < bottom
228     */
229    public boolean contains(float x, float y) {
230        return left < right && top < bottom  // check for empty first
231                && x >= left && x < right && y >= top && y < bottom;
232    }
233
234    /**
235     * Returns true iff the 4 specified sides of a rectangle are inside or equal
236     * to this rectangle. i.e. is this rectangle a superset of the specified
237     * rectangle. An empty rectangle never contains another rectangle.
238     *
239     * @param left The left side of the rectangle being tested for containment
240     * @param top The top of the rectangle being tested for containment
241     * @param right The right side of the rectangle being tested for containment
242     * @param bottom The bottom of the rectangle being tested for containment
243     * @return true iff the the 4 specified sides of a rectangle are inside or
244     *              equal to this rectangle
245     */
246    public boolean contains(float left, float top, float right, float bottom) {
247                // check for empty first
248        return this.left < this.right && this.top < this.bottom
249                // now check for containment
250                && this.left <= left && this.top <= top
251                && this.right >= right && this.bottom >= bottom;
252    }
253
254    /**
255     * Returns true iff the specified rectangle r is inside or equal to this
256     * rectangle. An empty rectangle never contains another rectangle.
257     *
258     * @param r The rectangle being tested for containment.
259     * @return true iff the specified rectangle r is inside or equal to this
260     *              rectangle
261     */
262    public boolean contains(RectF r) {
263                // check for empty first
264        return this.left < this.right && this.top < this.bottom
265                // now check for containment
266                && left <= r.left && top <= r.top
267                && right >= r.right && bottom >= r.bottom;
268    }
269
270    /**
271     * If the rectangle specified by left,top,right,bottom intersects this
272     * rectangle, return true and set this rectangle to that intersection,
273     * otherwise return false and do not change this rectangle. No check is
274     * performed to see if either rectangle is empty. Note: To just test for
275     * intersection, use intersects()
276     *
277     * @param left The left side of the rectangle being intersected with this
278     *             rectangle
279     * @param top The top of the rectangle being intersected with this rectangle
280     * @param right The right side of the rectangle being intersected with this
281     *              rectangle.
282     * @param bottom The bottom of the rectangle being intersected with this
283     *             rectangle.
284     * @return true if the specified rectangle and this rectangle intersect
285     *              (and this rectangle is then set to that intersection) else
286     *              return false and do not change this rectangle.
287     */
288    public boolean intersect(float left, float top, float right, float bottom) {
289        if (this.left < right && left < this.right
290                && this.top < bottom && top < this.bottom) {
291            if (this.left < left) {
292                this.left = left;
293            }
294            if (this.top < top) {
295                this.top = top;
296            }
297            if (this.right > right) {
298                this.right = right;
299            }
300            if (this.bottom > bottom) {
301                this.bottom = bottom;
302            }
303            return true;
304        }
305        return false;
306    }
307
308    /**
309     * If the specified rectangle intersects this rectangle, return true and set
310     * this rectangle to that intersection, otherwise return false and do not
311     * change this rectangle. No check is performed to see if either rectangle
312     * is empty. To just test for intersection, use intersects()
313     *
314     * @param r The rectangle being intersected with this rectangle.
315     * @return true if the specified rectangle and this rectangle intersect
316     *              (and this rectangle is then set to that intersection) else
317     *              return false and do not change this rectangle.
318     */
319    public boolean intersect(RectF r) {
320        return intersect(r.left, r.top, r.right, r.bottom);
321    }
322
323    /**
324     * If rectangles a and b intersect, return true and set this rectangle to
325     * that intersection, otherwise return false and do not change this
326     * rectangle. No check is performed to see if either rectangle is empty.
327     * To just test for intersection, use intersects()
328     *
329     * @param a The first rectangle being intersected with
330     * @param b The second rectangle being intersected with
331     * @return true iff the two specified rectangles intersect. If they do, set
332     *              this rectangle to that intersection. If they do not, return
333     *              false and do not change this rectangle.
334     */
335    public boolean setIntersect(RectF a, RectF b) {
336        if (a.left < b.right && b.left < a.right
337                && a.top < b.bottom && b.top < a.bottom) {
338            left = Math.max(a.left, b.left);
339            top = Math.max(a.top, b.top);
340            right = Math.min(a.right, b.right);
341            bottom = Math.min(a.bottom, b.bottom);
342            return true;
343        }
344        return false;
345    }
346
347    /**
348     * Returns true if this rectangle intersects the specified rectangle.
349     * In no event is this rectangle modified. No check is performed to see
350     * if either rectangle is empty. To record the intersection, use intersect()
351     * or setIntersect().
352     *
353     * @param left The left side of the rectangle being tested for intersection
354     * @param top The top of the rectangle being tested for intersection
355     * @param right The right side of the rectangle being tested for
356     *              intersection
357     * @param bottom The bottom of the rectangle being tested for intersection
358     * @return true iff the specified rectangle intersects this rectangle. In
359     *              no event is this rectangle modified.
360     */
361    public boolean intersects(float left, float top, float right,
362                              float bottom) {
363        return this.left < right && left < this.right
364                && this.top < bottom && top < this.bottom;
365    }
366
367    /**
368     * Returns true iff the two specified rectangles intersect. In no event are
369     * either of the rectangles modified. To record the intersection,
370     * use intersect() or setIntersect().
371     *
372     * @param a The first rectangle being tested for intersection
373     * @param b The second rectangle being tested for intersection
374     * @return true iff the two specified rectangles intersect. In no event are
375     *              either of the rectangles modified.
376     */
377    public static boolean intersects(RectF a, RectF b) {
378        return a.left < b.right && b.left < a.right
379                && a.top < b.bottom && b.top < a.bottom;
380    }
381
382    /**
383     * Set the dst integer Rect by rounding this rectangle's coordinates
384     * to their nearest integer values.
385     */
386    public void round(Rect dst) {
387        dst.set(FastMath.round(left), FastMath.round(top),
388                FastMath.round(right), FastMath.round(bottom));
389    }
390
391    /**
392     * Set the dst integer Rect by rounding "out" this rectangle, choosing the
393     * floor of top and left, and the ceiling of right and bottom.
394     */
395    public void roundOut(Rect dst) {
396        dst.set((int) FloatMath.floor(left), (int) FloatMath.floor(top),
397                (int) FloatMath.ceil(right), (int) FloatMath.ceil(bottom));
398    }
399
400    /**
401     * Update this Rect to enclose itself and the specified rectangle. If the
402     * specified rectangle is empty, nothing is done. If this rectangle is empty
403     * it is set to the specified rectangle.
404     *
405     * @param left The left edge being unioned with this rectangle
406     * @param top The top edge being unioned with this rectangle
407     * @param right The right edge being unioned with this rectangle
408     * @param bottom The bottom edge being unioned with this rectangle
409     */
410    public void union(float left, float top, float right, float bottom) {
411        if ((left < right) && (top < bottom)) {
412            if ((this.left < this.right) && (this.top < this.bottom)) {
413                if (this.left > left)
414                    this.left = left;
415                if (this.top > top)
416                    this.top = top;
417                if (this.right < right)
418                    this.right = right;
419                if (this.bottom < bottom)
420                    this.bottom = bottom;
421            } else {
422                this.left = left;
423                this.top = top;
424                this.right = right;
425                this.bottom = bottom;
426            }
427        }
428    }
429
430    /**
431     * Update this Rect to enclose itself and the specified rectangle. If the
432     * specified rectangle is empty, nothing is done. If this rectangle is empty
433     * it is set to the specified rectangle.
434     *
435     * @param r The rectangle being unioned with this rectangle
436     */
437    public void union(RectF r) {
438        union(r.left, r.top, r.right, r.bottom);
439    }
440
441    /**
442     * Update this Rect to enclose itself and the [x,y] coordinate. There is no
443     * check to see that this rectangle is non-empty.
444     *
445     * @param x The x coordinate of the point to add to the rectangle
446     * @param y The y coordinate of the point to add to the rectangle
447     */
448    public void union(float x, float y) {
449        if (x < left) {
450            left = x;
451        } else if (x > right) {
452            right = x;
453        }
454        if (y < top) {
455            top = y;
456        } else if (y > bottom) {
457            bottom = y;
458        }
459    }
460
461    /**
462     * Swap top/bottom or left/right if there are flipped (i.e. left > right
463     * and/or top > bottom). This can be called if
464     * the edges are computed separately, and may have crossed over each other.
465     * If the edges are already correct (i.e. left <= right and top <= bottom)
466     * then nothing is done.
467     */
468    public void sort() {
469        if (left > right) {
470            float temp = left;
471            left = right;
472            right = temp;
473        }
474        if (top > bottom) {
475            float temp = top;
476            top = bottom;
477            bottom = temp;
478        }
479    }
480
481    /**
482     * Parcelable interface methods
483     */
484    public int describeContents() {
485        return 0;
486    }
487
488    /**
489     * Write this rectangle to the specified parcel. To restore a rectangle from
490     * a parcel, use readFromParcel()
491     * @param out The parcel to write the rectangle's coordinates into
492     */
493    public void writeToParcel(Parcel out, int flags) {
494        out.writeFloat(left);
495        out.writeFloat(top);
496        out.writeFloat(right);
497        out.writeFloat(bottom);
498    }
499
500    public static final Parcelable.Creator<RectF> CREATOR = new Parcelable.Creator<RectF>() {
501        /**
502         * Return a new rectangle from the data in the specified parcel.
503         */
504        public RectF createFromParcel(Parcel in) {
505            RectF r = new RectF();
506            r.readFromParcel(in);
507            return r;
508        }
509
510        /**
511         * Return an array of rectangles of the specified size.
512         */
513        public RectF[] newArray(int size) {
514            return new RectF[size];
515        }
516    };
517
518    /**
519     * Set the rectangle's coordinates from the data stored in the specified
520     * parcel. To write a rectangle to a parcel, call writeToParcel().
521     *
522     * @param in The parcel to read the rectangle's coordinates from
523     */
524    public void readFromParcel(Parcel in) {
525        left = in.readFloat();
526        top = in.readFloat();
527        right = in.readFloat();
528        bottom = in.readFloat();
529    }
530}
531