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