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