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 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 */
32public 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     */
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     */
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     */
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
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
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
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
176    public final float centerY() {
177        return (top + bottom) * 0.5f;
178    }
179
180    /**
181     * Set the rectangle to (0,0,0,0)
182     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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