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