1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene;
  27 
  28 import com.sun.javafx.test.TestHelper;
  29 import javafx.geometry.BoundingBox;
  30 import javafx.scene.transform.Rotate;
  31 import static org.junit.Assert.assertEquals;
  32 import static org.junit.Assert.assertTrue;
  33 import javafx.geometry.Bounds;
  34 import javafx.geometry.Point2D;
  35 import javafx.scene.shape.Rectangle;
  36 import javafx.scene.shape.Circle;
  37 import javafx.scene.transform.Translate;
  38 
  39 import org.junit.Test;
  40 
  41 public class Parent_recomputeBounds_Test {
  42     private static final Rectangle r1 = new Rectangle(100, 100, 100, 100);
  43     private static final Rectangle r2 = new Rectangle(250, 250, 50, 50);
  44     private static final Rectangle r3 = new Rectangle(50, 50, 10, 10);
  45 
  46     @Test
  47     public void shouldRecomputeBoundsWhenNodeAdded() {
  48         final Group g = new Group();
  49         Bounds b;
  50 
  51         b = g.getBoundsInParent();
  52         assertTrue(b.isEmpty());
  53 
  54         g.getChildren().add(r1);
  55 
  56         b = g.getBoundsInParent();
  57         assertEquals(100, b.getMinX(), 0.0001);
  58         assertEquals(100, b.getMinY(), 0.0001);
  59         assertEquals(100, b.getWidth(), 0.0001);
  60         assertEquals(100, b.getHeight(), 0.0001);
  61 
  62         g.getChildren().add(r2);
  63 
  64         b = g.getBoundsInParent();
  65         assertEquals(100, b.getMinX(), 0.0001);
  66         assertEquals(100, b.getMinY(), 0.0001);
  67         assertEquals(200, b.getWidth(), 0.0001);
  68         assertEquals(200, b.getHeight(), 0.0001);
  69 
  70         g.getChildren().add(r3);
  71 
  72         b = g.getBoundsInParent();
  73         assertEquals(50, b.getMinX(), 0.0001);
  74         assertEquals(50, b.getMinY(), 0.0001);
  75         assertEquals(250, b.getWidth(), 0.0001);
  76         assertEquals(250, b.getHeight(), 0.0001);
  77     }
  78 
  79     @Test
  80     public void shouldRecomputeBoundsWhenNodeRemoved() {
  81         final Group g = new Group();
  82         g.getChildren().add(r1);
  83         g.getChildren().add(r2);
  84         g.getChildren().add(r3);
  85         Bounds b;
  86 
  87         b = g.getBoundsInParent();
  88         assertEquals(50, b.getMinX(), 0.0001);
  89         assertEquals(50, b.getMinY(), 0.0001);
  90         assertEquals(250, b.getWidth(), 0.0001);
  91         assertEquals(250, b.getHeight(), 0.0001);
  92 
  93         g.getChildren().remove(r3);
  94 
  95         b = g.getBoundsInParent();
  96         assertEquals(100, b.getMinX(), 0.0001);
  97         assertEquals(100, b.getMinY(), 0.0001);
  98         assertEquals(200, b.getWidth(), 0.0001);
  99         assertEquals(200, b.getHeight(), 0.0001);
 100 
 101         g.getChildren().remove(r2);
 102 
 103         b = g.getBoundsInParent();
 104         assertEquals(100, b.getMinX(), 0.0001);
 105         assertEquals(100, b.getMinY(), 0.0001);
 106         assertEquals(100, b.getWidth(), 0.0001);
 107         assertEquals(100, b.getHeight(), 0.0001);
 108 
 109         g.getChildren().remove(r1);
 110 
 111         b = g.getBoundsInParent();
 112         assertTrue(b.isEmpty());
 113     }
 114 
 115     @Test
 116     public void shouldRecomputeBoundsWhenNodeHiddenOrShown() {
 117         final Group g = new Group();
 118         g.getChildren().add(r1);
 119         g.getChildren().add(r2);
 120         g.getChildren().add(r3);
 121         Bounds b;
 122 
 123         b = g.getBoundsInParent();
 124         assertEquals(50, b.getMinX(), 0.0001);
 125         assertEquals(50, b.getMinY(), 0.0001);
 126         assertEquals(250, b.getWidth(), 0.0001);
 127         assertEquals(250, b.getHeight(), 0.0001);
 128 
 129         r3.setVisible(false);
 130 
 131         b = g.getBoundsInParent();
 132         assertEquals(100, b.getMinX(), 0.0001);
 133         assertEquals(100, b.getMinY(), 0.0001);
 134         assertEquals(200, b.getWidth(), 0.0001);
 135         assertEquals(200, b.getHeight(), 0.0001);
 136 
 137         r3.setVisible(true);
 138 
 139         b = g.getBoundsInParent();
 140         assertEquals(50, b.getMinX(), 0.0001);
 141         assertEquals(50, b.getMinY(), 0.0001);
 142         assertEquals(250, b.getWidth(), 0.0001);
 143         assertEquals(250, b.getHeight(), 0.0001);
 144     }
 145 
 146     @Test
 147     public void shouldNotRecomputeBoundsWhenHiddenNodeChanges() {
 148         final Group g = new Group();
 149         final Rectangle r = new Rectangle(200,200,1,1);
 150         g.getChildren().add(r2);
 151         g.getChildren().add(r);
 152         g.getChildren().add(r3);
 153         Bounds b;
 154 
 155         r.setVisible(false);
 156 
 157         b = g.getBoundsInParent();
 158         assertEquals(50, b.getMinX(), 0.0001);
 159         assertEquals(50, b.getMinY(), 0.0001);
 160         assertEquals(250, b.getWidth(), 0.0001);
 161         assertEquals(250, b.getHeight(), 0.0001);
 162 
 163         r.setX(10);
 164 
 165         b = g.getBoundsInParent();
 166         assertEquals(50, b.getMinX(), 0.0001);
 167         assertEquals(50, b.getMinY(), 0.0001);
 168         assertEquals(250, b.getWidth(), 0.0001);
 169         assertEquals(250, b.getHeight(), 0.0001);
 170 
 171         r.setWidth(500);
 172 
 173         b = g.getBoundsInParent();
 174         assertEquals(50, b.getMinX(), 0.0001);
 175         assertEquals(50, b.getMinY(), 0.0001);
 176         assertEquals(250, b.getWidth(), 0.0001);
 177         assertEquals(250, b.getHeight(), 0.0001);
 178     }
 179 
 180     @Test
 181     public void shouldNotRecomputeBoundsWhenNodeChangesInsideEdges() {
 182         final Group g = new Group();
 183         final Rectangle r = new Rectangle(200,200,1,1);
 184         g.getChildren().add(r2);
 185         g.getChildren().add(r);
 186         g.getChildren().add(r3);
 187         Bounds b;
 188 
 189         b = g.getBoundsInParent();
 190         assertEquals(50, b.getMinX(), 0.0001);
 191         assertEquals(50, b.getMinY(), 0.0001);
 192         assertEquals(250, b.getWidth(), 0.0001);
 193         assertEquals(250, b.getHeight(), 0.0001);
 194 
 195         r.setX(190);
 196         r.setY(190);
 197         r.setWidth(48);
 198         r.setHeight(48);
 199 
 200         b = g.getBoundsInParent();
 201         assertEquals(50, b.getMinX(), 0.0001);
 202         assertEquals(50, b.getMinY(), 0.0001);
 203         assertEquals(250, b.getWidth(), 0.0001);
 204         assertEquals(250, b.getHeight(), 0.0001);
 205     }
 206 
 207     @Test
 208     public void shouldNotRecomputeBoundsWhenEdgeNodeEnlargesInwards() {
 209         final Group g = new Group();
 210         final Rectangle r = new Rectangle(200,200,50,50);
 211         g.getChildren().add(r);
 212         g.getChildren().add(r3);
 213         Bounds b;
 214 
 215         b = g.getBoundsInParent();
 216         assertEquals(50, b.getMinX(), 0.0001);
 217         assertEquals(50, b.getMinY(), 0.0001);
 218         assertEquals(200, b.getWidth(), 0.0001);
 219         assertEquals(200, b.getHeight(), 0.0001);
 220 
 221         r.setX(100);
 222         r.setY(100);
 223         r.setWidth(150);
 224         r.setHeight(150);
 225 
 226         b = g.getBoundsInParent();
 227         assertEquals(50, b.getMinX(), 0.0001);
 228         assertEquals(50, b.getMinY(), 0.0001);
 229         assertEquals(200, b.getWidth(), 0.0001);
 230         assertEquals(200, b.getHeight(), 0.0001);
 231     }
 232 
 233     @Test
 234     public void shouldRecomputeBoundsWhenNodeMoved() {
 235         final Group g = new Group();
 236         final Rectangle r = new Rectangle(200,200,1,1);
 237 
 238         g.getChildren().add(r2);
 239         g.getChildren().add(r);
 240         g.getChildren().add(r3);
 241         Bounds b;
 242 
 243         b = g.getBoundsInParent();
 244         assertEquals(50, b.getMinX(), 0.0001);
 245         assertEquals(50, b.getMinY(), 0.0001);
 246         assertEquals(250, b.getWidth(), 0.0001);
 247         assertEquals(250, b.getHeight(), 0.0001);
 248 
 249         r.setX(40);
 250 
 251         b = g.getBoundsInParent();
 252         assertEquals(40, b.getMinX(), 0.0001);
 253         assertEquals(50, b.getMinY(), 0.0001);
 254         assertEquals(260, b.getWidth(), 0.0001);
 255         assertEquals(250, b.getHeight(), 0.0001);
 256 
 257         r.setX(200);
 258 
 259         b = g.getBoundsInParent();
 260         assertEquals(50, b.getMinX(), 0.0001);
 261         assertEquals(50, b.getMinY(), 0.0001);
 262         assertEquals(250, b.getWidth(), 0.0001);
 263         assertEquals(250, b.getHeight(), 0.0001);
 264     }
 265 
 266     @Test
 267     public void shouldRecomputeBoundsWhenNodeResized() {
 268         final Group g = new Group();
 269         final Rectangle r = new Rectangle(200,200,1,1);
 270 
 271         g.getChildren().add(r2);
 272         g.getChildren().add(r);
 273         g.getChildren().add(r3);
 274         Bounds b;
 275 
 276         b = g.getBoundsInParent();
 277         assertEquals(50, b.getMinX(), 0.0001);
 278         assertEquals(50, b.getMinY(), 0.0001);
 279         assertEquals(250, b.getWidth(), 0.0001);
 280         assertEquals(250, b.getHeight(), 0.0001);
 281 
 282         r.setWidth(200);
 283 
 284         b = g.getBoundsInParent();
 285         assertEquals(50, b.getMinX(), 0.0001);
 286         assertEquals(50, b.getMinY(), 0.0001);
 287         assertEquals(350, b.getWidth(), 0.0001);
 288         assertEquals(250, b.getHeight(), 0.0001);
 289 
 290         r.setWidth(5);
 291 
 292         b = g.getBoundsInParent();
 293         assertEquals(50, b.getMinX(), 0.0001);
 294         assertEquals(50, b.getMinY(), 0.0001);
 295         assertEquals(250, b.getWidth(), 0.0001);
 296         assertEquals(250, b.getHeight(), 0.0001);
 297     }
 298 
 299     @Test
 300     public void shouldNotRecomputeBoundsWhenManyNodesChange() {
 301         final Group g = new Group();
 302         final Rectangle lt = new Rectangle(100, 100, 100, 100);
 303         final Rectangle rt = new Rectangle(200, 100, 100, 100);
 304         final Rectangle lb = new Rectangle(100, 200, 100, 100);
 305         final Rectangle rb = new Rectangle(200, 200, 100, 100);
 306         final Rectangle rm1 = new Rectangle(150, 150, 50, 50);
 307         final Rectangle rm2 = new Rectangle(150, 150, 50, 50);
 308         Bounds b;
 309 
 310         g.getChildren().add(lt);
 311         g.getChildren().add(rt);
 312         g.getChildren().add(lb);
 313         g.getChildren().add(rb);
 314         g.getChildren().add(rm1);
 315         g.getChildren().add(rm2);
 316         for (int i = 0; i < 20; i++) {
 317             g.getChildren().add(new Rectangle(150 + i, 150 + i, 50 + i, 50 + i));
 318         }
 319 
 320         b = g.getBoundsInParent();
 321         assertEquals(100, b.getMinX(), 0.0001);
 322         assertEquals(100, b.getMinY(), 0.0001);
 323         assertEquals(200, b.getWidth(), 0.0001);
 324         assertEquals(200, b.getHeight(), 0.0001);
 325 
 326         lt.setX(50);
 327         rt.setX(250);
 328         lb.setY(250);
 329         rb.setX(220);
 330         rm1.setY(50);
 331         rm2.setX(151);
 332         rm2.setY(151);
 333 
 334         b = g.getBoundsInParent();
 335         assertEquals(50, b.getMinX(), 0.0001);
 336         assertEquals(50, b.getMinY(), 0.0001);
 337         assertEquals(300, b.getWidth(), 0.0001);
 338         assertEquals(300, b.getHeight(), 0.0001);
 339     }
 340 
 341     @Test
 342     public void transformedBoundsCalculationShouldNotInfluenceUntransformed() {
 343         final Group g = new Group();
 344         final Rectangle lt = new Rectangle(100, 100, 100, 100);
 345         final Rectangle rt = new Rectangle(200, 100, 100, 100);
 346         final Rectangle lb = new Rectangle(100, 200, 100, 100);
 347         final Rectangle rb = new Rectangle(200, 200, 100, 100);
 348         final Rectangle rm1 = new Rectangle(150, 150, 50, 50);
 349         final Rectangle rm2 = new Rectangle(150, 150, 50, 50);
 350         Bounds b;
 351 
 352         g.getChildren().addAll(lt, rt, lb, rb, rm1, rm2);
 353         for (int i = 0; i < 20; i++) {
 354             g.getChildren().add(new Rectangle(150 + i, 150 + i, 50 + i, 50 + i));
 355         }
 356 
 357         b = g.getBoundsInLocal();
 358         assertEquals(100, b.getMinX(), 0.0001);
 359         assertEquals(100, b.getMinY(), 0.0001);
 360         assertEquals(200, b.getWidth(), 0.0001);
 361         assertEquals(200, b.getHeight(), 0.0001);
 362 
 363         g.getTransforms().add(new Rotate(45));
 364 
 365         lt.setX(50);
 366         rt.setX(250);
 367         lb.setY(250);
 368         rb.setX(220);
 369         rm1.setY(50);
 370         rm2.setX(151);
 371         rm2.setY(151);
 372 
 373         // this call gets transformed Parent bounds and shouldn't clear the
 374         // Parent's dirty children list
 375         g.getBoundsInParent();
 376 
 377         b = g.getBoundsInLocal();
 378         assertEquals(50, b.getMinX(), 0.0001);
 379         assertEquals(50, b.getMinY(), 0.0001);
 380         assertEquals(300, b.getWidth(), 0.0001);
 381         assertEquals(300, b.getHeight(), 0.0001);
 382     }
 383 
 384     @Test
 385     public void shouldNotStartBoundsCalculationFromEmptyBounds() {
 386         final Group g = new Group();
 387         final Rectangle lt = new Rectangle(50, 50, 50, 50);
 388         final Rectangle rb = new Rectangle(100, 100, 50, 50);
 389         Bounds b;
 390 
 391         g.getChildren().addAll(lt, rb);
 392 
 393         b = g.getBoundsInLocal();
 394         assertEquals(50, b.getMinX(), 0.0001);
 395         assertEquals(50, b.getMinY(), 0.0001);
 396         assertEquals(100, b.getWidth(), 0.0001);
 397         assertEquals(100, b.getHeight(), 0.0001);
 398 
 399         // invalidate (empty) bounds
 400         rb.setVisible(false);
 401 
 402         // add new rectangle, bounds should not be recalculated by using
 403         // empty bounds
 404         g.getChildren().add(new Rectangle(150, 150, 50, 50));
 405 
 406         b = g.getBoundsInLocal();
 407         assertEquals(50, b.getMinX(), 0.0001);
 408         assertEquals(50, b.getMinY(), 0.0001);
 409         assertEquals(150, b.getWidth(), 0.0001);
 410         assertEquals(150, b.getHeight(), 0.0001);
 411     }
 412 
 413     @Test
 414     public void shouldNotIgnoreMultipleAddedNodes() {
 415         final Group g = new Group();
 416         final Rectangle lt = new Rectangle(100, 100, 100, 100);
 417         final Rectangle mt = new Rectangle(200, 100, 100, 100);
 418         final Rectangle rt = new Rectangle(300, 100, 100, 100);
 419         final Rectangle rb = new Rectangle(300, 200, 100, 100);
 420         Bounds b;
 421 
 422         g.getChildren().addAll(lt, mt);
 423 
 424         b = g.getBoundsInLocal();
 425         assertEquals(100, b.getMinX(), 0.0001);
 426         assertEquals(100, b.getMinY(), 0.0001);
 427         assertEquals(200, b.getWidth(), 0.0001);
 428         assertEquals(100, b.getHeight(), 0.0001);
 429 
 430         ((Node) rb).boundsChanged = false;
 431         // rt, rb should be either incorporated into parent bounds directly
 432         // or marked as dirty for parent bounds calculation
 433         g.getChildren().addAll(rt, rb);
 434 
 435         b = g.getBoundsInLocal();
 436         assertEquals(100, b.getMinX(), 0.0001);
 437         assertEquals(100, b.getMinY(), 0.0001);
 438         assertEquals(300, b.getWidth(), 0.0001);
 439         assertEquals(200, b.getHeight(), 0.0001);
 440     }
 441 
 442     @Test
 443     public void shouldNotCreateEmptyDirtyChildrenList() {
 444         final Group g = new Group();
 445         final Rectangle lt = new Rectangle(100, 100, 100, 100);
 446         final Rectangle rt = new Rectangle(200, 100, 100, 100);
 447         final Rectangle lb = new Rectangle(100, 200, 100, 100);
 448         final Rectangle rb = new Rectangle(200, 200, 100, 100);
 449         Bounds b;
 450 
 451         g.getChildren().addAll(lt, rt, lb, rb);
 452         final int toAdd = Parent.DIRTY_CHILDREN_THRESHOLD - 4;
 453         for (int i = 0; i < toAdd; ++i) {
 454             g.getChildren().add(
 455                     new Rectangle(150 + i * 80 / (toAdd - 1), 190, 20, 20));
 456         }
 457 
 458         b = g.getBoundsInLocal();
 459         assertEquals(100, b.getMinX(), 0.0001);
 460         assertEquals(100, b.getMinY(), 0.0001);
 461         assertEquals(200, b.getWidth(), 0.0001);
 462         assertEquals(200, b.getHeight(), 0.0001);
 463 
 464         lt.setX(50);
 465         // this should create a dirty children list on Parent, even though
 466         // the added node doesn't change the group gemetry, the created
 467         // dirty children list should still contain the previously modified
 468         // corner node (lt)
 469         g.getChildren().add(new Rectangle(150, 150, 100, 100));
 470 
 471         b = g.getBoundsInLocal();
 472         assertEquals(50, b.getMinX(), 0.0001);
 473         assertEquals(100, b.getMinY(), 0.0001);
 474         assertEquals(250, b.getWidth(), 0.0001);
 475         assertEquals(200, b.getHeight(), 0.0001);
 476     }
 477 
 478     @Test
 479     public void
 480             shouldNotSkipGeomChangedForChildAdditionInsideUntransformedBounds()
 481     {
 482         final Group g = new Group(new Circle(0, -100, 0.001),
 483                                   new Circle(0, 100, 0.001),
 484                                   new Circle(-100, 0, 0.001),
 485                                   new Circle(100, 0, 0.001));
 486         g.getTransforms().add(new Rotate(-45));
 487 
 488         Bounds b;
 489 
 490         b = g.getBoundsInParent();
 491         assertEquals(-100 * Math.sqrt(2) / 2, b.getMinX(), 0.1);
 492         assertEquals(-100 * Math.sqrt(2) / 2, b.getMinY(), 0.1);
 493         assertEquals(100 * Math.sqrt(2), b.getWidth(), 0.1);
 494         assertEquals(100 * Math.sqrt(2), b.getHeight(), 0.1);
 495 
 496         g.getChildren().add(new Circle(95, -95, 0.001));
 497 
 498         b = g.getBoundsInParent();
 499         assertEquals(-100 * Math.sqrt(2) / 2, b.getMinX(), 0.1);
 500         assertEquals(-95 * Math.sqrt(2), b.getMinY(), 0.1);
 501         assertEquals(100 * Math.sqrt(2), b.getWidth(), 0.1);
 502         assertEquals((50 + 95) * Math.sqrt(2), b.getHeight(), 0.1);
 503     }
 504 
 505     @Test
 506     public void
 507             shouldNotSkipGeomChangedForChildRemovalInsideUntransformedBounds()
 508     {
 509         final Circle toRemove = new Circle(95, -95, 0.001);
 510         final Group g = new Group(toRemove,
 511                                   new Circle(0, -100, 0.001),
 512                                   new Circle(0, 100, 0.001),
 513                                   new Circle(-100, 0, 0.001),
 514                                   new Circle(100, 0, 0.001));
 515         g.getTransforms().add(new Rotate(-45));
 516 
 517         Bounds b;
 518 
 519         b = g.getBoundsInParent();
 520         assertEquals(-100 * Math.sqrt(2) / 2, b.getMinX(), 0.1);
 521         assertEquals(-95 * Math.sqrt(2), b.getMinY(), 0.1);
 522         assertEquals(100 * Math.sqrt(2), b.getWidth(), 0.1);
 523         assertEquals((50 + 95) * Math.sqrt(2), b.getHeight(), 0.1);
 524 
 525         g.getChildren().remove(toRemove);
 526 
 527         b = g.getBoundsInParent();
 528         assertEquals(-100 * Math.sqrt(2) / 2, b.getMinX(), 0.1);
 529         assertEquals(-100 * Math.sqrt(2) / 2, b.getMinY(), 0.1);
 530         assertEquals(100 * Math.sqrt(2), b.getWidth(), 0.1);
 531         assertEquals(100 * Math.sqrt(2), b.getHeight(), 0.1);
 532     }
 533 
 534     @Test
 535     public void nodeShouldNotifyParentEvenIfItsTransformedBoundsAreDirty() {
 536         final Rectangle child = new Rectangle(0, 0, 100, 100);
 537         final Group parent = new Group(child);
 538 
 539         // ensures that child's getTransformedBounds will be called with
 540         // a non-identity transform argument during parent's bounds calculations
 541         parent.getTransforms().add(new Rotate(-45));
 542 
 543         // make the cached child's transformed bounds dirty
 544         child.getTransforms().add(new Translate(50, 0));
 545 
 546         // the cached child's transformed bounds will remain dirty because
 547         // of a non-trivial parent transformation
 548         TestHelper.assertSimilar(
 549                 boundsOfRotatedRect(50, 0, 100, 100, -45),
 550                 parent.getBoundsInParent());
 551 
 552         // during the following call the child bounds changed notification
 553         // should still be generated even though the child's cached transformed
 554         // bounds are already dirty
 555         child.getTransforms().add(new Translate(50, 0));
 556 
 557         TestHelper.assertSimilar(
 558                 boundsOfRotatedRect(100, 0, 100, 100, -45),
 559                 parent.getBoundsInParent());
 560     }
 561 
 562     private static Bounds boundsOfRotatedRect(
 563             final double x, final double y,
 564             final double width, final double height,
 565             final double angle) {
 566         final Point2D p1 = rotatePoint(x, y, angle);
 567         final Point2D p2 = rotatePoint(x + width, y, angle);
 568         final Point2D p3 = rotatePoint(x, y + height, angle);
 569         final Point2D p4 = rotatePoint(x + width, y + height, angle);
 570 
 571         final double minx = min(p1.getX(), p2.getX(), p3.getX(), p4.getX());
 572         final double miny = min(p1.getY(), p2.getY(), p3.getY(), p4.getY());
 573         final double maxx = max(p1.getX(), p2.getX(), p3.getX(), p4.getX());
 574         final double maxy = max(p1.getY(), p2.getY(), p3.getY(), p4.getY());
 575 
 576         return new BoundingBox(minx, miny, maxx - minx, maxy - miny);
 577     }
 578 
 579     private static Point2D rotatePoint(final double x, final double y,
 580                                        final double angle) {
 581         final double rada = Math.toRadians(angle);
 582         final double sina = Math.sin(rada);
 583         final double cosa = Math.cos(rada);
 584 
 585         return new Point2D(x * cosa - y * sina,
 586                            x * sina + y * cosa);
 587     }
 588 
 589     private static double min(final double... values) {
 590         double result = values[0];
 591         for (int i = 1; i < values.length; ++i) {
 592             if (result > values[i]) {
 593                 result = values[i];
 594             }
 595         }
 596 
 597         return result;
 598     }
 599 
 600     private static double max(final double... values) {
 601         double result = values[0];
 602         for (int i = 1; i < values.length; ++i) {
 603             if (result < values[i]) {
 604                 result = values[i];
 605             }
 606         }
 607 
 608         return result;
 609     }
 610 }