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