private static Bounds computeExtensionBounds(
      Bounds backgroundBounds, Bounds unclippedContentBounds) {
    final Bounds totalBounds = unionOfBounds(backgroundBounds, unclippedContentBounds);
    final double backgroundCenterX, backgroundCenterY;
    backgroundCenterX = (backgroundBounds.getMinX() + backgroundBounds.getMaxX()) / 2.0;
    backgroundCenterY = (backgroundBounds.getMinY() + backgroundBounds.getMaxY()) / 2.0;
    assert totalBounds.contains(backgroundCenterX, backgroundCenterY);
    double extensionHalfWidth, extensionHalfHeight;
    extensionHalfWidth =
        Math.max(
            backgroundCenterX - totalBounds.getMinX(), totalBounds.getMaxX() - backgroundCenterX);
    extensionHalfHeight =
        Math.max(
            backgroundCenterY - totalBounds.getMinY(), totalBounds.getMaxY() - backgroundCenterY);

    // We a few pixels in order the parent ring of root object
    // to fit inside the extension rect.
    extensionHalfWidth += 20.0;
    extensionHalfHeight += 20.0;

    return new BoundingBox(
        backgroundCenterX - extensionHalfWidth,
        backgroundCenterY - extensionHalfHeight,
        extensionHalfWidth * 2,
        extensionHalfHeight * 2);
  }
Ejemplo n.º 2
0
 private static Bounds translateBounds(Bounds bounds, Point2D offset) {
   return new BoundingBox(
       bounds.getMinX() + offset.getX(),
       bounds.getMinY() + offset.getY(),
       bounds.getWidth(),
       bounds.getHeight());
 }
Ejemplo n.º 3
0
  public void setLayoutBounds(Bounds layoutBounds) {

    // Setup layoutX/layoutY on the image view and the region (1)
    region.setLayoutX(layoutBounds.getMinX());
    region.setLayoutY(layoutBounds.getMinY());
    region.setPrefWidth(layoutBounds.getWidth());
    region.setPrefHeight(layoutBounds.getHeight());
  }
 @Override
 protected void anchorBoundsDidChange() {
   final Bounds lb = getAnchor().getLayoutBounds();
   messagePanelController.setPanelWidth(lb.getWidth());
   // This callback should not be needed for auto hiding popups
   // See RT-31292 : Popup does not auto hide when resizing the window
   updatePopupLocation();
 }
 @Override
 public Point2D _getMidpoint() {
   StackPane tabRegion = getTabRegion();
   Bounds boundsInParent = tabRegion.getBoundsInParent();
   double x = boundsInParent.getWidth() / 2;
   double y = boundsInParent.getHeight() / 2;
   return tabRegion.localToParent(x, y);
 }
Ejemplo n.º 6
0
 private void handleExplode() {
   if (ballBounds.getMinY() > areaBounds.getMaxY()) {
     isExploding = false;
     resetBall();
   } else {
     ball.setTranslateX(ball.getTranslateX() + dx);
     ball.setTranslateY(ball.getTranslateY() - dy);
   }
 }
Ejemplo n.º 7
0
 /* This code has been taken from:
  * https://community.oracle.com/thread/2534556?tstart=0
  * AND WAS MODIFIED!*/
 public static final Point2D getNodeLocation(Node node) {
   double x = 0, y = 0;
   for (Node n = node; n != null; n = n.getParent()) {
     Bounds parentBounds = n.getBoundsInParent();
     x += parentBounds.getMinX();
     y += parentBounds.getMinY();
   }
   return new Point2D(x, y);
 }
 // as of JDK 1.6: @Override
 public int getScreenLocationY() {
   // this code is never called?
   Bounds lBoundsInScenenode = node.localToScene(node.getBoundsInLocal());
   int v =
       (int)
           Math.ceil(
               node.getScene().getY() + node.getScene().getY() + lBoundsInScenenode.getMinY());
   // for debugging System.out.println(getComponent() + " getScreenLocationX =" + v);
   return v;
 }
Ejemplo n.º 9
0
 protected void refreshHandleLocation(Node hostVisual) {
   // position vbox top-right next to the host
   Bounds hostBounds = hostVisual.getBoundsInParent();
   Parent parent = hostVisual.getParent();
   if (parent != null) {
     hostBounds = parent.localToScene(hostBounds);
   }
   Point2D location =
       getVisual().getParent().sceneToLocal(hostBounds.getMaxX(), hostBounds.getMinY());
   getVisual().setLayoutX(location.getX());
   getVisual().setLayoutY(location.getY());
 }
