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