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