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;
23import java.util.regex.Matcher;
24import java.util.regex.Pattern;
25
26/**
27 * Rect holds four integer 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 final class Rect implements Parcelable {
34    public int left;
35    public int top;
36    public int right;
37    public int bottom;
38
39    private static final Pattern FLATTENED_PATTERN = Pattern.compile(
40            "(-?\\d+) (-?\\d+) (-?\\d+) (-?\\d+)");
41
42    /**
43     * Create a new empty Rect. All coordinates are initialized to 0.
44     */
45    public Rect() {}
46
47    /**
48     * Create a new rectangle with the specified coordinates. Note: no range
49     * checking is performed, so the caller must ensure that left <= right and
50     * top <= bottom.
51     *
52     * @param left   The X coordinate of the left side of the rectangle
53     * @param top    The Y coordinate of the top of the rectangle
54     * @param right  The X coordinate of the right side of the rectangle
55     * @param bottom The Y coordinate of the bottom of the rectangle
56     */
57    public Rect(int left, int top, int right, int bottom) {
58        this.left = left;
59        this.top = top;
60        this.right = right;
61        this.bottom = bottom;
62    }
63
64    /**
65     * Create a new rectangle, initialized with the values in the specified
66     * rectangle (which is left unmodified).
67     *
68     * @param r The rectangle whose coordinates are copied into the new
69     *          rectangle.
70     */
71    public Rect(Rect r) {
72        if (r == null) {
73            left = top = right = bottom = 0;
74        } else {
75            left = r.left;
76            top = r.top;
77            right = r.right;
78            bottom = r.bottom;
79        }
80    }
81
82    @Override
83    public boolean equals(Object o) {
84        if (this == o) return true;
85        if (o == null || getClass() != o.getClass()) return false;
86
87        Rect r = (Rect) o;
88        return left == r.left && top == r.top && right == r.right && bottom == r.bottom;
89    }
90
91    @Override
92    public int hashCode() {
93        int result = left;
94        result = 31 * result + top;
95        result = 31 * result + right;
96        result = 31 * result + bottom;
97        return result;
98    }
99
100    @Override
101    public String toString() {
102        StringBuilder sb = new StringBuilder(32);
103        sb.append("Rect("); 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     * Return a string representation of the rectangle in a compact form.
111     */
112    public String toShortString() {
113        return toShortString(new StringBuilder(32));
114    }
115
116    /**
117     * Return a string representation of the rectangle in a compact form.
118     * @hide
119     */
120    public String toShortString(StringBuilder sb) {
121        sb.setLength(0);
122        sb.append('['); sb.append(left); sb.append(',');
123        sb.append(top); sb.append("]["); sb.append(right);
124        sb.append(','); sb.append(bottom); sb.append(']');
125        return sb.toString();
126    }
127
128    /**
129     * Return a string representation of the rectangle in a well-defined format.
130     *
131     * <p>You can later recover the Rect from this string through
132     * {@link #unflattenFromString(String)}.
133     *
134     * @return Returns a new String of the form "left top right bottom"
135     */
136    public String flattenToString() {
137        StringBuilder sb = new StringBuilder(32);
138        // WARNING: Do not change the format of this string, it must be
139        // preserved because Rects are saved in this flattened format.
140        sb.append(left);
141        sb.append(' ');
142        sb.append(top);
143        sb.append(' ');
144        sb.append(right);
145        sb.append(' ');
146        sb.append(bottom);
147        return sb.toString();
148    }
149
150    /**
151     * Returns a Rect from a string of the form returned by {@link #flattenToString},
152     * or null if the string is not of that form.
153     */
154    public static Rect unflattenFromString(String str) {
155        Matcher matcher = FLATTENED_PATTERN.matcher(str);
156        if (!matcher.matches()) {
157            return null;
158        }
159        return new Rect(Integer.parseInt(matcher.group(1)),
160                Integer.parseInt(matcher.group(2)),
161                Integer.parseInt(matcher.group(3)),
162                Integer.parseInt(matcher.group(4)));
163    }
164
165    /**
166     * Print short representation to given writer.
167     * @hide
168     */
169    public void printShortString(PrintWriter pw) {
170        pw.print('['); pw.print(left); pw.print(',');
171        pw.print(top); pw.print("]["); pw.print(right);
172        pw.print(','); pw.print(bottom); pw.print(']');
173    }
174
175    /**
176     * Returns true if the rectangle is empty (left >= right or top >= bottom)
177     */
178    public final boolean isEmpty() {
179        return left >= right || top >= bottom;
180    }
181
182    /**
183     * @return the rectangle's width. This does not check for a valid rectangle
184     * (i.e. left <= right) so the result may be negative.
185     */
186    public final int width() {
187        return right - left;
188    }
189
190    /**
191     * @return the rectangle's height. This does not check for a valid rectangle
192     * (i.e. top <= bottom) so the result may be negative.
193     */
194    public final int height() {
195        return bottom - top;
196    }
197
198    /**
199     * @return the horizontal center of the rectangle. If the computed value
200     *         is fractional, this method returns the largest integer that is
201     *         less than the computed value.
202     */
203    public final int centerX() {
204        return (left + right) >> 1;
205    }
206
207    /**
208     * @return the vertical center of the rectangle. If the computed value
209     *         is fractional, this method returns the largest integer that is
210     *         less than the computed value.
211     */
212    public final int centerY() {
213        return (top + bottom) >> 1;
214    }
215
216    /**
217     * @return the exact horizontal center of the rectangle as a float.
218     */
219    public final float exactCenterX() {
220        return (left + right) * 0.5f;
221    }
222
223    /**
224     * @return the exact vertical center of the rectangle as a float.
225     */
226    public final float exactCenterY() {
227        return (top + bottom) * 0.5f;
228    }
229
230    /**
231     * Set the rectangle to (0,0,0,0)
232     */
233    public void setEmpty() {
234        left = right = top = bottom = 0;
235    }
236
237    /**
238     * Set the rectangle's coordinates to the specified values. Note: no range
239     * checking is performed, so it is up to the caller to ensure that
240     * left <= right and top <= bottom.
241     *
242     * @param left   The X coordinate of the left side of the rectangle
243     * @param top    The Y coordinate of the top of the rectangle
244     * @param right  The X coordinate of the right side of the rectangle
245     * @param bottom The Y coordinate of the bottom of the rectangle
246     */
247    public void set(int left, int top, int right, int bottom) {
248        this.left = left;
249        this.top = top;
250        this.right = right;
251        this.bottom = bottom;
252    }
253
254    /**
255     * Copy the coordinates from src into this rectangle.
256     *
257     * @param src The rectangle whose coordinates are copied into this
258     *           rectangle.
259     */
260    public void set(Rect src) {
261        this.left = src.left;
262        this.top = src.top;
263        this.right = src.right;
264        this.bottom = src.bottom;
265    }
266
267    /**
268     * Offset the rectangle by adding dx to its left and right coordinates, and
269     * adding dy to its top and bottom coordinates.
270     *
271     * @param dx The amount to add to the rectangle's left and right coordinates
272     * @param dy The amount to add to the rectangle's top and bottom coordinates
273     */
274    public void offset(int dx, int dy) {
275        left += dx;
276        top += dy;
277        right += dx;
278        bottom += dy;
279    }
280
281    /**
282     * Offset the rectangle to a specific (left, top) position,
283     * keeping its width and height the same.
284     *
285     * @param newLeft   The new "left" coordinate for the rectangle
286     * @param newTop    The new "top" coordinate for the rectangle
287     */
288    public void offsetTo(int newLeft, int newTop) {
289        right += newLeft - left;
290        bottom += newTop - top;
291        left = newLeft;
292        top = newTop;
293    }
294
295    /**
296     * Inset the rectangle by (dx,dy). If dx is positive, then the sides are
297     * moved inwards, making the rectangle narrower. If dx is negative, then the
298     * sides are moved outwards, making the rectangle wider. The same holds true
299     * for dy and the top and bottom.
300     *
301     * @param dx The amount to add(subtract) from the rectangle's left(right)
302     * @param dy The amount to add(subtract) from the rectangle's top(bottom)
303     */
304    public void inset(int dx, int dy) {
305        left += dx;
306        top += dy;
307        right -= dx;
308        bottom -= dy;
309    }
310
311    /**
312     * Returns true if (x,y) is inside the rectangle. The left and top are
313     * considered to be inside, while the right and bottom are not. This means
314     * that for a x,y to be contained: left <= x < right and top <= y < bottom.
315     * An empty rectangle never contains any point.
316     *
317     * @param x The X coordinate of the point being tested for containment
318     * @param y The Y coordinate of the point being tested for containment
319     * @return true iff (x,y) are contained by the rectangle, where containment
320     *              means left <= x < right and top <= y < bottom
321     */
322    public boolean contains(int x, int y) {
323        return left < right && top < bottom  // check for empty first
324               && x >= left && x < right && y >= top && y < bottom;
325    }
326
327    /**
328     * Returns true iff the 4 specified sides of a rectangle are inside or equal
329     * to this rectangle. i.e. is this rectangle a superset of the specified
330     * rectangle. An empty rectangle never contains another rectangle.
331     *
332     * @param left The left side of the rectangle being tested for containment
333     * @param top The top of the rectangle being tested for containment
334     * @param right The right side of the rectangle being tested for containment
335     * @param bottom The bottom of the rectangle being tested for containment
336     * @return true iff the the 4 specified sides of a rectangle are inside or
337     *              equal to this rectangle
338     */
339    public boolean contains(int left, int top, int right, int bottom) {
340               // check for empty first
341        return this.left < this.right && this.top < this.bottom
342               // now check for containment
343                && this.left <= left && this.top <= top
344                && this.right >= right && this.bottom >= bottom;
345    }
346
347    /**
348     * Returns true iff the specified rectangle r is inside or equal to this
349     * rectangle. An empty rectangle never contains another rectangle.
350     *
351     * @param r The rectangle being tested for containment.
352     * @return true iff the specified rectangle r is inside or equal to this
353     *              rectangle
354     */
355    public boolean contains(Rect r) {
356               // check for empty first
357        return this.left < this.right && this.top < this.bottom
358               // now check for containment
359               && left <= r.left && top <= r.top && right >= r.right && bottom >= r.bottom;
360    }
361
362    /**
363     * If the rectangle specified by left,top,right,bottom intersects this
364     * rectangle, return true and set this rectangle to that intersection,
365     * otherwise return false and do not change this rectangle. No check is
366     * performed to see if either rectangle is empty. Note: To just test for
367     * intersection, use {@link #intersects(Rect, Rect)}.
368     *
369     * @param left The left side of the rectangle being intersected with this
370     *             rectangle
371     * @param top The top of the rectangle being intersected with this rectangle
372     * @param right The right side of the rectangle being intersected with this
373     *              rectangle.
374     * @param bottom The bottom of the rectangle being intersected with this
375     *             rectangle.
376     * @return true if the specified rectangle and this rectangle intersect
377     *              (and this rectangle is then set to that intersection) else
378     *              return false and do not change this rectangle.
379     */
380    public boolean intersect(int left, int top, int right, int bottom) {
381        if (this.left < right && left < this.right && this.top < bottom && top < this.bottom) {
382            if (this.left < left) this.left = left;
383            if (this.top < top) this.top = top;
384            if (this.right > right) this.right = right;
385            if (this.bottom > bottom) this.bottom = bottom;
386            return true;
387        }
388        return false;
389    }
390
391    /**
392     * If the specified rectangle intersects this rectangle, return true and set
393     * this rectangle to that intersection, otherwise return false and do not
394     * change this rectangle. No check is performed to see if either rectangle
395     * is empty. To just test for intersection, use intersects()
396     *
397     * @param r The rectangle being intersected with this rectangle.
398     * @return true if the specified rectangle and this rectangle intersect
399     *              (and this rectangle is then set to that intersection) else
400     *              return false and do not change this rectangle.
401     */
402    public boolean intersect(Rect r) {
403        return intersect(r.left, r.top, r.right, r.bottom);
404    }
405
406    /**
407     * If rectangles a and b intersect, return true and set this rectangle to
408     * that intersection, otherwise return false and do not change this
409     * rectangle. No check is performed to see if either rectangle is empty.
410     * To just test for intersection, use intersects()
411     *
412     * @param a The first rectangle being intersected with
413     * @param b The second rectangle being intersected with
414     * @return true iff the two specified rectangles intersect. If they do, set
415     *              this rectangle to that intersection. If they do not, return
416     *              false and do not change this rectangle.
417     */
418    public boolean setIntersect(Rect a, Rect b) {
419        if (a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom) {
420            left = Math.max(a.left, b.left);
421            top = Math.max(a.top, b.top);
422            right = Math.min(a.right, b.right);
423            bottom = Math.min(a.bottom, b.bottom);
424            return true;
425        }
426        return false;
427    }
428
429    /**
430     * Returns true if this rectangle intersects the specified rectangle.
431     * In no event is this rectangle modified. No check is performed to see
432     * if either rectangle is empty. To record the intersection, use intersect()
433     * or setIntersect().
434     *
435     * @param left The left side of the rectangle being tested for intersection
436     * @param top The top of the rectangle being tested for intersection
437     * @param right The right side of the rectangle being tested for
438     *              intersection
439     * @param bottom The bottom of the rectangle being tested for intersection
440     * @return true iff the specified rectangle intersects this rectangle. In
441     *              no event is this rectangle modified.
442     */
443    public boolean intersects(int left, int top, int right, int bottom) {
444        return this.left < right && left < this.right && this.top < bottom && top < this.bottom;
445    }
446
447    /**
448     * Returns true iff the two specified rectangles intersect. In no event are
449     * either of the rectangles modified. To record the intersection,
450     * use {@link #intersect(Rect)} or {@link #setIntersect(Rect, Rect)}.
451     *
452     * @param a The first rectangle being tested for intersection
453     * @param b The second rectangle being tested for intersection
454     * @return true iff the two specified rectangles intersect. In no event are
455     *              either of the rectangles modified.
456     */
457    public static boolean intersects(Rect a, Rect b) {
458        return a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom;
459    }
460
461    /**
462     * Update this Rect to enclose itself and the specified rectangle. If the
463     * specified rectangle is empty, nothing is done. If this rectangle is empty
464     * it is set to the specified rectangle.
465     *
466     * @param left The left edge being unioned with this rectangle
467     * @param top The top edge being unioned with this rectangle
468     * @param right The right edge being unioned with this rectangle
469     * @param bottom The bottom edge being unioned with this rectangle
470     */
471    public void union(int left, int top, int right, int bottom) {
472        if ((left < right) && (top < bottom)) {
473            if ((this.left < this.right) && (this.top < this.bottom)) {
474                if (this.left > left) this.left = left;
475                if (this.top > top) this.top = top;
476                if (this.right < right) this.right = right;
477                if (this.bottom < bottom) 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(Rect 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(int x, int 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            int temp = left;
528            left = right;
529            right = temp;
530        }
531        if (top > bottom) {
532            int 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.writeInt(left);
552        out.writeInt(top);
553        out.writeInt(right);
554        out.writeInt(bottom);
555    }
556
557    public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {
558        /**
559         * Return a new rectangle from the data in the specified parcel.
560         */
561        public Rect createFromParcel(Parcel in) {
562            Rect r = new Rect();
563            r.readFromParcel(in);
564            return r;
565        }
566
567        /**
568         * Return an array of rectangles of the specified size.
569         */
570        public Rect[] newArray(int size) {
571            return new Rect[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.readInt();
583        top = in.readInt();
584        right = in.readInt();
585        bottom = in.readInt();
586    }
587
588    /**
589     * Scales up the rect by the given scale.
590     * @hide
591     */
592    public void scale(float scale) {
593        if (scale != 1.0f) {
594            left = (int) (left * scale + 0.5f);
595            top = (int) (top * scale + 0.5f);
596            right = (int) (right * scale + 0.5f);
597            bottom = (int) (bottom * scale + 0.5f);
598        }
599    }
600}
601