blob: 3361fa21e44b7f0ba498dfae69ef25a8db3be9e0 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Jake Whartondfda4ea2018-06-05 12:44:36 -040019import android.annotation.NonNull;
20import android.annotation.Nullable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.os.Parcel;
22import android.os.Parcelable;
Jake Whartondfda4ea2018-06-05 12:44:36 -040023
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import com.android.internal.util.FastMath;
25
Jake Whartondfda4ea2018-06-05 12:44:36 -040026import java.io.PrintWriter;
27
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028/**
29 * RectF holds four float coordinates for a rectangle. The rectangle is
30 * represented by the coordinates of its 4 edges (left, top, right bottom).
31 * These fields can be accessed directly. Use width() and height() to retrieve
32 * the rectangle's width and height. Note: most methods do not check to see that
33 * the coordinates are sorted correctly (i.e. left <= right and top <= bottom).
34 */
35public class RectF implements Parcelable {
36 public float left;
37 public float top;
38 public float right;
39 public float bottom;
40
41 /**
42 * Create a new empty RectF. All coordinates are initialized to 0.
43 */
44 public RectF() {}
45
46 /**
47 * Create a new rectangle with the specified coordinates. Note: no range
48 * checking is performed, so the caller must ensure that left <= right and
49 * top <= bottom.
50 *
Pin Ting852be162012-05-11 18:09:58 +080051 * @param left The X coordinate of the left side of the rectangle
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 * @param top The Y coordinate of the top of the rectangle
Pin Ting852be162012-05-11 18:09:58 +080053 * @param right The X coordinate of the right side of the rectangle
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 * @param bottom The Y coordinate of the bottom of the rectangle
55 */
56 public RectF(float left, float top, float right, float bottom) {
57 this.left = left;
58 this.top = top;
59 this.right = right;
60 this.bottom = bottom;
61 }
62
63 /**
64 * Create a new rectangle, initialized with the values in the specified
65 * rectangle (which is left unmodified).
66 *
67 * @param r The rectangle whose coordinates are copied into the new
68 * rectangle.
69 */
Jake Whartondfda4ea2018-06-05 12:44:36 -040070 public RectF(@Nullable RectF r) {
Romain Guydd4b1fe2012-09-12 17:28:02 -070071 if (r == null) {
72 left = top = right = bottom = 0.0f;
73 } else {
74 left = r.left;
75 top = r.top;
76 right = r.right;
77 bottom = r.bottom;
78 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 }
80
Jake Whartondfda4ea2018-06-05 12:44:36 -040081 public RectF(@Nullable Rect r) {
Romain Guydd4b1fe2012-09-12 17:28:02 -070082 if (r == null) {
83 left = top = right = bottom = 0.0f;
84 } else {
85 left = r.left;
86 top = r.top;
87 right = r.right;
88 bottom = r.bottom;
89 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 }
91
Romain Guy74d7ca12012-01-31 10:50:38 -080092 @Override
93 public boolean equals(Object o) {
94 if (this == o) return true;
95 if (o == null || getClass() != o.getClass()) return false;
96
Amith Yamasani3b577dd2012-03-09 11:53:31 -080097 RectF r = (RectF) o;
Romain Guy74d7ca12012-01-31 10:50:38 -080098 return left == r.left && top == r.top && right == r.right && bottom == r.bottom;
99 }
100
101 @Override
102 public int hashCode() {
103 int result = (left != +0.0f ? Float.floatToIntBits(left) : 0);
104 result = 31 * result + (top != +0.0f ? Float.floatToIntBits(top) : 0);
105 result = 31 * result + (right != +0.0f ? Float.floatToIntBits(right) : 0);
106 result = 31 * result + (bottom != +0.0f ? Float.floatToIntBits(bottom) : 0);
107 return result;
108 }
109
Jake Whartondfda4ea2018-06-05 12:44:36 -0400110 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 public String toString() {
112 return "RectF(" + left + ", " + top + ", "
113 + right + ", " + bottom + ")";
114 }
Dianne Hackbornd040edb2011-08-31 12:47:58 -0700115
116 /**
117 * Return a string representation of the rectangle in a compact form.
118 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400119 @NonNull
Dianne Hackbornd040edb2011-08-31 12:47:58 -0700120 public String toShortString() {
121 return toShortString(new StringBuilder(32));
122 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123
124 /**
Dianne Hackbornd040edb2011-08-31 12:47:58 -0700125 * Return a string representation of the rectangle in a compact form.
126 * @hide
127 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400128 @NonNull
129 public String toShortString(@NonNull StringBuilder sb) {
Dianne Hackbornd040edb2011-08-31 12:47:58 -0700130 sb.setLength(0);
131 sb.append('['); sb.append(left); sb.append(',');
132 sb.append(top); sb.append("]["); sb.append(right);
133 sb.append(','); sb.append(bottom); sb.append(']');
134 return sb.toString();
135 }
136
137 /**
138 * Print short representation to given writer.
139 * @hide
140 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400141 public void printShortString(@NonNull PrintWriter pw) {
Dianne Hackbornd040edb2011-08-31 12:47:58 -0700142 pw.print('['); pw.print(left); pw.print(',');
143 pw.print(top); pw.print("]["); pw.print(right);
144 pw.print(','); pw.print(bottom); pw.print(']');
145 }
146
147 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 * Returns true if the rectangle is empty (left >= right or top >= bottom)
149 */
150 public final boolean isEmpty() {
151 return left >= right || top >= bottom;
152 }
153
154 /**
155 * @return the rectangle's width. This does not check for a valid rectangle
156 * (i.e. left <= right) so the result may be negative.
157 */
158 public final float width() {
159 return right - left;
160 }
161
162 /**
163 * @return the rectangle's height. This does not check for a valid rectangle
164 * (i.e. top <= bottom) so the result may be negative.
165 */
166 public final float height() {
167 return bottom - top;
168 }
169
170 /**
171 * @return the horizontal center of the rectangle. This does not check for
172 * a valid rectangle (i.e. left <= right)
173 */
174 public final float centerX() {
175 return (left + right) * 0.5f;
176 }
177
178 /**
179 * @return the vertical center of the rectangle. This does not check for
180 * a valid rectangle (i.e. top <= bottom)
181 */
182 public final float centerY() {
183 return (top + bottom) * 0.5f;
184 }
185
186 /**
187 * Set the rectangle to (0,0,0,0)
188 */
189 public void setEmpty() {
190 left = right = top = bottom = 0;
191 }
192
193 /**
194 * Set the rectangle's coordinates to the specified values. Note: no range
195 * checking is performed, so it is up to the caller to ensure that
196 * left <= right and top <= bottom.
197 *
Pin Ting852be162012-05-11 18:09:58 +0800198 * @param left The X coordinate of the left side of the rectangle
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 * @param top The Y coordinate of the top of the rectangle
Pin Ting852be162012-05-11 18:09:58 +0800200 * @param right The X coordinate of the right side of the rectangle
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 * @param bottom The Y coordinate of the bottom of the rectangle
202 */
203 public void set(float left, float top, float right, float bottom) {
204 this.left = left;
205 this.top = top;
206 this.right = right;
207 this.bottom = 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 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400216 public void set(@NonNull RectF src) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 this.left = src.left;
218 this.top = src.top;
219 this.right = src.right;
220 this.bottom = src.bottom;
221 }
222
223 /**
224 * Copy the coordinates from src into this rectangle.
225 *
226 * @param src The rectangle whose coordinates are copied into this
227 * rectangle.
228 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400229 public void set(@NonNull Rect src) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 this.left = src.left;
231 this.top = src.top;
232 this.right = src.right;
233 this.bottom = src.bottom;
234 }
235
236 /**
237 * Offset the rectangle by adding dx to its left and right coordinates, and
238 * adding dy to its top and bottom coordinates.
239 *
240 * @param dx The amount to add to the rectangle's left and right coordinates
241 * @param dy The amount to add to the rectangle's top and bottom coordinates
242 */
243 public void offset(float dx, float dy) {
244 left += dx;
245 top += dy;
246 right += dx;
247 bottom += dy;
248 }
249
250 /**
251 * Offset the rectangle to a specific (left, top) position,
252 * keeping its width and height the same.
253 *
254 * @param newLeft The new "left" coordinate for the rectangle
255 * @param newTop The new "top" coordinate for the rectangle
256 */
257 public void offsetTo(float newLeft, float newTop) {
258 right += newLeft - left;
259 bottom += newTop - top;
260 left = newLeft;
261 top = newTop;
262 }
263
264 /**
265 * Inset the rectangle by (dx,dy). If dx is positive, then the sides are
266 * moved inwards, making the rectangle narrower. If dx is negative, then the
267 * sides are moved outwards, making the rectangle wider. The same holds true
268 * for dy and the top and bottom.
269 *
270 * @param dx The amount to add(subtract) from the rectangle's left(right)
271 * @param dy The amount to add(subtract) from the rectangle's top(bottom)
272 */
273 public void inset(float dx, float dy) {
274 left += dx;
275 top += dy;
276 right -= dx;
277 bottom -= dy;
278 }
279
280 /**
281 * Returns true if (x,y) is inside the rectangle. The left and top are
282 * considered to be inside, while the right and bottom are not. This means
283 * that for a x,y to be contained: left <= x < right and top <= y < bottom.
284 * An empty rectangle never contains any point.
285 *
286 * @param x The X coordinate of the point being tested for containment
287 * @param y The Y coordinate of the point being tested for containment
288 * @return true iff (x,y) are contained by the rectangle, where containment
289 * means left <= x < right and top <= y < bottom
290 */
291 public boolean contains(float x, float y) {
292 return left < right && top < bottom // check for empty first
293 && x >= left && x < right && y >= top && y < bottom;
294 }
295
296 /**
297 * Returns true iff the 4 specified sides of a rectangle are inside or equal
298 * to this rectangle. i.e. is this rectangle a superset of the specified
299 * rectangle. An empty rectangle never contains another rectangle.
300 *
301 * @param left The left side of the rectangle being tested for containment
302 * @param top The top of the rectangle being tested for containment
303 * @param right The right side of the rectangle being tested for containment
304 * @param bottom The bottom of the rectangle being tested for containment
305 * @return true iff the the 4 specified sides of a rectangle are inside or
306 * equal to this rectangle
307 */
308 public boolean contains(float left, float top, float right, float bottom) {
309 // check for empty first
310 return this.left < this.right && this.top < this.bottom
311 // now check for containment
312 && this.left <= left && this.top <= top
313 && this.right >= right && this.bottom >= bottom;
314 }
315
316 /**
317 * Returns true iff the specified rectangle r is inside or equal to this
318 * rectangle. An empty rectangle never contains another rectangle.
319 *
320 * @param r The rectangle being tested for containment.
321 * @return true iff the specified rectangle r is inside or equal to this
322 * rectangle
323 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400324 public boolean contains(@NonNull RectF r) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 // check for empty first
326 return this.left < this.right && this.top < this.bottom
327 // now check for containment
328 && left <= r.left && top <= r.top
329 && right >= r.right && bottom >= r.bottom;
330 }
331
332 /**
333 * If the rectangle specified by left,top,right,bottom intersects this
334 * rectangle, return true and set this rectangle to that intersection,
335 * otherwise return false and do not change this rectangle. No check is
336 * performed to see if either rectangle is empty. Note: To just test for
337 * intersection, use intersects()
338 *
339 * @param left The left side of the rectangle being intersected with this
340 * rectangle
341 * @param top The top of the rectangle being intersected with this rectangle
342 * @param right The right side of the rectangle being intersected with this
343 * rectangle.
344 * @param bottom The bottom of the rectangle being intersected with this
345 * 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(float left, float top, float right, float bottom) {
351 if (this.left < right && left < this.right
352 && this.top < bottom && top < this.bottom) {
353 if (this.left < left) {
354 this.left = left;
355 }
356 if (this.top < top) {
357 this.top = top;
358 }
359 if (this.right > right) {
360 this.right = right;
361 }
362 if (this.bottom > bottom) {
363 this.bottom = bottom;
364 }
365 return true;
366 }
367 return false;
368 }
369
370 /**
371 * If the specified rectangle intersects this rectangle, return true and set
372 * this rectangle to that intersection, otherwise return false and do not
373 * change this rectangle. No check is performed to see if either rectangle
374 * is empty. To just test for intersection, use intersects()
375 *
376 * @param r The rectangle being intersected with this rectangle.
377 * @return true if the specified rectangle and this rectangle intersect
378 * (and this rectangle is then set to that intersection) else
379 * return false and do not change this rectangle.
380 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400381 public boolean intersect(@NonNull RectF r) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 return intersect(r.left, r.top, r.right, r.bottom);
383 }
384
385 /**
386 * If rectangles a and b intersect, return true and set this rectangle to
387 * that intersection, otherwise return false and do not change this
388 * rectangle. No check is performed to see if either rectangle is empty.
389 * To just test for intersection, use intersects()
390 *
391 * @param a The first rectangle being intersected with
392 * @param b The second rectangle being intersected with
393 * @return true iff the two specified rectangles intersect. If they do, set
394 * this rectangle to that intersection. If they do not, return
395 * false and do not change this rectangle.
396 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400397 public boolean setIntersect(@NonNull RectF a, @NonNull RectF b) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 if (a.left < b.right && b.left < a.right
399 && a.top < b.bottom && b.top < a.bottom) {
400 left = Math.max(a.left, b.left);
401 top = Math.max(a.top, b.top);
402 right = Math.min(a.right, b.right);
403 bottom = Math.min(a.bottom, b.bottom);
404 return true;
405 }
406 return false;
407 }
408
409 /**
410 * Returns true if this rectangle intersects the specified rectangle.
411 * In no event is this rectangle modified. No check is performed to see
412 * if either rectangle is empty. To record the intersection, use intersect()
413 * or setIntersect().
414 *
415 * @param left The left side of the rectangle being tested for intersection
416 * @param top The top of the rectangle being tested for intersection
417 * @param right The right side of the rectangle being tested for
418 * intersection
419 * @param bottom The bottom of the rectangle being tested for intersection
420 * @return true iff the specified rectangle intersects this rectangle. In
421 * no event is this rectangle modified.
422 */
423 public boolean intersects(float left, float top, float right,
424 float bottom) {
425 return this.left < right && left < this.right
426 && this.top < bottom && top < this.bottom;
427 }
428
429 /**
430 * Returns true iff the two specified rectangles intersect. In no event are
431 * either of the rectangles modified. To record the intersection,
432 * use intersect() or setIntersect().
433 *
434 * @param a The first rectangle being tested for intersection
435 * @param b The second rectangle being tested for intersection
436 * @return true iff the two specified rectangles intersect. In no event are
437 * either of the rectangles modified.
438 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400439 public static boolean intersects(@NonNull RectF a, @NonNull RectF b) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 return a.left < b.right && b.left < a.right
441 && a.top < b.bottom && b.top < a.bottom;
442 }
443
444 /**
445 * Set the dst integer Rect by rounding this rectangle's coordinates
446 * to their nearest integer values.
447 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400448 public void round(@NonNull Rect dst) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 dst.set(FastMath.round(left), FastMath.round(top),
450 FastMath.round(right), FastMath.round(bottom));
451 }
452
453 /**
454 * Set the dst integer Rect by rounding "out" this rectangle, choosing the
455 * floor of top and left, and the ceiling of right and bottom.
456 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400457 public void roundOut(@NonNull Rect dst) {
Neil Fuller33253a42014-10-01 11:55:10 +0100458 dst.set((int) Math.floor(left), (int) Math.floor(top),
459 (int) Math.ceil(right), (int) Math.ceil(bottom));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 }
461
462 /**
463 * Update this Rect to enclose itself and the specified rectangle. If the
464 * specified rectangle is empty, nothing is done. If this rectangle is empty
465 * it is set to the specified rectangle.
466 *
467 * @param left The left edge being unioned with this rectangle
468 * @param top The top edge being unioned with this rectangle
469 * @param right The right edge being unioned with this rectangle
470 * @param bottom The bottom edge being unioned with this rectangle
471 */
472 public void union(float left, float top, float right, float bottom) {
473 if ((left < right) && (top < bottom)) {
474 if ((this.left < this.right) && (this.top < this.bottom)) {
475 if (this.left > left)
476 this.left = left;
477 if (this.top > top)
478 this.top = top;
479 if (this.right < right)
480 this.right = right;
481 if (this.bottom < bottom)
482 this.bottom = bottom;
483 } else {
484 this.left = left;
485 this.top = top;
486 this.right = right;
487 this.bottom = bottom;
488 }
489 }
490 }
491
492 /**
493 * Update this Rect to enclose itself and the specified rectangle. If the
494 * specified rectangle is empty, nothing is done. If this rectangle is empty
495 * it is set to the specified rectangle.
496 *
497 * @param r The rectangle being unioned with this rectangle
498 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400499 public void union(@NonNull RectF r) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 union(r.left, r.top, r.right, r.bottom);
501 }
502
503 /**
504 * Update this Rect to enclose itself and the [x,y] coordinate. There is no
505 * check to see that this rectangle is non-empty.
506 *
507 * @param x The x coordinate of the point to add to the rectangle
508 * @param y The y coordinate of the point to add to the rectangle
509 */
510 public void union(float x, float y) {
511 if (x < left) {
512 left = x;
513 } else if (x > right) {
514 right = x;
515 }
516 if (y < top) {
517 top = y;
518 } else if (y > bottom) {
519 bottom = y;
520 }
521 }
522
523 /**
524 * Swap top/bottom or left/right if there are flipped (i.e. left > right
525 * and/or top > bottom). This can be called if
526 * the edges are computed separately, and may have crossed over each other.
527 * If the edges are already correct (i.e. left <= right and top <= bottom)
528 * then nothing is done.
529 */
530 public void sort() {
531 if (left > right) {
532 float temp = left;
533 left = right;
534 right = temp;
535 }
536 if (top > bottom) {
537 float temp = top;
538 top = bottom;
539 bottom = temp;
540 }
541 }
542
543 /**
544 * Parcelable interface methods
545 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400546 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 public int describeContents() {
548 return 0;
549 }
550
551 /**
552 * Write this rectangle to the specified parcel. To restore a rectangle from
553 * a parcel, use readFromParcel()
554 * @param out The parcel to write the rectangle's coordinates into
555 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400556 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 public void writeToParcel(Parcel out, int flags) {
558 out.writeFloat(left);
559 out.writeFloat(top);
560 out.writeFloat(right);
561 out.writeFloat(bottom);
562 }
563
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700564 public static final @android.annotation.NonNull Parcelable.Creator<RectF> CREATOR = new Parcelable.Creator<RectF>() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 /**
566 * Return a new rectangle from the data in the specified parcel.
567 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400568 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 public RectF createFromParcel(Parcel in) {
570 RectF r = new RectF();
571 r.readFromParcel(in);
572 return r;
573 }
574
575 /**
576 * Return an array of rectangles of the specified size.
577 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400578 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 public RectF[] newArray(int size) {
580 return new RectF[size];
581 }
582 };
583
584 /**
585 * Set the rectangle's coordinates from the data stored in the specified
586 * parcel. To write a rectangle to a parcel, call writeToParcel().
587 *
588 * @param in The parcel to read the rectangle's coordinates from
589 */
Jake Whartondfda4ea2018-06-05 12:44:36 -0400590 public void readFromParcel(@NonNull Parcel in) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 left = in.readFloat();
592 top = in.readFloat();
593 right = in.readFloat();
594 bottom = in.readFloat();
595 }
Phil Weaverc2e28932016-12-08 12:29:25 -0800596
597 /**
598 * Scales up the rect by the given scale.
599 * @hide
600 */
601 public void scale(float scale) {
602 if (scale != 1.0f) {
603 left = left * scale;
604 top = top * scale ;
605 right = right * scale;
606 bottom = bottom * scale;
607 }
608 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609}