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