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