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