1 /* 2 * Copyright (c) 2011, 2015, 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 test.javafx.scene.bounds; 27 28 import com.sun.javafx.scene.NodeHelper; 29 import static test.com.sun.javafx.test.TestHelper.assertBoundsEqual; 30 import static test.com.sun.javafx.test.TestHelper.assertGroupBounds; 31 import static test.com.sun.javafx.test.TestHelper.box; 32 import static test.com.sun.javafx.test.TestHelper.formatBounds; 33 import static junit.framework.Assert.assertEquals; 34 35 import java.util.LinkedList; 36 37 import javafx.geometry.Bounds; 38 import javafx.scene.Group; 39 import javafx.scene.shape.Rectangle; 40 41 import org.junit.Test; 42 43 public class GroupBoundsTest { 44 45 /*************************************************************************** 46 * * Group Tests * * These are tests related to transforming the basic node 47 * types including * * / 48 **************************************************************************/ 49 50 // test that an empty group has empty bounds 51 public @Test 52 void testGroupBounds_Empty() { 53 assertGroupBounds(new Group()); 54 } 55 56 // test that adding a node to a group updates the Groups bounds 57 public @Test 58 void testGroupBounds_AddRect() { 59 Group g = new Group(); 60 assertGroupBounds(g); 61 62 g.getChildren().add(new Rectangle(20, 20, 50, 50)); 63 assertGroupBounds(g); 64 65 g.getChildren().add(new Rectangle(90, 20, 50, 50)); 66 assertGroupBounds(g); 67 } 68 69 // test that changing a node in a group updates the groups bounds 70 public @Test 71 void testGroupBounds_ChangeRect() { 72 Rectangle r1 = new Rectangle(20, 20, 50, 50); 73 Rectangle r2 = new Rectangle(90, 20, 50, 50); 74 Group g = new Group(r1, r2); 75 76 assertGroupBounds(g); 77 78 r1.setWidth(200); 79 assertGroupBounds(g); 80 r2.setWidth(20); 81 assertGroupBounds(g); 82 } 83 84 // test that removing a node from the group updates the Groups bounds 85 public @Test 86 void testGroupBounds_RemoveRect() { 87 Rectangle r1 = new Rectangle(20, 20, 50, 50); 88 Rectangle r2 = new Rectangle(90, 20, 50, 50); 89 Group g = new Group(r1, r2); 90 91 assertGroupBounds(g); 92 93 g.getChildren().remove(r2); 94 assertGroupBounds(g); 95 g.getChildren().remove(r1); 96 assertGroupBounds(g); 97 } 98 99 public @Test 100 void testGroupBounds_RemoveAllChildNodesAndAddOneBack() { 101 Rectangle r1 = new Rectangle(20, 20, 50, 50); 102 Rectangle r2 = new Rectangle(90, 20, 50, 50); 103 Group g = new Group(r1, r2); 104 105 assertGroupBounds(g); 106 g.getChildren().clear(); 107 assertGroupBounds(g); 108 g.getChildren().add(r1); 109 assertGroupBounds(g); 110 } 111 112 // test that adding the first node to a Group, which node is an invisible 113 // node, gets the right answer for the Group's bounds. 114 public @Test 115 void testGroupBounds_InitialNodeInvisible() { 116 Rectangle r1 = new Rectangle(20, 20, 50, 50); 117 r1.setVisible(false); 118 Group g = new Group(r1); 119 120 assertGroupBounds(g); 121 } 122 123 // test that adding invisible nodes to a group doesn't update the bounds 124 public @Test 125 void testGroupBounds_InvisibleNodes() { 126 Rectangle r1 = new Rectangle(20, 20, 50, 50); 127 Rectangle r2 = new Rectangle(90, 20, 50, 50); 128 Group g = new Group(r1, r2); 129 assertGroupBounds(g); 130 131 Rectangle invisible = new Rectangle(-100, -100, 1, 1); 132 invisible.setVisible(true); 133 g.getChildren().add(invisible); 134 assertGroupBounds(g); 135 } 136 137 // test that adding zero sized nodes does update a Groups bounds 138 public @Test 139 void testGroupBounds_ZeroSizedNodes() { 140 Rectangle r1 = new Rectangle(20, 20, 50, 50); 141 Rectangle r2 = new Rectangle(90, 20, 50, 50); 142 Group g = new Group(r1, r2); 143 assertGroupBounds(g); 144 145 Rectangle small = new Rectangle(-100, -100, 0, 0); 146 g.getChildren().add(small); 147 assertGroupBounds(g); 148 } 149 150 // test that making a node invisible updates the groups bounds 151 public @Test 152 void testGroupBounds_NodeMadeInvisible() { 153 Rectangle r1 = new Rectangle(20, 20, 50, 50); 154 Rectangle r2 = new Rectangle(90, 20, 50, 50); 155 Group g = new Group(r1, r2); 156 assertGroupBounds(g); 157 158 r1.setVisible(false); 159 assertGroupBounds(g); 160 } 161 162 public @Test 163 void testGroupBounds_NodeWithNoWidthMadeInvisible() { 164 Rectangle r1 = new Rectangle(20, 20, 50, 50); 165 Rectangle r2 = new Rectangle(90, 20, 0, 50); 166 Group g = new Group(r1, r2); 167 assertGroupBounds(g); 168 169 r2.setVisible(false); 170 assertGroupBounds(g); 171 } 172 173 public @Test 174 void testGroupBounds_FirstNodeHasNoWidthSecondNodeDoes() { 175 Rectangle r1 = new Rectangle(20, 20, 0, 50); 176 Rectangle r2 = new Rectangle(90, 20, 50, 50); 177 Group g = new Group(r1, r2); 178 assertGroupBounds(g); 179 } 180 181 public @Test 182 void testGroupBounds_FirstNodeHasNoWidthSecondNodeDoes_Added() { 183 Rectangle r1 = new Rectangle(20, 20, 0, 50); 184 Rectangle r2 = new Rectangle(90, 20, 50, 50); 185 Group g = new Group(); 186 assertGroupBounds(g); 187 g.getChildren().add(r1); 188 assertGroupBounds(g); 189 g.getChildren().add(r2); 190 assertGroupBounds(g); 191 } 192 193 public @Test 194 void testGroupBounds_FirstNodeHasNoWidthSecondNodeDoes_ClearedThenAdded() { 195 Rectangle r1 = new Rectangle(20, 20, 0, 50); 196 Rectangle r2 = new Rectangle(90, 20, 50, 50); 197 Rectangle r3 = new Rectangle(10, 15, 75, 20); 198 Group g = new Group(r1, r2); 199 assertGroupBounds(g); 200 g.getChildren().remove(r2); 201 assertGroupBounds(g); 202 g.getChildren().add(r3); 203 assertGroupBounds(g); 204 } 205 206 // this test uncovered a bug when using the GroupBoundsHelper. It needs 207 // two invisible nodes to tickle it You also have to ask assert the bounds 208 // after each change or it may not fail 209 public @Test 210 void testGroupBounds_TwoNodesMadeInvisible_ThenNodeAdded() { 211 Rectangle r1 = new Rectangle(20, 20, 50, 50); 212 Rectangle r2 = new Rectangle(90, 20, 50, 50); 213 Rectangle r3 = new Rectangle(10, 15, 75, 20); 214 Group g = new Group(); 215 216 assertGroupBounds(g); 217 g.getChildren().add(r1); 218 assertGroupBounds(g); 219 r1.setVisible(false); 220 assertGroupBounds(g); 221 g.getChildren().add(r2); 222 assertGroupBounds(g); 223 r2.setVisible(false); 224 assertGroupBounds(g); 225 g.getChildren().add(r3); 226 assertGroupBounds(g); 227 } 228 229 public @Test 230 void testGroupBounds_TogglingVisiblityToFalseAndTrueWhileExpandingBounds() { 231 Rectangle r1 = new Rectangle(20, 20, 50, 50); 232 Rectangle r2 = new Rectangle(90, 20, 50, 50); 233 Rectangle r3 = new Rectangle(10, 15, 75, 20); 234 Group g = new Group(); 235 assertGroupBounds(g); 236 g.getChildren().add(r1); 237 assertGroupBounds(g); 238 g.getChildren().add(r2); 239 assertGroupBounds(g); 240 r2.setVisible(false); 241 assertGroupBounds(g); 242 g.getChildren().add(r3); 243 assertGroupBounds(g); 244 r2.setVisible(true); 245 assertGroupBounds(g); 246 } 247 248 private Group createGroupWithRects() { 249 Group group = new Group(); 250 group.setId("group"); 251 for (int i = 1; i <= 3; i++) { 252 for (int j = 1; j <= 3; j++) { 253 Rectangle r = new Rectangle(j * 100, i * 100, 50, 50); 254 r.setId("rect[" + i + "," + j + "]"); 255 group.getChildren().add(r); 256 } 257 } 258 259 // sanity test check that the size of the group is correct 260 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 261 262 return group; 263 } 264 265 // Started out as a simple test and now pretty well covers all the use 266 // cases. Oops. Bad Richard. 267 public @Test 268 void testMovingNodesTowardCenterOfGroupAndOther() { 269 Group group = createGroupWithRects(); 270 271 // move the 1, 3, 5, 7 nodes in towards the center. Doing so should not 272 // change the group's bounds 273 ((Rectangle) group.getChildren().get(1)).setX(200); 274 ((Rectangle) group.getChildren().get(1)).setY(200); 275 ((Rectangle) group.getChildren().get(3)).setX(200); 276 ((Rectangle) group.getChildren().get(3)).setY(200); 277 ((Rectangle) group.getChildren().get(5)).setX(200); 278 ((Rectangle) group.getChildren().get(5)).setY(200); 279 ((Rectangle) group.getChildren().get(7)).setX(200); 280 ((Rectangle) group.getChildren().get(7)).setY(200); 281 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 282 283 // move the 2 and 6 nodes in towards the center. Doing so should not 284 // change the group's bounds 285 ((Rectangle) group.getChildren().get(2)).setX(200); 286 ((Rectangle) group.getChildren().get(2)).setY(200); 287 ((Rectangle) group.getChildren().get(6)).setX(200); 288 ((Rectangle) group.getChildren().get(6)).setY(200); 289 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 290 291 // move the 0 node into the center. Doing so SHOULD change the bounds 292 // such that all nodes are in the center (200, 200) except for the 293 // number 8 node which is at (300, 300) 294 ((Rectangle) group.getChildren().get(0)).setX(200); 295 ((Rectangle) group.getChildren().get(0)).setY(200); 296 assertEquals(box(200, 200, 150, 150), group.getBoundsInLocal()); 297 298 // move the 8 node a little towards the center (but not all the way) 299 // just to test that moving the bottom-right will adjust things 300 ((Rectangle) group.getChildren().get(8)).setX(250); 301 ((Rectangle) group.getChildren().get(8)).setY(250); 302 assertEquals(box(200, 200, 100, 100), group.getBoundsInLocal()); 303 304 // move the rest of the nodes down past #8 305 for (int i = 0; i <= 7; i++) { 306 ((Rectangle) group.getChildren().get(i)).setX(300); 307 ((Rectangle) group.getChildren().get(i)).setY(300); 308 } 309 assertEquals(box(250, 250, 100, 100), group.getBoundsInLocal()); 310 311 // now move #8 onto the rest 312 ((Rectangle) group.getChildren().get(8)).setX(300); 313 ((Rectangle) group.getChildren().get(8)).setY(300); 314 assertEquals(box(300, 300, 50, 50), group.getBoundsInLocal()); 315 } 316 317 public @Test 318 void testMovingNodesAwayFromCenterOfGroup() { 319 Group group = createGroupWithRects(); 320 321 ((Rectangle) group.getChildren().get(4)).setX(600); 322 ((Rectangle) group.getChildren().get(4)).setY(600); 323 assertEquals(box(100, 100, 550, 550), group.getBoundsInLocal()); 324 } 325 326 public @Test 327 void testMovingNodesWithinInteriorOfGroup() { 328 Group group = createGroupWithRects(); 329 330 ((Rectangle) group.getChildren().get(4)).setX(250); 331 ((Rectangle) group.getChildren().get(4)).setY(250); 332 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 333 } 334 335 public @Test 336 void testMovingExteriorNodesOnExterior() { 337 Group group = createGroupWithRects(); 338 339 ((Rectangle) group.getChildren().get(0)).setY(150); 340 ((Rectangle) group.getChildren().get(1)).setX(100); 341 ((Rectangle) group.getChildren().get(2)).setX(150); 342 ((Rectangle) group.getChildren().get(3)).setY(120); 343 ((Rectangle) group.getChildren().get(5)).setY(100); 344 ((Rectangle) group.getChildren().get(6)).setX(250); 345 ((Rectangle) group.getChildren().get(7)).setX(120); 346 ((Rectangle) group.getChildren().get(8)).setY(200); 347 348 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 349 } 350 351 public @Test 352 void testRemovingInteriorNodes() { 353 Group group = createGroupWithRects(); 354 355 group.getChildren().remove(4); 356 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 357 } 358 359 public @Test 360 void testRemovingExteriorNodes() { 361 Group group = createGroupWithRects(); 362 363 group.getChildren().remove(7); 364 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 365 group.getChildren().remove(6); 366 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 367 group.getChildren().remove(5); 368 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 369 group.getChildren().remove(3); 370 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 371 group.getChildren().remove(2); 372 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 373 group.getChildren().remove(1); 374 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 375 376 // and now the bounds should be changing 377 group.getChildren().remove(0); 378 assertEquals(box(200, 200, 150, 150), group.getBoundsInLocal()); 379 group.getChildren().remove(1); 380 assertEquals(box(200, 200, 50, 50), group.getBoundsInLocal()); 381 } 382 383 // run a set of tests that add 1000's of random sized and random positioned 384 // rectangles to the Group, and check the groups bounds at each stage. 385 // Also randomly make some invisible, some visible, remove some, toggle 386 // visibility, and so forth. 387 public @Test 388 void testGroupBounds_Stress() { 389 boolean fullTest = Boolean.getBoolean("test.everything"); 390 if (fullTest) { 391 Group g = new Group(); 392 assertGroupBounds(g); 393 for (int j = 0; j < 500; j++) { 394 LinkedList<String> whatHappenedStack = new java.util.LinkedList<String>(); 395 for (int i = 0; i <= 50; i++) { 396 Bounds layoutBounds = g.getLayoutBounds(); 397 int numChildren = g.getChildren().size(); 398 String whatHappened = ""; 399 double rnd = Math.random(); 400 int index = (int) (Math.random() * numChildren); 401 if (rnd > .3 && rnd < .5 && numChildren > 0) { 402 // translate some rect 403 int direction = (Math.random() > .5) ? -1 : 1; 404 Bounds oldBoundsInParent = g.getChildren().get(index) 405 .getBoundsInParent(); 406 g.getChildren() 407 .get(index) 408 .setTranslateX( 409 (int) (Math.random() * 500 * direction)); 410 g.getChildren() 411 .get(index) 412 .setTranslateY( 413 (int) (Math.random() * 500 * direction)); 414 whatHappened = "On iteration " + j + "-"+ i + "I translated a " 415 + g.getChildren().get(index) + " with boundsInParent " 416 + formatBounds(oldBoundsInParent) + " which was the " 417 + index+1 + "th item. Its new bounds are " 418 + formatBounds(g.getChildren().get(index).getBoundsInParent()) + ". " 419 + "The layoutBounds of the Group was " 420 + formatBounds(layoutBounds) + " and is now " + formatBounds(g.getLayoutBounds()) + " and had " 421 + numChildren + " number of child nodes"; 422 whatHappenedStack.add(whatHappened); 423 } else if (rnd > .5 && rnd < .6 && numChildren > 0) { 424 // deletesome rect 425 whatHappened = "On iteration " + j + "-" + i+ " I deleted a " 426 + "" + g.getChildren().get(index) + " with boundsInParent " 427 + "" + formatBounds(g.getChildren().get(index).getBoundsInParent())+ " which was the " 428 + "" + index+1+ "th item. The layoutBounds of the Group was " 429 + "" + formatBounds(layoutBounds)+ " and is now " + formatBounds(g.getLayoutBounds())+ " and had " 430 + "" + numChildren+ " number of child nodes"; 431 g.getChildren().remove(index); 432 whatHappenedStack.add(whatHappened); 433 } else if (rnd > .6 && rnd < .65 && numChildren > 0) { 434 // toggle visibility on some rect 435 g.getChildren().get(index) 436 .setVisible(!g.getChildren().get(index).isVisible()); 437 whatHappened = "On iteration " + j+ "-" + i+ " I toggled the " 438 + "visibility of " + g.getChildren().get(index) + " with boundsInParent " 439 + "" + formatBounds(g.getChildren().get(index).getBoundsInParent())+ " to " 440 + "" + g.getChildren().get(index).isVisible() + ". The layoutBounds of the " 441 + "Group was " + formatBounds(layoutBounds)+ " and is now " + formatBounds(g.getLayoutBounds())+ " and " 442 + "had " + numChildren+ " number of child nodes"; 443 whatHappenedStack.add(whatHappened); 444 } else { 445 Rectangle rect = new Rectangle( 446 ((int) (Math.random() * 1000)), 447 ((int) (Math.random() * 1000)), 448 ((int) (Math.random() * 100)), 449 ((int) (Math.random() * 100))); 450 g.getChildren().add(rect); 451 whatHappened = "On iteration " + j+ "-" + i+ " I added a " + rect+ " with " 452 + "boundsInParent " + formatBounds(rect.getBoundsInParent())+ " to the group. The " 453 + "layoutBounds of the Group was " + formatBounds(layoutBounds)+ " and is now " 454 + "" + formatBounds(g.getLayoutBounds())+ " and had " + numChildren+ " number of " 455 + "child nodes"; 456 whatHappenedStack.add(whatHappened); 457 } 458 try { 459 assertGroupBounds(g); 460 } catch (Exception any) { 461 System.out.println("Something went wrong. Here's what happened:"); 462 for (String item : whatHappenedStack) { 463 System.out.println("{item}"); 464 } 465 throw new RuntimeException(any); 466 } 467 } 468 } 469 } 470 } 471 472 // here is a special test for getPivotX and getPivotY 473 @Test 474 public void testPivotXAndPivotY() { 475 Rectangle rect = new Rectangle(100, 100); 476 assertEquals(50.0f, (float) NodeHelper.getPivotX(rect)); 477 assertEquals(50.0f, (float) NodeHelper.getPivotY(rect)); 478 rect.setWidth(70.0f); 479 assertEquals(35.0f, (float) NodeHelper.getPivotX(rect)); 480 assertEquals(50.0f, (float) NodeHelper.getPivotY(rect)); 481 } 482 483 /*************************************************************************** 484 * Group Bounds Sanity Tests * * These tests are for Group 485 * to make sure that their * bounds are reported correctly 486 * before effects or clips or transforms * are taken into account. * * The 487 * group tests here are just basic tests, a full set of Group bounds * tests 488 * are included in their own section later in this file. There are * many 489 * edge cases to group bounds. The primary purpose of these next * few tests 490 * are just to make sure that group's bounds are correct in the * simple 491 * case. * * 492 **************************************************************************/ 493 494 public @Test 495 void testBoundsForGroup() { 496 Rectangle r1 = new Rectangle(20, 20, 50, 50); 497 Rectangle r2 = new Rectangle(90, 20, 50, 50); 498 Group group = new Group(r1, r2); 499 500 assertEquals(box(20, 20, 120, 50), group.getBoundsInLocal()); 501 assertEquals(group.getBoundsInLocal(), group.getBoundsInParent()); 502 assertEquals(group.getBoundsInLocal(), group.getLayoutBounds()); 503 } 504 505 /** 506 * Just a basic test to make sure basic group node bounds calculation works 507 */ 508 public @Test 509 void testBoundsForGroup_childGeomChanging() { 510 Rectangle rect = new Rectangle(); 511 rect.setId("rect"); 512 Rectangle square = new Rectangle(); 513 square.setId("square"); 514 Group group = new Group(rect, square); 515 group.setId("group"); 516 517 assertBoundsEqual(box(0, 0, 0, 0), rect.getBoundsInLocal()); 518 assertBoundsEqual(box(0, 0, 0, 0), square.getBoundsInLocal()); 519 assertBoundsEqual(box(0, 0, 0, 0), group.getBoundsInLocal()); 520 521 rect.setX(50); 522 rect.setY(50); 523 rect.setWidth(100); 524 rect.setHeight(30); 525 526 assertBoundsEqual(box(50, 50, 100, 30), rect.getBoundsInLocal()); 527 assertBoundsEqual(box(0, 0, 0, 0), square.getBoundsInLocal()); 528 assertBoundsEqual(box(0, 0, 150, 80), group.getBoundsInLocal()); 529 530 square.setX(25); 531 square.setY(25); 532 square.setWidth(50); 533 square.setHeight(50); 534 535 assertBoundsEqual(box(50, 50, 100, 30), rect.getBoundsInLocal()); 536 assertBoundsEqual(box(25, 25, 50, 50), square.getBoundsInLocal()); 537 assertBoundsEqual(box(25, 25, 125, 55), group.getBoundsInLocal()); 538 } 539 540 public @Test 541 void testBoundsInParentOfGroup() { 542 Rectangle rect = new Rectangle(50, 50, 100, 30); 543 rect.setId("rect"); 544 Group group = new Group(rect); 545 group.setId("group"); 546 group.setTranslateX(100); 547 group.setTranslateY(100); 548 549 assertBoundsEqual(box(150, 150, 100, 30), group.getBoundsInParent()); 550 } 551 552 /** 553 * Tests that if I have a group with nobody listening to the bounds, and the 554 * bounds change, and then I ask for the new bounds, that the bounds are 555 * valid. 556 */ 557 public @Test 558 void testRequestingBoundsOnGroupWithNoListenersWorks() { 559 Group group = new Group(new Rectangle(50, 50, 100, 30)); 560 561 assertBoundsEqual(box(50, 50, 100, 30), group.getBoundsInLocal()); 562 } 563 564 }