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