RectF.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
10529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch/*
20529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * Copyright (C) 2006 The Android Open Source Project
30529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch *
40529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * Licensed under the Apache License, Version 2.0 (the "License");
50529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * you may not use this file except in compliance with the License.
60529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * You may obtain a copy of the License at
70529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch *
80529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch *      http://www.apache.org/licenses/LICENSE-2.0
90529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch *
100529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * Unless required by applicable law or agreed to in writing, software
110529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * distributed under the License is distributed on an "AS IS" BASIS,
120529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * See the License for the specific language governing permissions and
140529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * limitations under the License.
150529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch */
160529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
170529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochpackage android.graphics;
180529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
190529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.util.FloatMath;
200529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport com.android.internal.util.FastMath;
210529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
220529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch/**
230529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * RectF holds four float coordinates for a rectangle. The rectangle is
240529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * represented by the coordinates of its 4 edges (left, top, right bottom).
250529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * These fields can be accessed directly. Use width() and height() to retrieve
260529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * the rectangle's width and height. Note: most methods do not check to see that
270529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * the coordinates are sorted correctly (i.e. left <= right and top <= bottom).
280529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch */
290529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochpublic class RectF {
300529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    public float left;
310529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    public float top;
320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    public float right;
330529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    public float bottom;
340529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
350529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    /**
360529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     * Create a new empty RectF. All coordinates are initialized to 0.
370529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     */
380529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    public RectF() {}
390529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
400529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    /**
410529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     * Create a new rectangle with the specified coordinates. Note: no range
420529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     * checking is performed, so the caller must ensure that left <= right and
430529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     * top <= bottom.
44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     *
45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)     * @param left   The X coordinate of the left side of the rectagle
460529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     * @param top    The Y coordinate of the top of the rectangle
470529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     * @param right  The X coordinate of the right side of the rectagle
480529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     * @param bottom The Y coordinate of the bottom of the rectangle
490529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     */
500529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    public RectF(float left, float top, float right, float bottom) {
510529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        this.left = left;
520529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        this.top = top;
530529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        this.right = right;
540529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        this.bottom = bottom;
550529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
560529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
570529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    /**
580529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     * Create a new rectangle, initialized with the values in the specified
590529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     * rectangle (which is left unmodified).
600529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     *
610529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     * @param r The rectangle whose coordinates are copied into the new
620529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     *          rectangle.
630529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch     */
640529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    public RectF(RectF r) {
650529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        left = r.left;
660529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        top = r.top;
670529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        right = r.right;
680529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        bottom = r.bottom;
690529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
700529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
710529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    public RectF(Rect r) {
720529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        left = r.left;
73        top = r.top;
74        right = r.right;
75        bottom = r.bottom;
76    }
77
78    public String toString() {
79        return "RectF(" + left + ", " + top + ", "
80                      + right + ", " + bottom + ")";
81    }
82
83    /**
84     * Returns true if the rectangle is empty (left >= right or top >= bottom)
85     */
86    public final boolean isEmpty() {
87        return left >= right || top >= bottom;
88    }
89
90    /**
91     * @return the rectangle's width. This does not check for a valid rectangle
92     * (i.e. left <= right) so the result may be negative.
93     */
94    public final float width() {
95        return right - left;
96    }
97
98    /**
99     * @return the rectangle's height. This does not check for a valid rectangle
100     * (i.e. top <= bottom) so the result may be negative.
101     */
102    public final float height() {
103        return bottom - top;
104    }
105
106    /**
107     * @return the horizontal center of the rectangle. This does not check for
108     * a valid rectangle (i.e. left <= right)
109     */
110    public final float centerX() {
111        return (left + right) * 0.5f;
112    }
113
114    /**
115     * @return the vertical center of the rectangle. This does not check for
116     * a valid rectangle (i.e. top <= bottom)
117     */
118    public final float centerY() {
119        return (top + bottom) * 0.5f;
120    }
121
122    /**
123     * Set the rectangle to (0,0,0,0)
124     */
125    public void setEmpty() {
126        left = right = top = bottom = 0;
127    }
128
129    /**
130     * Set the rectangle's coordinates to the specified values. Note: no range
131     * checking is performed, so it is up to the caller to ensure that
132     * left <= right and top <= bottom.
133     *
134     * @param left   The X coordinate of the left side of the rectagle
135     * @param top    The Y coordinate of the top of the rectangle
136     * @param right  The X coordinate of the right side of the rectagle
137     * @param bottom The Y coordinate of the bottom of the rectangle
138     */
139    public void set(float left, float top, float right, float bottom) {
140        this.left   = left;
141        this.top    = top;
142        this.right  = right;
143        this.bottom = bottom;
144    }
145
146    /**
147     * Copy the coordinates from src into this rectangle.
148     *
149     * @param src The rectangle whose coordinates are copied into this
150     *           rectangle.
151     */
152    public void set(RectF src) {
153        this.left   = src.left;
154        this.top    = src.top;
155        this.right  = src.right;
156        this.bottom = src.bottom;
157    }
158
159    /**
160     * Copy the coordinates from src into this rectangle.
161     *
162     * @param src The rectangle whose coordinates are copied into this
163     *           rectangle.
164     */
165    public void set(Rect src) {
166        this.left   = src.left;
167        this.top    = src.top;
168        this.right  = src.right;
169        this.bottom = src.bottom;
170    }
171
172    /**
173     * Offset the rectangle by adding dx to its left and right coordinates, and
174     * adding dy to its top and bottom coordinates.
175     *
176     * @param dx The amount to add to the rectangle's left and right coordinates
177     * @param dy The amount to add to the rectangle's top and bottom coordinates
178     */
179    public void offset(float dx, float dy) {
180        left    += dx;
181        top     += dy;
182        right   += dx;
183        bottom  += dy;
184    }
185
186    /**
187     * Offset the rectangle to a specific (left, top) position,
188     * keeping its width and height the same.
189     *
190     * @param newLeft   The new "left" coordinate for the rectangle
191     * @param newTop    The new "top" coordinate for the rectangle
192     */
193    public void offsetTo(float newLeft, float newTop) {
194        right += newLeft - left;
195        bottom += newTop - top;
196        left = newLeft;
197        top = newTop;
198    }
199
200    /**
201     * Inset the rectangle by (dx,dy). If dx is positive, then the sides are
202     * moved inwards, making the rectangle narrower. If dx is negative, then the
203     * sides are moved outwards, making the rectangle wider. The same holds true
204     * for dy and the top and bottom.
205     *
206     * @param dx The amount to add(subtract) from the rectangle's left(right)
207     * @param dy The amount to add(subtract) from the rectangle's top(bottom)
208     */
209    public void inset(float dx, float dy) {
210        left    += dx;
211        top     += dy;
212        right   -= dx;
213        bottom  -= dy;
214    }
215
216    /**
217     * Returns true if (x,y) is inside the rectangle. The left and top are
218     * considered to be inside, while the right and bottom are not. This means
219     * that for a x,y to be contained: left <= x < right and top <= y < bottom.
220     * An empty rectangle never contains any point.
221     *
222     * @param x The X coordinate of the point being tested for containment
223     * @param y The Y coordinate of the point being tested for containment
224     * @return true iff (x,y) are contained by the rectangle, where containment
225     *              means left <= x < right and top <= y < bottom
226     */
227    public boolean contains(float x, float y) {
228        return left < right && top < bottom  // check for empty first
229                && x >= left && x < right && y >= top && y < bottom;
230    }
231
232    /**
233     * Returns true iff the 4 specified sides of a rectangle are inside or equal
234     * to this rectangle. i.e. is this rectangle a superset of the specified
235     * rectangle. An empty rectangle never contains another rectangle.
236     *
237     * @param left The left side of the rectangle being tested for containment
238     * @param top The top of the rectangle being tested for containment
239     * @param right The right side of the rectangle being tested for containment
240     * @param bottom The bottom of the rectangle being tested for containment
241     * @return true iff the the 4 specified sides of a rectangle are inside or
242     *              equal to this rectangle
243     */
244    public boolean contains(float left, float top, float right, float bottom) {
245                // check for empty first
246        return this.left < this.right && this.top < this.bottom
247                // now check for containment
248                && this.left <= left && this.top <= top
249                && this.right >= right && this.bottom >= bottom;
250    }
251
252    /**
253     * Returns true iff the specified rectangle r is inside or equal to this
254     * rectangle. An empty rectangle never contains another rectangle.
255     *
256     * @param r The rectangle being tested for containment.
257     * @return true iff the specified rectangle r is inside or equal to this
258     *              rectangle
259     */
260    public boolean contains(RectF r) {
261                // check for empty first
262        return this.left < this.right && this.top < this.bottom
263                // now check for containment
264                && left <= r.left && top <= r.top
265                && right >= r.right && bottom >= r.bottom;
266    }
267
268    /**
269     * If the rectangle specified by left,top,right,bottom intersects this
270     * rectangle, return true and set this rectangle to that intersection,
271     * otherwise return false and do not change this rectangle. No check is
272     * performed to see if either rectangle is empty. Note: To just test for
273     * intersection, use intersects()
274     *
275     * @param left The left side of the rectangle being intersected with this
276     *             rectangle
277     * @param top The top of the rectangle being intersected with this rectangle
278     * @param right The right side of the rectangle being intersected with this
279     *              rectangle.
280     * @param bottom The bottom of the rectangle being intersected with this
281     *             rectangle.
282     * @return true if the specified rectangle and this rectangle intersect
283     *              (and this rectangle is then set to that intersection) else
284     *              return false and do not change this rectangle.
285     */
286    public boolean intersect(float left, float top, float right, float bottom) {
287        if (this.left < right && left < this.right
288                && this.top < bottom && top < this.bottom) {
289            if (this.left < left) {
290                this.left = left;
291            }
292            if (this.top < top) {
293                this.top = top;
294            }
295            if (this.right > right) {
296                this.right = right;
297            }
298            if (this.bottom > bottom) {
299                this.bottom = bottom;
300            }
301            return true;
302        }
303        return false;
304    }
305
306    /**
307     * If the specified rectangle intersects this rectangle, return true and set
308     * this rectangle to that intersection, otherwise return false and do not
309     * change this rectangle. No check is performed to see if either rectangle
310     * is empty. To just test for intersection, use intersects()
311     *
312     * @param r The rectangle being intersected with this rectangle.
313     * @return true if the specified rectangle and this rectangle intersect
314     *              (and this rectangle is then set to that intersection) else
315     *              return false and do not change this rectangle.
316     */
317    public boolean intersect(RectF r) {
318        return intersect(r.left, r.top, r.right, r.bottom);
319    }
320
321    /**
322     * If rectangles a and b intersect, return true and set this rectangle to
323     * that intersection, otherwise return false and do not change this
324     * rectangle. No check is performed to see if either rectangle is empty.
325     * To just test for intersection, use intersects()
326     *
327     * @param a The first rectangle being intersected with
328     * @param b The second rectangle being intersected with
329     * @return true iff the two specified rectangles intersect. If they do, set
330     *              this rectangle to that intersection. If they do not, return
331     *              false and do not change this rectangle.
332     */
333    public boolean setIntersect(RectF a, RectF b) {
334        if (a.left < b.right && b.left < a.right
335                && a.top < b.bottom && b.top < a.bottom) {
336            left = Math.max(a.left, b.left);
337            top = Math.max(a.top, b.top);
338            right = Math.min(a.right, b.right);
339            bottom = Math.min(a.bottom, b.bottom);
340            return true;
341        }
342        return false;
343    }
344
345    /**
346     * Returns true if this rectangle intersects the specified rectangle.
347     * In no event is this rectangle modified. No check is performed to see
348     * if either rectangle is empty. To record the intersection, use intersect()
349     * or setIntersect().
350     *
351     * @param left The left side of the rectangle being tested for intersection
352     * @param top The top of the rectangle being tested for intersection
353     * @param right The right side of the rectangle being tested for
354     *              intersection
355     * @param bottom The bottom of the rectangle being tested for intersection
356     * @return true iff the specified rectangle intersects this rectangle. In
357     *              no event is this rectangle modified.
358     */
359    public boolean intersects(float left, float top, float right,
360                              float bottom) {
361        return this.left < right && left < this.right
362                && this.top < bottom && top < this.bottom;
363    }
364
365    /**
366     * Returns true iff the two specified rectangles intersect. In no event are
367     * either of the rectangles modified. To record the intersection,
368     * use intersect() or setIntersect().
369     *
370     * @param a The first rectangle being tested for intersection
371     * @param b The second rectangle being tested for intersection
372     * @return true iff the two specified rectangles intersect. In no event are
373     *              either of the rectangles modified.
374     */
375    public static boolean intersects(RectF a, RectF b) {
376        return a.left < b.right && b.left < a.right
377                && a.top < b.bottom && b.top < a.bottom;
378    }
379
380    /**
381     * Set the dst integer Rect by rounding this rectangle's coordinates
382     * to their nearest integer values.
383     */
384    public void round(Rect dst) {
385        dst.set(FastMath.round(left), FastMath.round(top),
386                FastMath.round(right), FastMath.round(bottom));
387    }
388
389    /**
390     * Set the dst integer Rect by rounding "out" this rectangle, choosing the
391     * floor of top and left, and the ceiling of right and bottom.
392     */
393    public void roundOut(Rect dst) {
394        dst.set((int) FloatMath.floor(left), (int) FloatMath.floor(top),
395                (int) FloatMath.ceil(right), (int) FloatMath.ceil(bottom));
396    }
397
398    /**
399     * Update this Rect to enclose itself and the specified rectangle. If the
400     * specified rectangle is empty, nothing is done. If this rectangle is empty
401     * it is set to the specified rectangle.
402     *
403     * @param left The left edge being unioned with this rectangle
404     * @param top The top edge being unioned with this rectangle
405     * @param right The right edge being unioned with this rectangle
406     * @param bottom The bottom edge being unioned with this rectangle
407     */
408    public void union(float left, float top, float right, float bottom) {
409        if ((left < right) && (top < bottom)) {
410            if ((this.left < this.right) && (this.top < this.bottom)) {
411                if (this.left > left)
412                    this.left = left;
413                if (this.top > top)
414                    this.top = top;
415                if (this.right < right)
416                    this.right = right;
417                if (this.bottom < bottom)
418                    this.bottom = bottom;
419            } else {
420                this.left = left;
421                this.top = top;
422                this.right = right;
423                this.bottom = bottom;
424            }
425        }
426    }
427
428    /**
429     * Update this Rect to enclose itself and the specified rectangle. If the
430     * specified rectangle is empty, nothing is done. If this rectangle is empty
431     * it is set to the specified rectangle.
432     *
433     * @param r The rectangle being unioned with this rectangle
434     */
435    public void union(RectF r) {
436        union(r.left, r.top, r.right, r.bottom);
437    }
438
439    /**
440     * Update this Rect to enclose itself and the [x,y] coordinate. There is no
441     * check to see that this rectangle is non-empty.
442     *
443     * @param x The x coordinate of the point to add to the rectangle
444     * @param y The y coordinate of the point to add to the rectangle
445     */
446    public void union(float x, float y) {
447        if (x < left) {
448            left = x;
449        } else if (x > right) {
450            right = x;
451        }
452        if (y < top) {
453            top = y;
454        } else if (y > bottom) {
455            bottom = y;
456        }
457    }
458
459    /**
460     * Swap top/bottom or left/right if there are flipped (i.e. left > right
461     * and/or top > bottom). This can be called if
462     * the edges are computed separately, and may have crossed over each other.
463     * If the edges are already correct (i.e. left <= right and top <= bottom)
464     * then nothing is done.
465     */
466    public void sort() {
467        if (left > right) {
468            float temp = left;
469            left = right;
470            right = temp;
471        }
472        if (top > bottom) {
473            float temp = top;
474            top = bottom;
475            bottom = temp;
476        }
477    }
478}
479