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