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