1 /* 2 * Copyright (c) 1995, 2015, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package com.javafx.experiments.utils3d.geom; 34 35 /** 36 * A <code>Rectangle</code> specifies an area in a coordinate space that is 37 * enclosed by the <code>Rectangle</code> object's upper-left point 38 * {@code (x,y)} 39 * in the coordinate space, its width, and its height. 40 * <p> 41 * A <code>Rectangle</code> object's <code>width</code> and 42 * <code>height</code> are <code>public</code> fields. The constructors 43 * that create a <code>Rectangle</code>, and the methods that can modify 44 * one, do not prevent setting a negative value for width or height. 45 * <p> 46 * <a name="Empty"> 47 * A {@code Rectangle} whose width or height is exactly zero has location 48 * along those axes with zero dimension, but is otherwise considered empty. 49 * The {@link #isEmpty} method will return true for such a {@code Rectangle}. 50 * Methods which test if an empty {@code Rectangle} contains or intersects 51 * a point or rectangle will always return false if either dimension is zero. 52 * Methods which combine such a {@code Rectangle} with a point or rectangle 53 * will include the location of the {@code Rectangle} on that axis in the 54 * result as if the {@link #add(Point)} method were being called. 55 * </a> 56 * <p> 57 * <a name="NonExistant"> 58 * A {@code Rectangle} whose width or height is negative has neither 59 * location nor dimension along those axes with negative dimensions. 60 * Such a {@code Rectangle} is treated as non-existant along those axes. 61 * Such a {@code Rectangle} is also empty with respect to containment 62 * calculations and methods which test if it contains or intersects a 63 * point or rectangle will always return false. 64 * Methods which combine such a {@code Rectangle} with a point or rectangle 65 * will ignore the {@code Rectangle} entirely in generating the result. 66 * If two {@code Rectangle} objects are combined and each has a negative 67 * dimension, the result will have at least one negative dimension. 68 * </a> 69 * <p> 70 * Methods which affect only the location of a {@code Rectangle} will 71 * operate on its location regardless of whether or not it has a negative 72 * or zero dimension along either axis. 73 * <p> 74 * Note that a {@code Rectangle} constructed with the default no-argument 75 * constructor will have dimensions of {@code 0x0} and therefore be empty. 76 * That {@code Rectangle} will still have a location of {@code (0,0)} and 77 * will contribute that location to the union and add operations. 78 * Code attempting to accumulate the bounds of a set of points should 79 * therefore initially construct the {@code Rectangle} with a specifically 80 * negative width and height or it should use the first point in the set 81 * to construct the {@code Rectangle}. 82 * For example: 83 * <pre> 84 * Rectangle bounds = new Rectangle(0, 0, -1, -1); 85 * for (int i = 0; i < points.length; i++) { 86 * bounds.add(points[i]); 87 * } 88 * </pre> 89 * or if we know that the points array contains at least one point: 90 * <pre> 91 * Rectangle bounds = new Rectangle(points[0]); 92 * for (int i = 1; i < points.length; i++) { 93 * bounds.add(points[i]); 94 * } 95 * </pre> 96 * <p> 97 * This class uses 32-bit integers to store its location and dimensions. 98 * Frequently operations may produce a result that exceeds the range of 99 * a 32-bit integer. 100 * The methods will calculate their results in a way that avoids any 101 * 32-bit overflow for intermediate results and then choose the best 102 * representation to store the final results back into the 32-bit fields 103 * which hold the location and dimensions. 104 * The location of the result will be stored into the {@link #x} and 105 * {@link #y} fields by clipping the true result to the nearest 32-bit value. 106 * The values stored into the {@link #width} and {@link #height} dimension 107 * fields will be chosen as the 32-bit values that encompass the largest 108 * part of the true result as possible. 109 * Generally this means that the dimension will be clipped independently 110 * to the range of 32-bit integers except that if the location had to be 111 * moved to store it into its pair of 32-bit fields then the dimensions 112 * will be adjusted relative to the "best representation" of the location. 113 * If the true result had a negative dimension and was therefore 114 * non-existant along one or both axes, the stored dimensions will be 115 * negative numbers in those axes. 116 * If the true result had a location that could be represented within 117 * the range of 32-bit integers, but zero dimension along one or both 118 * axes, then the stored dimensions will be zero in those axes. 119 */ 120 public class Rectangle { 121 122 /** 123 * The X coordinate of the upper-left corner of the <code>Rectangle</code>. 124 */ 125 public int x; 126 127 /** 128 * The Y coordinate of the upper-left corner of the <code>Rectangle</code>. 129 */ 130 public int y; 131 132 /** 133 * The width of the <code>Rectangle</code>. 134 */ 135 public int width; 136 137 /** 138 * The height of the <code>Rectangle</code>. 139 */ 140 public int height; 141 142 /** 143 * Constructs a new <code>Rectangle</code> whose upper-left corner 144 * is at (0, 0) in the coordinate space, and whose width and 145 * height are both zero. 146 */ 147 public Rectangle() { 148 this(0, 0, 0, 0); 149 } 150 151 /** 152 * Constructs a new <code>Rectangle</code>, initialized to match 153 * the values of the specified <code>Rectangle</code>. 154 * @param r the <code>Rectangle</code> from which to copy initial values 155 * to a newly constructed <code>Rectangle</code> 156 */ 157 public Rectangle(BaseBounds b) { 158 setBounds(b); 159 } 160 161 /** 162 * Constructs a new <code>Rectangle</code>, initialized to match 163 * the values of the specified <code>BaseBounds</code>. Since BaseBounds has 164 * float values, the Rectangle will be created such that the bounding rectangle 165 * of the specified BaseBounds would always lie within the bounding box 166 * specified by this Rectangle. 167 * @param r the <code>BaseBounds</code> from which to copy initial values 168 * to a newly constructed <code>Rectangle</code> 169 */ 170 public Rectangle(Rectangle r) { 171 this(r.x, r.y, r.width, r.height); 172 } 173 174 /** 175 * Constructs a new <code>Rectangle</code> whose upper-left corner is 176 * specified as 177 * {@code (x,y)} and whose width and height 178 * are specified by the arguments of the same name. 179 * @param x the specified X coordinate 180 * @param y the specified Y coordinate 181 * @param width the width of the <code>Rectangle</code> 182 * @param height the height of the <code>Rectangle</code> 183 */ 184 public Rectangle(int x, int y, int width, int height) { 185 this.x = x; 186 this.y = y; 187 this.width = width; 188 this.height = height; 189 } 190 191 /** 192 * Constructs a new <code>Rectangle</code> whose upper-left corner 193 * is at (0, 0) in the coordinate space, and whose width and 194 * height are specified by the arguments of the same name. 195 * @param width the width of the <code>Rectangle</code> 196 * @param height the height of the <code>Rectangle</code> 197 */ 198 public Rectangle(int width, int height) { 199 this(0, 0, width, height); 200 } 201 202 /** 203 * Sets the bounding <code>Rectangle</code> of this <code>Rectangle</code> 204 * to match the specified <code>Rectangle</code>. 205 * <p> 206 * This method is included for completeness, to parallel the 207 * <code>setBounds</code> method of <code>Component</code>. 208 * @param r the specified <code>Rectangle</code> 209 * @see #getBounds 210 * @see java.awt.Component#setBounds(java.awt.Rectangle) 211 */ 212 public void setBounds(Rectangle r) { 213 setBounds(r.x, r.y, r.width, r.height); 214 } 215 216 /** 217 * Sets the bounding <code>Rectangle</code> of this 218 * <code>Rectangle</code> to the specified 219 * <code>x</code>, <code>y</code>, <code>width</code>, 220 * and <code>height</code>. 221 * <p> 222 * This method is included for completeness, to parallel the 223 * <code>setBounds</code> method of <code>Component</code>. 224 * @param x the new X coordinate for the upper-left 225 * corner of this <code>Rectangle</code> 226 * @param y the new Y coordinate for the upper-left 227 * corner of this <code>Rectangle</code> 228 * @param width the new width for this <code>Rectangle</code> 229 * @param height the new height for this <code>Rectangle</code> 230 * @see #getBounds 231 * @see java.awt.Component#setBounds(int, int, int, int) 232 */ 233 public void setBounds(int x, int y, int width, int height) { 234 reshape(x, y, width, height); 235 } 236 237 public void setBounds(BaseBounds b) { 238 x = (int) Math.floor(b.getMinX()); 239 y = (int) Math.floor(b.getMinY()); 240 int x2 = (int) Math.ceil(b.getMaxX()); 241 int y2 = (int) Math.ceil(b.getMaxY()); 242 width = x2 - x; 243 height = y2 - y; 244 } 245 246 /** 247 * Checks whether or not this <code>Rectangle</code> contains the 248 * point at the specified location {@code (cx,cy)}. 249 * 250 * @param cx the specified X coordinate 251 * @param cy the specified Y coordinate 252 * @return <code>true</code> if the point 253 * {@code (cx,cy)} is inside this 254 * <code>Rectangle</code>; 255 * <code>false</code> otherwise. 256 */ 257 public boolean contains(int cx, int cy) { 258 int tw = this.width; 259 int th = this.height; 260 if ((tw | th) < 0) { 261 // At least one of the dimensions is negative... 262 return false; 263 } 264 // Note: if either dimension is zero, tests below must return false... 265 int tx = this.x; 266 int ty = this.y; 267 if (cx < tx || cy < ty) { 268 return false; 269 } 270 tw += tx; 271 th += ty; 272 // overflow || intersect 273 return ((tw < tx || tw > cx) && 274 (th < ty || th > cy)); 275 } 276 277 /** 278 * Checks whether or not this <code>Rectangle</code> entirely contains 279 * the specified <code>Rectangle</code>. 280 * 281 * @param r the specified <code>Rectangle</code> 282 * @return <code>true</code> if the <code>Rectangle</code> 283 * is contained entirely inside this <code>Rectangle</code>; 284 * <code>false</code> otherwise 285 */ 286 public boolean contains(Rectangle r) { 287 return contains(r.x, r.y, r.width, r.height); 288 } 289 290 /** 291 * Checks whether this <code>Rectangle</code> entirely contains 292 * the <code>Rectangle</code> 293 * at the specified location {@code (cx,cy)} with the 294 * specified dimensions {@code (cw,ch)}. 295 * @param cx the specified X coordinate 296 * @param cy the specified Y coordinate 297 * @param cw the width of the <code>Rectangle</code> 298 * @param ch the height of the <code>Rectangle</code> 299 * @return <code>true</code> if the <code>Rectangle</code> specified by 300 * {@code (cx, cy, cw, ch)} 301 * is entirely enclosed inside this <code>Rectangle</code>; 302 * <code>false</code> otherwise. 303 */ 304 public boolean contains(int cx, int cy, int cw, int ch) { 305 int tw = this.width; 306 int th = this.height; 307 if ((tw | th | cw | ch) < 0) { 308 // At least one of the dimensions is negative... 309 return false; 310 } 311 // Note: if any dimension is zero, tests below must return false... 312 int tx = this.x; 313 int ty = this.y; 314 if (cx < tx || cy < ty) { 315 return false; 316 } 317 tw += tx; 318 cw += cx; 319 if (cw <= cx) { 320 // cx+cw overflowed or cw was zero, return false if... 321 // either original tw or cw was zero or 322 // tx+tw did not overflow or 323 // the overflowed cx+cw is smaller than the overflowed tx+tw 324 if (tw >= tx || cw > tw) return false; 325 } else { 326 // cx+cw did not overflow and cw was not zero, return false if... 327 // original tw was zero or 328 // tx+tw did not overflow and tx+tw is smaller than cx+cw 329 if (tw >= tx && cw > tw) return false; 330 } 331 th += ty; 332 ch += cy; 333 if (ch <= cy) { 334 if (th >= ty || ch > th) return false; 335 } else { 336 if (th >= ty && ch > th) return false; 337 } 338 return true; 339 } 340 341 public Rectangle intersection(Rectangle r) { 342 Rectangle ret = new Rectangle(this); 343 ret.intersectWith(r); 344 return ret; 345 } 346 347 public void intersectWith(Rectangle r) { 348 if (r == null) { 349 return; 350 } 351 int tx1 = this.x; 352 int ty1 = this.y; 353 int rx1 = r.x; 354 int ry1 = r.y; 355 long tx2 = tx1; tx2 += this.width; 356 long ty2 = ty1; ty2 += this.height; 357 long rx2 = rx1; rx2 += r.width; 358 long ry2 = ry1; ry2 += r.height; 359 if (tx1 < rx1) tx1 = rx1; 360 if (ty1 < ry1) ty1 = ry1; 361 if (tx2 > rx2) tx2 = rx2; 362 if (ty2 > ry2) ty2 = ry2; 363 tx2 -= tx1; 364 ty2 -= ty1; 365 // tx2,ty2 will never overflow (they will never be 366 // larger than the smallest of the two source w,h) 367 // they might underflow, though... 368 if (tx2 < Integer.MIN_VALUE) tx2 = Integer.MIN_VALUE; 369 if (ty2 < Integer.MIN_VALUE) ty2 = Integer.MIN_VALUE; 370 setBounds(tx1, ty1, (int) tx2, (int) ty2); 371 } 372 373 /** 374 * Translates this <code>Rectangle</code> the indicated distance, 375 * to the right along the X coordinate axis, and 376 * downward along the Y coordinate axis. 377 * @param dx the distance to move this <code>Rectangle</code> 378 * along the X axis 379 * @param dy the distance to move this <code>Rectangle</code> 380 * along the Y axis 381 * @see java.awt.Rectangle#setLocation(int, int) 382 * @see java.awt.Rectangle#setLocation(java.awt.Point) 383 */ 384 public void translate(int dx, int dy) { 385 int oldv = this.x; 386 int newv = oldv + dx; 387 if (dx < 0) { 388 // moving leftward 389 if (newv > oldv) { 390 // negative overflow 391 // Only adjust width if it was valid (>= 0). 392 if (width >= 0) { 393 // The right edge is now conceptually at 394 // newv+width, but we may move newv to prevent 395 // overflow. But we want the right edge to 396 // remain at its new location in spite of the 397 // clipping. Think of the following adjustment 398 // conceptually the same as: 399 // width += newv; newv = MIN_VALUE; width -= newv; 400 width += newv - Integer.MIN_VALUE; 401 // width may go negative if the right edge went past 402 // MIN_VALUE, but it cannot overflow since it cannot 403 // have moved more than MIN_VALUE and any non-negative 404 // number + MIN_VALUE does not overflow. 405 } 406 newv = Integer.MIN_VALUE; 407 } 408 } else { 409 // moving rightward (or staying still) 410 if (newv < oldv) { 411 // positive overflow 412 if (width >= 0) { 413 // Conceptually the same as: 414 // width += newv; newv = MAX_VALUE; width -= newv; 415 width += newv - Integer.MAX_VALUE; 416 // With large widths and large displacements 417 // we may overflow so we need to check it. 418 if (width < 0) width = Integer.MAX_VALUE; 419 } 420 newv = Integer.MAX_VALUE; 421 } 422 } 423 this.x = newv; 424 425 oldv = this.y; 426 newv = oldv + dy; 427 if (dy < 0) { 428 // moving upward 429 if (newv > oldv) { 430 // negative overflow 431 if (height >= 0) { 432 height += newv - Integer.MIN_VALUE; 433 // See above comment about no overflow in this case 434 } 435 newv = Integer.MIN_VALUE; 436 } 437 } else { 438 // moving downward (or staying still) 439 if (newv < oldv) { 440 // positive overflow 441 if (height >= 0) { 442 height += newv - Integer.MAX_VALUE; 443 if (height < 0) height = Integer.MAX_VALUE; 444 } 445 newv = Integer.MAX_VALUE; 446 } 447 } 448 this.y = newv; 449 } 450 451 public RectBounds toRectBounds() { 452 return new RectBounds(x, y, x+width, y+height); 453 } 454 455 /** 456 * Adds a point, specified by the integer arguments {@code newx,newy} 457 * to the bounds of this {@code Rectangle}. 458 * <p> 459 * If this {@code Rectangle} has any dimension less than zero, 460 * the rules for <a href=#NonExistant>non-existant</a> 461 * rectangles apply. 462 * In that case, the new bounds of this {@code Rectangle} will 463 * have a location equal to the specified coordinates and 464 * width and height equal to zero. 465 * <p> 466 * After adding a point, a call to <code>contains</code> with the 467 * added point as an argument does not necessarily return 468 * <code>true</code>. The <code>contains</code> method does not 469 * return <code>true</code> for points on the right or bottom 470 * edges of a <code>Rectangle</code>. Therefore, if the added point 471 * falls on the right or bottom edge of the enlarged 472 * <code>Rectangle</code>, <code>contains</code> returns 473 * <code>false</code> for that point. 474 * If the specified point must be contained within the new 475 * {@code Rectangle}, a 1x1 rectangle should be added instead: 476 * <pre> 477 * r.add(newx, newy, 1, 1); 478 * </pre> 479 * @param newx the X coordinate of the new point 480 * @param newy the Y coordinate of the new point 481 */ 482 public void add(int newx, int newy) { 483 if ((width | height) < 0) { 484 this.x = newx; 485 this.y = newy; 486 this.width = this.height = 0; 487 return; 488 } 489 int x1 = this.x; 490 int y1 = this.y; 491 long x2 = this.width; 492 long y2 = this.height; 493 x2 += x1; 494 y2 += y1; 495 if (x1 > newx) x1 = newx; 496 if (y1 > newy) y1 = newy; 497 if (x2 < newx) x2 = newx; 498 if (y2 < newy) y2 = newy; 499 x2 -= x1; 500 y2 -= y1; 501 if (x2 > Integer.MAX_VALUE) x2 = Integer.MAX_VALUE; 502 if (y2 > Integer.MAX_VALUE) y2 = Integer.MAX_VALUE; 503 reshape(x1, y1, (int) x2, (int) y2); 504 } 505 506 /** 507 * Adds a <code>Rectangle</code> to this <code>Rectangle</code>. 508 * The resulting <code>Rectangle</code> is the union of the two 509 * rectangles. 510 * <p> 511 * If either {@code Rectangle} has any dimension less than 0, the 512 * result will have the dimensions of the other {@code Rectangle}. 513 * If both {@code Rectangle}s have at least one dimension less 514 * than 0, the result will have at least one dimension less than 0. 515 * <p> 516 * If either {@code Rectangle} has one or both dimensions equal 517 * to 0, the result along those axes with 0 dimensions will be 518 * equivalent to the results obtained by adding the corresponding 519 * origin coordinate to the result rectangle along that axis, 520 * similar to the operation of the {@link #add(Point)} method, 521 * but contribute no further dimension beyond that. 522 * <p> 523 * If the resulting {@code Rectangle} would have a dimension 524 * too large to be expressed as an {@code int}, the result 525 * will have a dimension of {@code Integer.MAX_VALUE} along 526 * that dimension. 527 * @param r the specified <code>Rectangle</code> 528 */ 529 public void add(Rectangle r) { 530 long tx2 = this.width; 531 long ty2 = this.height; 532 if ((tx2 | ty2) < 0) { 533 reshape(r.x, r.y, r.width, r.height); 534 } 535 long rx2 = r.width; 536 long ry2 = r.height; 537 if ((rx2 | ry2) < 0) { 538 return; 539 } 540 int tx1 = this.x; 541 int ty1 = this.y; 542 tx2 += tx1; 543 ty2 += ty1; 544 int rx1 = r.x; 545 int ry1 = r.y; 546 rx2 += rx1; 547 ry2 += ry1; 548 if (tx1 > rx1) tx1 = rx1; 549 if (ty1 > ry1) ty1 = ry1; 550 if (tx2 < rx2) tx2 = rx2; 551 if (ty2 < ry2) ty2 = ry2; 552 tx2 -= tx1; 553 ty2 -= ty1; 554 // tx2,ty2 will never underflow since both original 555 // rectangles were non-empty 556 // they might overflow, though... 557 if (tx2 > Integer.MAX_VALUE) tx2 = Integer.MAX_VALUE; 558 if (ty2 > Integer.MAX_VALUE) ty2 = Integer.MAX_VALUE; 559 reshape(tx1, ty1, (int) tx2, (int) ty2); 560 } 561 562 /** 563 * Resizes the <code>Rectangle</code> both horizontally and vertically. 564 * <p> 565 * This method modifies the <code>Rectangle</code> so that it is 566 * <code>h</code> units larger on both the left and right side, 567 * and <code>v</code> units larger at both the top and bottom. 568 * <p> 569 * The new <code>Rectangle</code> has {@code (x - h, y - v)} 570 * as its upper-left corner, 571 * width of {@code (width + 2h)}, 572 * and a height of {@code (height + 2v)}. 573 * <p> 574 * If negative values are supplied for <code>h</code> and 575 * <code>v</code>, the size of the <code>Rectangle</code> 576 * decreases accordingly. 577 * The {@code grow} method will check for integer overflow 578 * and underflow, but does not check whether the resulting 579 * values of {@code width} and {@code height} grow 580 * from negative to non-negative or shrink from non-negative 581 * to negative. 582 * @param h the horizontal expansion 583 * @param v the vertical expansion 584 */ 585 public void grow(int h, int v) { 586 long x0 = this.x; 587 long y0 = this.y; 588 long x1 = this.width; 589 long y1 = this.height; 590 x1 += x0; 591 y1 += y0; 592 593 x0 -= h; 594 y0 -= v; 595 x1 += h; 596 y1 += v; 597 598 if (x1 < x0) { 599 // Non-existant in X direction 600 // Final width must remain negative so subtract x0 before 601 // it is clipped so that we avoid the risk that the clipping 602 // of x0 will reverse the ordering of x0 and x1. 603 x1 -= x0; 604 if (x1 < Integer.MIN_VALUE) x1 = Integer.MIN_VALUE; 605 if (x0 < Integer.MIN_VALUE) x0 = Integer.MIN_VALUE; 606 else if (x0 > Integer.MAX_VALUE) x0 = Integer.MAX_VALUE; 607 } else { // (x1 >= x0) 608 // Clip x0 before we subtract it from x1 in case the clipping 609 // affects the representable area of the rectangle. 610 if (x0 < Integer.MIN_VALUE) x0 = Integer.MIN_VALUE; 611 else if (x0 > Integer.MAX_VALUE) x0 = Integer.MAX_VALUE; 612 x1 -= x0; 613 // The only way x1 can be negative now is if we clipped 614 // x0 against MIN and x1 is less than MIN - in which case 615 // we want to leave the width negative since the result 616 // did not intersect the representable area. 617 if (x1 < Integer.MIN_VALUE) x1 = Integer.MIN_VALUE; 618 else if (x1 > Integer.MAX_VALUE) x1 = Integer.MAX_VALUE; 619 } 620 621 if (y1 < y0) { 622 // Non-existant in Y direction 623 y1 -= y0; 624 if (y1 < Integer.MIN_VALUE) y1 = Integer.MIN_VALUE; 625 if (y0 < Integer.MIN_VALUE) y0 = Integer.MIN_VALUE; 626 else if (y0 > Integer.MAX_VALUE) y0 = Integer.MAX_VALUE; 627 } else { // (y1 >= y0) 628 if (y0 < Integer.MIN_VALUE) y0 = Integer.MIN_VALUE; 629 else if (y0 > Integer.MAX_VALUE) y0 = Integer.MAX_VALUE; 630 y1 -= y0; 631 if (y1 < Integer.MIN_VALUE) y1 = Integer.MIN_VALUE; 632 else if (y1 > Integer.MAX_VALUE) y1 = Integer.MAX_VALUE; 633 } 634 635 reshape((int) x0, (int) y0, (int) x1, (int) y1); 636 } 637 638 private void reshape(int x, int y, int width, int height) { 639 this.x = x; 640 this.y = y; 641 this.width = width; 642 this.height = height; 643 } 644 645 /** 646 * {@inheritDoc} 647 */ 648 public boolean isEmpty() { 649 return (width <= 0) || (height <= 0); 650 } 651 652 /** 653 * Checks whether two rectangles are equal. 654 * <p> 655 * The result is <code>true</code> if and only if the argument is not 656 * <code>null</code> and is a <code>Rectangle</code> object that has the 657 * same upper-left corner, width, and height as 658 * this <code>Rectangle</code>. 659 * @param obj the <code>Object</code> to compare with 660 * this <code>Rectangle</code> 661 * @return <code>true</code> if the objects are equal; 662 * <code>false</code> otherwise. 663 */ 664 @Override 665 public boolean equals(Object obj) { 666 if (obj instanceof Rectangle) { 667 Rectangle r = (Rectangle)obj; 668 return ((x == r.x) && 669 (y == r.y) && 670 (width == r.width) && 671 (height == r.height)); 672 } 673 return super.equals(obj); 674 } 675 676 @Override 677 public int hashCode() { 678 int bits = Float.floatToIntBits(x); 679 bits += Float.floatToIntBits(y) * 37; 680 bits += Float.floatToIntBits(width) * 43; 681 bits += Float.floatToIntBits(height) * 47; 682 return bits; 683 } 684 685 /** 686 * Returns a <code>String</code> representing this 687 * <code>Rectangle</code> and its values. 688 * @return a <code>String</code> representing this 689 * <code>Rectangle</code> object's coordinate and size values. 690 */ 691 @Override 692 public String toString() { 693 return getClass().getName() + "[x=" + x + ",y=" + y + ",width=" + width + ",height=" + height + "]"; 694 } 695 }