• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.graphics;
18 
19 import android.os.Parcel;
20 import android.os.Parcelable;
21 import android.util.FloatMath;
22 import 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  */
31 public 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      */
RectF()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      */
RectF(float left, float top, float right, float bottom)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      */
RectF(RectF r)66     public RectF(RectF r) {
67         left = r.left;
68         top = r.top;
69         right = r.right;
70         bottom = r.bottom;
71     }
72 
RectF(Rect r)73     public RectF(Rect r) {
74         left = r.left;
75         top = r.top;
76         right = r.right;
77         bottom = r.bottom;
78     }
79 
toString()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      */
isEmpty()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      */
width()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      */
height()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      */
centerX()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      */
centerY()120     public final float centerY() {
121         return (top + bottom) * 0.5f;
122     }
123 
124     /**
125      * Set the rectangle to (0,0,0,0)
126      */
setEmpty()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      */
set(float left, float top, float right, float bottom)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      */
set(RectF src)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      */
set(Rect src)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      */
offset(float dx, float dy)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      */
offsetTo(float newLeft, float newTop)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      */
inset(float dx, float dy)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      */
contains(float x, float y)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      */
contains(float left, float top, float right, float bottom)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      */
contains(RectF r)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      */
intersect(float left, float top, float right, float bottom)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      */
intersect(RectF r)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      */
setIntersect(RectF a, RectF b)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      */
intersects(float left, float top, float right, float bottom)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      */
intersects(RectF a, RectF b)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      */
round(Rect dst)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      */
roundOut(Rect dst)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      */
union(float left, float top, float right, float bottom)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      */
union(RectF r)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      */
union(float x, float y)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      */
sort()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      */
describeContents()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      */
writeToParcel(Parcel out, int flags)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      */
readFromParcel(Parcel in)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