Ejemplo n.º 10
0
 private static Bounds intersectBounds(Bounds a, Bounds b) {
   double minX = Math.max(a.getMinX(), b.getMinX());
   double minY = Math.max(a.getMinY(), b.getMinY());
   double maxX = Math.min(a.getMaxX(), b.getMaxX());
   double maxY = Math.min(a.getMaxY(), b.getMaxY());
   double width = maxX - minX;
   double height = maxY - minY;
   return new BoundingBox(minX, minY, width, height);
 }
Ejemplo n.º 11
0
  private void animateTransitionToLabel(Label newLabel) {
    final Bounds newLabelBounds = newLabel.getBoundsInParent();

    final double newX = newLabelBounds.getMinX();
    final double newWidth = newLabelBounds.getWidth();

    animation.getKeyFrames().clear();
    animation
        .getKeyFrames()
        .add(
            new KeyFrame(
                TRANSITION_DURATION,
                new KeyValue(selectedBackground.xProperty(), newX),
                new KeyValue(selectedBackground.widthProperty(), newWidth)));

    animation.play();
  }
  /** Update the popup location below the anchor node. */
  @Override
  protected void updatePopupLocation() {
    final Bounds anchorBounds = getAnchor().getLayoutBounds();
    Point2D popupLocation;

    assert anchorBounds != null;

    // At exit time, closeRequestHandler() is not always called.
    // So this method can be invoked after the anchor has been removed the
    // scene. This looks like a bug in FX...
    // Anway we protect ourself by checking.
    if (getAnchor().getScene() != null) {
      popupLocation = getAnchor().localToScreen(anchorBounds.getMinX(), anchorBounds.getMaxY());
      getPopup().setX(popupLocation.getX());
      getPopup().setY(popupLocation.getY());
    }
  }
Ejemplo n.º 13
0
  private void init(LedColor initialColor) {
    PhongMaterial material = new PhongMaterial();
    material.setDiffuseColor(initialColor.getColor());

    int ledSpacing = 60;
    int ledSize = 10;

    for (int x = 0; x < cubeSize; x++) {
      for (int y = 0; y < cubeSize; y++) {
        for (int z = 0; z < cubeSize; z++) {
          final Sphere led = new Sphere(ledSize);
          led.setMaterial(material);
          led.setTranslateX(x * (ledSize + ledSpacing) + ledSize);
          led.setTranslateY(y * (ledSize + ledSpacing) + ledSize);
          led.setTranslateZ(z * (ledSize + ledSpacing) + ledSize);
          led.setOnMouseClicked(
              event -> {
                fireOnClick(event);
              });
          led.setUserData(new Coordinate3D(x, z, cubeSize - y - 1));
          getChildren().add(led);
          leds.add(led);
        }
      }
    }

    // add some axis
    Bounds b = getBoundsInLocal();

    Cylinder xAxis = new Cylinder(2, b.getMaxX());
    xAxis.setMaterial(new PhongMaterial(Color.RED));
    xAxis.getTransforms().add(new Rotate(90, Rotate.Z_AXIS));
    xAxis.setTranslateX(b.getMaxX() / 2);
    xAxis.setTranslateY(b.getMaxY() + 20);
    getChildren().add(xAxis);

    Cylinder yAxis = new Cylinder(2, b.getMaxZ());
    yAxis.setMaterial(new PhongMaterial(Color.BLUE));
    yAxis.getTransforms().add(new Rotate(90, Rotate.X_AXIS));
    yAxis.setTranslateX(-20);
    yAxis.setTranslateY(b.getMaxY() + 20);
    yAxis.setTranslateZ(b.getMaxZ() / 2);
    getChildren().add(yAxis);

    Cylinder zAxis = new Cylinder(2, b.getMaxX());
    zAxis.setMaterial(new PhongMaterial(Color.GREEN));
    zAxis.setTranslateX(-20);
    zAxis.setTranslateY(b.getMaxY() / 2);
    getChildren().add(zAxis);
  }
