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