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