Ejemplo n.º 14
0
  @Override
  public Object eval(Environment env) {

    Sprite sprite = env.getSprite();
    Bounds spriteBounds = sprite.getBoundsInLocal();
    Pane parent = (AnchorPane) sprite.getParent();

    // x軸への動きしか想定しない
    double rCollision =
        parent.getWidth() - sprite.getTranslateX() - env.getX() - spriteBounds.getWidth();
    double lCollision = sprite.getTranslateX() + env.getX();

    if (rCollision < 0 || lCollision < 0) {
      return TRUE;
    }

    return FALSE;
  }
 @Override
 public void doActivate() {
   super.doActivate();
   final Procedure1<Text> _function =
       (Text it) -> {
         String _name = this.getName();
         it.setText(_name);
         TooltipExtensions.setTooltip(it, "Zoom to reveal content");
       };
   ObjectExtensions.<Text>operator_doubleArrow(this.label, _function);
   boolean _equals = Objects.equal(this.innerDiagram, null);
   if (_equals) {
     String _name = this.getName();
     String _plus = ("No inner diagram set on node " + _name);
     String _plus_1 = (_plus + ". LOD behavior deactivated");
     LevelOfDetailDiagramNode.LOG.severe(_plus_1);
   } else {
     XDiagram _diagram = CoreExtensions.getDiagram(this);
     ViewportTransform _viewportTransform = _diagram.getViewportTransform();
     ReadOnlyDoubleProperty _scaleProperty = _viewportTransform.scaleProperty();
     final ChangeListener<Number> _function_1 =
         (ObservableValue<? extends Number> prop, Number oldVal, Number newVal) -> {
           Bounds _boundsInLocal = this.getBoundsInLocal();
           final Bounds bounds = this.localToScene(_boundsInLocal);
           double _width = bounds.getWidth();
           double _height = bounds.getHeight();
           final double area = (_width * _height);
           if ((area <= 100000)) {
             this.label.setVisible(true);
             this.innerDiagramGroup.setVisible(false);
             this.pane.setBackgroundPaint(RectangleBorderPane.DEFAULT_BACKGROUND);
           } else {
             this.label.setVisible(false);
             this.innerDiagramGroup.setVisible(true);
             this.innerDiagram.activate();
             final Procedure1<DiagramScaler> _function_2 =
                 (DiagramScaler it) -> {
                   Bounds _layoutBounds = this.label.getLayoutBounds();
                   double _width_1 = _layoutBounds.getWidth();
                   double _plus_2 = (_width_1 + 40);
                   it.setWidth(_plus_2);
                   Bounds _layoutBounds_1 = this.label.getLayoutBounds();
                   double _height_1 = _layoutBounds_1.getHeight();
                   double _plus_3 = (_height_1 + 20);
                   it.setHeight(_plus_3);
                   it.activate();
                 };
             ObjectExtensions.<DiagramScaler>operator_doubleArrow(this.diagramScaler, _function_2);
             Paint _backgroundPaint = this.innerDiagram.getBackgroundPaint();
             this.pane.setBackgroundPaint(_backgroundPaint);
           }
         };
     _scaleProperty.addListener(_function_1);
   }
 }
Ejemplo n.º 16
0
 /** Transforms the given bounds in the given scene to the screen's coordinate system. */
 public static Bounds boundsOnScreen(Bounds boundsInScene, Scene scene) {
   Bounds sceneBoundsInWindow = bounds(scene);
   Bounds windowBoundsOnScreen = bounds(scene.getWindow());
   return translateBounds(
       boundsInScene,
       byOffset(
           sceneBoundsInWindow.getMinX() + windowBoundsOnScreen.getMinX(),
           sceneBoundsInWindow.getMinY() + windowBoundsOnScreen.getMinY()));
 }
Ejemplo n.º 17
0
 @Override
 public void handle(MouseEvent event) {
   EventType<? extends Event> type = event.getEventType();
   if (type.equals(MouseEvent.MOUSE_ENTERED) || type.equals(MouseEvent.MOUSE_EXITED)) {
     refreshVisual();
   } else if (type.equals(MouseEvent.MOUSE_MOVED) || type.equals(MouseEvent.MOUSE_DRAGGED)) {
     if (originalBounds != null) {
       if (!originalBounds.contains(event.getSceneX(), event.getSceneY())) {
         // unhover the visual by making it mouse transparent
         getVisual().setMouseTransparent(true);
         // this will result in a MOUSE_EXITED event being fired,
         // which will lead to a refreshVisual() call, which will
         // update the visualization
       }
     }
   }
 }
