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