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