Ejemplo n.º 18
0
 private void handleEvents(final String EVENT_TYPE) {
   if ("RESIZE".equals(EVENT_TYPE)) {
     resize();
     redraw();
   } else if ("REDRAW".equals(EVENT_TYPE)) {
     redraw();
   } else if ("RECALC".equals(EVENT_TYPE)) {
     angleRange = Helper.clamp(90.0, 180.0, getSkinnable().getAngleRange());
     startAngle = getStartAngle();
     minValue = getSkinnable().getMinValue();
     range = getSkinnable().getRange();
     sections = getSkinnable().getSections();
     angleStep = angleRange / range;
     redraw();
   } else if ("FINISHED".equals(EVENT_TYPE)) {
     needleTooltip.setText(String.format(locale, formatString, getSkinnable().getValue()));
     double value = getSkinnable().getValue();
     if (getSkinnable().isValueVisible()) {
       Bounds bounds = pane.localToScreen(pane.getBoundsInLocal());
       double xFactor = value > getSkinnable().getRange() * 0.8 ? 0.0 : 0.25;
       double tooltipAngle = value * angleStep;
       double sinValue = Math.sin(Math.toRadians(180 + angleRange * 0.5 - tooltipAngle));
       double cosValue = Math.cos(Math.toRadians(180 + angleRange * 0.5 - tooltipAngle));
       double needleTipX =
           bounds.getMinX() + bounds.getWidth() * 0.5 + bounds.getHeight() * sinValue;
       double needleTipY =
           bounds.getMinY() + bounds.getHeight() * 0.72 + bounds.getHeight() * cosValue;
       needleTooltip.show(needle, needleTipX, needleTipY);
       needleTooltip.setAnchorX(needleTooltip.getX() - needleTooltip.getWidth() * xFactor);
     }
     if (sections.isEmpty() || sectionsAlwaysVisible) return;
     for (Section section : sections) {
       if (section.contains(value)) {
         barTooltip.setText(section.getText());
         break;
       }
     }
   } else if ("VISIBILITY".equals(EVENT_TYPE)) {
     Helper.enableNode(titleText, !getSkinnable().getTitle().isEmpty());
   }
 }
Ejemplo n.º 19
0
  private void handlePlatform() {

    switch (currentDirection) {
      case NONE:
        break;

      case LEFT:
        {
          double newPos = rect.getTranslateX() - SPEED;
          if (newPos < areaBounds.getMinX()) {
            newPos = areaBounds.getMinX();
          }
          rect.setTranslateX(newPos);
        }
        break;

      case RIGHT:
        {
          double newPos = rect.getTranslateX() + SPEED;
          if (newPos > areaBounds.getMaxX() - rectBounds.getWidth()) {
            newPos = areaBounds.getMaxX() - rectBounds.getWidth();
          }
          rect.setTranslateX(newPos);
        }
        break;

      case UP:
        sceneSpeed += 0.1;
        if (sceneSpeed > 10) {
          sceneSpeed = 10;
        }
        break;

      case DOWN:
        sceneSpeed -= 0.1;
        if (sceneSpeed < 0) {
          sceneSpeed = 0;
        }
        break;

      default:
        break;
    }
  }
Ejemplo n.º 20
0
 protected void animateComputeBox(double frac, Context context) {
   bounds = label.getBoundsInParent();
   bounds =
       new BoundingBox(
           bounds.getMinX() - HMARGIN,
           bounds.getMinY() - VMARGIN,
           bounds.getWidth() + 2 * HMARGIN,
           bounds.getHeight() + 2 * VMARGIN);
   rectangle.setFill(Color.TRANSPARENT);
   context.styles.get(StyleId.LineColor).apply(rectangle);
   context.styles.get(StyleId.PenWidth).apply(rectangle);
   context.styles.get(StyleId.Dash).apply(rectangle);
   context.styles.get(StyleId.Opacity).apply(rectangle);
   rectangle.setX(bounds.getMinX());
   rectangle.setY(bounds.getMinY());
   rectangle.setWidth(0d);
   rectangle.setHeight(0d);
   rectangle.setVisible(false);
 }
  private SequentialTransition CalculateTransition(
      Card c, HBox PlayerCardBox, ImageView imView, int iCardDrawn) {
    // This is the card that is going to be dealt to the player.
    String strCard = "/res/img/" + c.getCardImg();
    ImageView imgvCardDealt =
        new ImageView(new Image(getClass().getResourceAsStream(strCard), 96, 71, true, true));

    // imgvCardFaceDown - There's already a place holder card
    // sitting in
    // the player's hbox. It's face down. Find it
    // and then determine it's bounds and top left hand handle.
    ImageView imgvCardFaceDown = (ImageView) PlayerCardBox.getChildren().get(iCardDrawn - 1);
    Bounds bndCardDealt = imgvCardFaceDown.localToScene(imgvCardFaceDown.getBoundsInLocal());
    Point2D pntCardDealt = new Point2D(bndCardDealt.getMinX(), bndCardDealt.getMinY());

    // imgvDealerDeck = the card in the common area, where dealer's
    // card
    // is located. Find the boundary top left point.
    ImageView imgvDealerDeck = (ImageView) HboxCommonArea.getChildren().get(0);
    Bounds bndCardDeck = imgvDealerDeck.localToScene(imgvDealerDeck.getBoundsInLocal());
    Point2D pntCardDeck = new Point2D(bndCardDeck.getMinX(), bndCardDeck.getMinY());

    // Add a sequential transition to the card (move, rotate)
    SequentialTransition transMoveRotCard = createTransition(pntCardDeck, pntCardDealt, imView);

    // Add a parallel transition to the card (fade in/fade out).
    final ParallelTransition transFadeCardInOut =
        createFadeTransition(
            imgvCardFaceDown,
            new Image(getClass().getResourceAsStream(strCard), 75, 75, true, true));

    SequentialTransition transAllActions = new SequentialTransition();
    transAllActions.getChildren().addAll(transMoveRotCard, transFadeCardInOut);

    return transAllActions;
  }
Ejemplo n.º 22
0
 public Pair<CoordinatePair, CoordinatePair> addAnchorLines(ComponentView componentView) {
   Bounds bounds = componentView.getBoundsInParent();
   // Left Anchor wire
   CoordinatePair pair1 =
       new CoordinatePair(bounds.getMinX() - Component.OFFSET, bounds.getMaxY() / 2);
   // Right Anchor wire
   CoordinatePair pair2 =
       new CoordinatePair(bounds.getMaxX() + Component.OFFSET, bounds.getMaxY() / 2);
   Line line1 =
       new Line(
           bounds.getMinX(),
           bounds.getMaxY() / 2,
           bounds.getMinX() - Component.OFFSET,
           bounds.getMaxY() / 2);
   Line line2 =
       new Line(
           bounds.getMaxX(),
           bounds.getMaxY() / 2,
           bounds.getMaxX() + Component.OFFSET,
           bounds.getMaxY() / 2);
   componentView.getChildren().addAll(line1, line2);
   return new Pair(pair1, pair2);
 }
Ejemplo n.º 23
0
 protected void animateDrawBox(double frac, Context context) {
   rectangle.setWidth(bounds.getWidth() * frac);
   rectangle.setHeight(bounds.getHeight() * frac);
   if (frac != 0d) rectangle.setVisible(true);
 }
  @Override
  public void drag(MouseEvent e, Dimension delta) {
    if (invalidGesture) {
      return;
    }

    if (selectionBounds == null) {
      return;
    }

    Rectangle sel = updateSelectionBounds(e);
    for (IContentPart<Node, ? extends Node> targetPart : targetParts) {
      // compute initial and new bounds for this target
      Bounds initialBounds = getBounds(selectionBounds, targetPart);
      Bounds newBounds = getBounds(sel, targetPart);

      // compute translation in scene coordinates
      double dx = newBounds.getMinX() - initialBounds.getMinX();
      double dy = newBounds.getMinY() - initialBounds.getMinY();

      // transform translation to parent coordinates
      Node visual = targetPart.getVisual();
      Point2D originInParent = visual.getParent().sceneToLocal(0, 0);
      Point2D deltaInParent = visual.getParent().sceneToLocal(dx, dy);
      dx = deltaInParent.getX() - originInParent.getX();
      dy = deltaInParent.getY() - originInParent.getY();

      // apply translation
      getTransformPolicy(targetPart).setPostTranslate(translateIndices.get(targetPart), dx, dy);

      // check if we can resize the part
      AffineTransform affineTransform = getTransformPolicy(targetPart).getCurrentNodeTransform();
      if (affineTransform.getRotation().equals(Angle.fromDeg(0))) {
        // no rotation => resize possible
        // TODO: special case 90 degree rotations
        double dw = newBounds.getWidth() - initialBounds.getWidth();
        double dh = newBounds.getHeight() - initialBounds.getHeight();
        Point2D originInLocal = visual.sceneToLocal(newBounds.getMinX(), newBounds.getMinY());
        Point2D dstInLocal =
            visual.sceneToLocal(newBounds.getMinX() + dw, newBounds.getMinY() + dh);
        dw = dstInLocal.getX() - originInLocal.getX();
        dh = dstInLocal.getY() - originInLocal.getY();
        getResizePolicy(targetPart).resize(dw, dh);
      } else {
        // compute scaling based on bounds change
        double sx = newBounds.getWidth() / initialBounds.getWidth();
        double sy = newBounds.getHeight() / initialBounds.getHeight();
        // apply scaling
        getTransformPolicy(targetPart).setPostScale(scaleIndices.get(targetPart), sx, sy);
      }
    }
  }
  private static Bounds unionOfBounds(Bounds b1, Bounds b2) {
    final Bounds result;

    if (b1.isEmpty()) {
      result = b2;
    } else if (b2.isEmpty()) {
      result = b1;
    } else {
      final double minX = Math.min(b1.getMinX(), b2.getMinX());
      final double minY = Math.min(b1.getMinY(), b2.getMinY());
      final double minZ = Math.min(b1.getMinZ(), b2.getMinZ());
      final double maxX = Math.max(b1.getMaxX(), b2.getMaxX());
      final double maxY = Math.max(b1.getMaxY(), b2.getMaxY());
      final double maxZ = Math.max(b1.getMaxZ(), b2.getMaxZ());

      assert minX <= maxX;
      assert minY <= maxY;
      assert minZ <= maxZ;

      result = new BoundingBox(minX, minY, minZ, maxX - minX, maxY - minY, maxZ - minZ);
    }

    return result;
  }
Ejemplo n.º 26
0
 private static boolean areBoundsVisible(Bounds bounds) {
   return !bounds.isEmpty();
 }
Ejemplo n.º 27
0
 /** Translates the given bounds in the given window to the screen's coordinate system */
 public static Bounds boundsOnScreen(Bounds boundsInWindow, Window window) {
   Bounds windowBoundsOnScreen = bounds(window);
   return translateBounds(
       boundsInWindow, byOffset(windowBoundsOnScreen.getMinX(), windowBoundsOnScreen.getMinY()));
 }
  private static Bounds computeUnclippedBounds(Node node) {
    final Bounds layoutBounds;
    double minX, minY, maxX, maxY, minZ, maxZ;

    assert node != null;
    assert node.getLayoutBounds().isEmpty() == false;

    layoutBounds = node.getLayoutBounds();
    minX = layoutBounds.getMinX();
    minY = layoutBounds.getMinY();
    maxX = layoutBounds.getMaxX();
    maxY = layoutBounds.getMaxY();
    minZ = layoutBounds.getMinZ();
    maxZ = layoutBounds.getMaxZ();

    if (node instanceof Parent) {
      final Parent parent = (Parent) node;

      for (Node child : parent.getChildrenUnmodifiable()) {
        final Bounds childBounds = child.getBoundsInParent();
        minX = Math.min(minX, childBounds.getMinX());
        minY = Math.min(minY, childBounds.getMinY());
        maxX = Math.max(maxX, childBounds.getMaxX());
        maxY = Math.max(maxY, childBounds.getMaxY());
        minZ = Math.min(minZ, childBounds.getMinZ());
        maxZ = Math.max(maxZ, childBounds.getMaxZ());
      }
    }

    assert minX <= maxX;
    assert minY <= maxY;
    assert minZ <= maxZ;

    return new BoundingBox(minX, minY, minZ, maxX - minX, maxY - minY, maxZ - minZ);
  }
Ejemplo n.º 29
0
  @Override
  protected void layoutChildren() {
    super.layoutChildren();

    if (_nodeByPosition.isEmpty()) {
      adjustLineCount(0);

      setPrefWidth(0);
      setPrefHeight(0);

      return;
    }

    // Calculate width per position based on layout bounds
    Map<NodePosition, Double> widthByPosition = new HashMap<>();
    Map<Integer, Double> levelHeight = new HashMap<>();

    Map<Integer, Set<NodePosition>> positionsByLevel = new HashMap<>();
    Map<NodePosition, Set<NodePosition>> positionsByParentPosition = new HashMap<>();

    int maxLevel = Collections.max(_nodeByLevel.keySet());

    for (int curLevel = maxLevel; curLevel >= 0; --curLevel) {
      levelHeight.put(curLevel, 0.0);

      positionsByLevel.put(curLevel, new HashSet<NodePosition>());
    }

    for (int curLevel = maxLevel; curLevel >= 0; --curLevel) {
      // Get bounds of nodes on current level
      Set<Node> curLevelNodes = _nodeByLevel.get(curLevel);

      if (curLevelNodes != null) {
        // Get node bounds
        for (Node node : curLevelNodes) {
          // Node data
          NodePosition nodePosition = _positionByNode.get(node);
          Bounds nodeBounds = node.getLayoutBounds();

          // Get bounds
          widthByPosition.put(nodePosition, nodeBounds.getWidth() + this.getXAxisSpacing());
          levelHeight.put(
              curLevel,
              Math.max(levelHeight.get(curLevel), nodeBounds.getHeight() + this.getYAxisSpacing()));

          // Register positions
          positionsByLevel.get(curLevel).add(nodePosition);

          if (curLevel > 0) {
            positionsByLevel.get(curLevel - 1).add(nodePosition.getParent());
          }
        }
      }

      // Calculate position widths of current level
      for (NodePosition position : positionsByLevel.get(curLevel)) {
        // Register positions
        if (position.getLevel() > 0) {
          NodePosition parentPosition = position.getParent();

          positionsByLevel.get(position.getLevel() - 1).add(parentPosition);

          if (positionsByParentPosition.containsKey(parentPosition) == false) {
            positionsByParentPosition.put(parentPosition, new HashSet<NodePosition>());
          }

          positionsByParentPosition.get(parentPosition).add(position);
        }

        // Get width of children
        double widthOfChildren = 0;

        Set<NodePosition> parentPositions = positionsByParentPosition.get(position);

        if (parentPositions != null) {
          for (NodePosition childPosition : parentPositions) {
            if (widthByPosition.containsKey(childPosition) == true) {
              widthOfChildren += widthByPosition.get(childPosition);
            }
          }
        }

        // Get maximum of node bound and sum of child node bounds
        if (widthByPosition.containsKey(position) == false) {
          widthByPosition.put(position, widthOfChildren);
        } else {
          widthByPosition.put(position, Math.max(widthByPosition.get(position), widthOfChildren));
        }
      }
    }

    // Calculate position boxes
    Map<NodePosition, Rectangle2D> boxesByPosition = new HashMap<>();

    if (positionsByLevel.containsKey(0) == false || positionsByLevel.get(0).size() != 1) {
      throw new IllegalStateException();
    }

    boxesByPosition.put(
        NodePosition.ROOT,
        new Rectangle2D(0, 0, widthByPosition.get(NodePosition.ROOT), levelHeight.get(0)));

    for (int curLevel = 0; curLevel <= maxLevel; ++curLevel) {
      for (NodePosition position : positionsByLevel.get(curLevel)) {
        Rectangle2D positionBox = boxesByPosition.get(position);

        List<NodePosition> childPositions = new ArrayList<>();

        if (positionsByParentPosition.containsKey(position)) {
          childPositions.addAll(positionsByParentPosition.get(position));
        }

        Collections.sort(childPositions);

        double childX = positionBox.getMinX();

        for (NodePosition childPosition : childPositions) {
          double childWidth = widthByPosition.get(childPosition);

          boxesByPosition.put(
              childPosition,
              new Rectangle2D(
                  childX,
                  positionBox.getMaxY(),
                  childWidth,
                  levelHeight.get(childPosition.getLevel())));

          childX += childWidth;
        }
      }
    }

    // Position nodes
    Map<NodePosition, Double> xCenterHintByPosition = new HashMap<>();
    Map<NodePosition, Double> yCenterHintByPosition = new HashMap<>();

    for (int curLevel = maxLevel; curLevel >= 0; --curLevel) {
      for (NodePosition position : positionsByLevel.get(curLevel)) {
        // Calculate center hints
        Rectangle2D positionBox = boxesByPosition.get(position);

        double xCenterHint = (positionBox.getMinX() + positionBox.getMaxX()) / 2;

        if (xCenterHintByPosition.containsKey(position) == true) {
          xCenterHint = xCenterHintByPosition.get(position);
        }

        double yCenterHint = (positionBox.getMinY() + positionBox.getMaxY()) / 2;

        xCenterHintByPosition.put(position, xCenterHint);
        yCenterHintByPosition.put(position, yCenterHint);

        // Position node
        if (_nodeByPosition.containsKey(position)) {
          Node node = _nodeByPosition.get(position);
          Bounds nodeBounds = node.getLayoutBounds();

          node.relocate(
              xCenterHint - nodeBounds.getWidth() / 2, yCenterHint - nodeBounds.getHeight() / 2);
        }

        // Update parent node position hint
        NodePosition parentPosition = position.getParent();

        if (xCenterHintByPosition.containsKey(parentPosition)) {
          xCenterHintByPosition.put(
              parentPosition, (xCenterHintByPosition.get(parentPosition) + xCenterHint) / 2);
        } else {
          xCenterHintByPosition.put(parentPosition, xCenterHint);
        }
      }
    }

    // Update lines
    if (this.getShowLines() == true) {
      adjustLineCount(boxesByPosition.size() - 1);

      int currentLine = 0;

      for (NodePosition position : boxesByPosition.keySet()) {
        if (positionsByParentPosition.containsKey(position) == false) {
          continue;
        }

        for (NodePosition childPosition : positionsByParentPosition.get(position)) {
          Bounds fromBounds =
              _nodeByPosition.containsKey(position)
                  ? _nodeByPosition.get(position).getLayoutBounds()
                  : null;
          Bounds toBounds =
              _nodeByPosition.containsKey(childPosition)
                  ? _nodeByPosition.get(childPosition).getLayoutBounds()
                  : null;

          Point2D lineFrom =
              new Point2D(
                  xCenterHintByPosition.get(position),
                  yCenterHintByPosition.get(position)
                      + (fromBounds != null ? (fromBounds.getHeight() / 2) : 0)
                      + this.getLineSpacing());

          Point2D lineTo =
              new Point2D(
                  xCenterHintByPosition.get(childPosition),
                  yCenterHintByPosition.get(childPosition)
                      - (toBounds != null ? (toBounds.getHeight() / 2) : 0)
                      - this.getLineSpacing());

          Line l = _lines.get(currentLine);

          l.setStartX(lineFrom.getX());
          l.setStartY(lineFrom.getY());

          l.setEndX(lineTo.getX());
          l.setEndY(lineTo.getY());

          ++currentLine;
        }
      }
    } else {
      adjustLineCount(0);
    }

    // Update preferred size
    double totalHeight = 0;
    for (Double h : levelHeight.values()) {
      totalHeight += h;
    }

    setPrefWidth(widthByPosition.get(NodePosition.ROOT));
    setPrefHeight(totalHeight);
  }
  public TreeTableColumnResizer(TreeTableColumn<?, ?> treeTableColumn) {
    assert treeTableColumn != null;
    assert treeTableColumn.getTreeTableView() != null;

    this.treeTableColumn = treeTableColumn;
    this.originalSizing = new ColumnSizing(this.treeTableColumn);

    final List<?> columns;
    if (this.treeTableColumn.getParentColumn() != null) {
      columns = this.treeTableColumn.getParentColumn().getColumns();
    } else {
      columns = this.treeTableColumn.getTreeTableView().getColumns();
    }
    final int columnIndex = columns.indexOf(this.treeTableColumn);
    if (columnIndex + 1 < columns.size()) {
      this.treeTableColumnNext = (TreeTableColumn<?, ?>) columns.get(columnIndex + 1);
      this.originalSizingNext = new ColumnSizing(this.treeTableColumnNext);
    } else {
      this.treeTableColumnNext = null;
      this.originalSizingNext = null;
    }

    //
    //  Case #1 : treeTableColumnNext != null
    //
    //         x1                x2                       x3
    //       --+-----------------+------------------------+--
    //         |      col n      |         col n+1        |
    //         |                 |                        |
    //
    //       Range for moving x2 is [x1, x3]
    //
    //
    //  Case #2 : treeTableColumnNext == null
    //
    //      Case #2.1: treeTableColumn.getParentColumn() != null
    //
    //         x1                x2                       x3
    //       --+-----------------+                        |
    //         |      col n      |                        |
    //         |                 |                        |
    //                                               parentColumn maxX
    //
    //
    //      Case #2.2: treeTableColumn.getParentColumn() == null
    //
    //         x1                x2                       x3
    //       --+-----------------+                        |
    //         |      col n      |                        |
    //         |                 |                        |
    //                                               treeTableView maxX
    //
    //       Range for moving x2 is [x1, x3]
    //
    //

    final TreeTableViewDesignInfoX di = new TreeTableViewDesignInfoX();
    final Bounds columnBounds = di.getColumnBounds(treeTableColumn);
    x1 = columnBounds.getMinX();
    x2 = columnBounds.getMaxX();
    if (treeTableColumnNext != null) {
      final Bounds nextBounds = di.getColumnBounds(treeTableColumnNext);
      x3 = nextBounds.getMaxX();
    } else {
      if (treeTableColumn.getParentColumn() != null) {
        final TableColumnBase<?, ?> parentColumn =
            (TableColumnBase<?, ?>) this.treeTableColumn.getParentColumn();
        assert parentColumn instanceof TreeTableColumn<?, ?>;
        final Bounds parentBounds = di.getColumnBounds((TreeTableColumn<?, ?>) parentColumn);
        x3 = parentBounds.getMaxX();
      } else {
        final Bounds layoutBounds = treeTableColumn.getTreeTableView().getLayoutBounds();
        x3 = layoutBounds.getMaxX();
      }
    }
  }