/** * calculate the layout point for a popup window based on the two points, windowPoint from popup * window and parentPoint from parent node * * @param window the popup window * @param windowPoint align point inside popup window * @param parent parent node with which the popup window try to align * @param parentPoint align point inside parent node * @return the Point2D */ public static Point2D pointRelativeTo( PopupWindow window, Point2D windowPoint, Node parent, Point2D parentPoint) { if (parent == null || window == null) { return null; } double anchorWidth = window.getWidth(); double anchorHeight = window.getHeight(); Point2D point2D = Utils.pointRelativeTo(parent, anchorWidth, anchorHeight, HPos.CENTER, VPos.BOTTOM, false); // Rectangle2D screenBounds = JideFXUtilities.getScreenBounds(new Point2D(0, // parentPoint.getY())); // if (screenBounds.getMaxX() >= point2D.getX() + anchorWidth + parentPoint.getX() - // windowPoint.getX()) { // point2D = point2D.add(parentPoint.getX() - windowPoint.getX(), 0); // } // if (screenBounds.getMaxY() >= point2D.getY() + anchorHeight + parentPoint.getY() - // parent.getLayoutBounds().getMaxY() - windowPoint.getY()) { // point2D = point2D.add(0, parentPoint.getY() - parent.getLayoutBounds().getMaxY() + // windowPoint.getY()); // } point2D = new Point2D( point2D.getX() + parentPoint.getX() - windowPoint.getX(), point2D.getY() + parentPoint.getY() - parent.getLayoutBounds().getMaxY() + windowPoint.getY()); return point2D; }
/** * Traverse the scene graph for all open stages and pick an event target for a dock event based on * the location. Once the event target is chosen run the event task with the target and the * previous target of the last dock event if one is cached. If an event target is not found fire * the explicit dock event on the stage root if one is provided. * * @param location The location of the dock event in screen coordinates. * @param eventTask The event task to be run when the event target is found. * @param explicit The explicit event to be fired on the stage root when no event target is found. */ private void pickEventTarget(Point2D location, EventTask eventTask, Event explicit) { // RFE for public scene graph traversal API filed but closed: // https://bugs.openjdk.java.net/browse/JDK-8133331 ObservableList<Stage> stages = FXCollections.unmodifiableObservableList(StageHelper.getStages()); // fire the dock over event for the active stages for (Stage targetStage : stages) { // obviously this title bar does not need to receive its own events // though users of this library may want to know when their // dock node is being dragged by subclassing it or attaching // an event listener in which case a new event can be defined or // this continue behavior can be removed if (targetStage == this.dockNode.getStage()) continue; eventTask.reset(); Node dragNode = dragNodes.get(targetStage); Parent root = targetStage.getScene().getRoot(); Stack<Parent> stack = new Stack<Parent>(); if (root.contains(root.screenToLocal(location.getX(), location.getY())) && !root.isMouseTransparent()) { stack.push(root); } // depth first traversal to find the deepest node or parent with no children // that intersects the point of interest while (!stack.isEmpty()) { Parent parent = stack.pop(); // if this parent contains the mouse click in screen coordinates in its local bounds // then traverse its children boolean notFired = true; for (Node node : parent.getChildrenUnmodifiable()) { if (node.contains(node.screenToLocal(location.getX(), location.getY())) && !node.isMouseTransparent()) { if (node instanceof Parent) { stack.push((Parent) node); } else { eventTask.run(node, dragNode); } notFired = false; break; } } // if none of the children fired the event or there were no children // fire it with the parent as the target to receive the event if (notFired) { eventTask.run(parent, dragNode); } } if (explicit != null && dragNode != null && eventTask.getExecutions() < 1) { Event.fireEvent(dragNode, explicit.copyFor(this, dragNode)); dragNodes.put(targetStage, null); } } }
public Shape getReflectionClip() { if (reflection == null || !getSkinnable().getClipReflections()) { return null; } return new Polygon( ulReflection2d.getX(), ulReflection2d.getY(), urReflection2d.getX(), urReflection2d.getY(), perspectiveTransform.getLrx(), perspectiveTransform.getLry(), perspectiveTransform.getLlx(), perspectiveTransform.getLly()); }
@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); } } }
public void update(Point2D center, Dimension2D dim) { moveTo1.setX(center.getX() - 10); moveTo1.setY(center.getY()); lineTo1.setX(center.getX() + 10); lineTo1.setY(center.getY()); moveTo2.setX(center.getX()); moveTo2.setY(center.getY() - 10); lineTo2.setX(center.getX()); lineTo2.setY(center.getY() + 10); }
protected Point2D getCrossPointWithSide( Point2D point, double rotationAngleInDegrees, double fieldWidth, double fieldHeight, GameState.SystemSides side) { double centerX = point.getX(), centerY = point.getY(); double angle = (rotationAngleInDegrees + 360) % 360; if (angle == 90) { return new Point2D(centerX, fieldHeight); } else if (angle == 270) { return new Point2D(centerX, 0); } double a = Math.tan(Math.toRadians(angle)); double b = centerY - a * centerX; switch (side) { case Right: return new Point2D(fieldWidth, a * fieldWidth + b); case Down: return new Point2D((fieldHeight - b) / a, fieldHeight); case Left: return new Point2D(0, b); case Up: return new Point2D(-b / a, 0); default: return new Point2D(0, 0); } }
public Rectangle getLocalRectangle() { Point2D pos = this.parent.getPosition(); double x = pos.getX(); double y = pos.getY(); outside: for (Node node : this.parent.getRootPane().getItems()) { if (node != this) { y = 0; // VerticalなSplitPane if (node instanceof DockableAreaGroupPane) { DockableAreaGroupPane pane = (DockableAreaGroupPane) node; for (DockableTabPane tabPane : pane.getTabPaneList()) { if (tabPane != this) { y += tabPane.getHeight(); } else { break outside; } } x += pane.getWidth(); } } else { break; } } double w = this.getWidth(); double h = this.getHeight(); // System.out.println("x:" + x + " y:" + y + " w:" + w + " h:" + h); Rectangle rect = new Rectangle(); rect.x = (int) x; rect.y = (int) y; rect.width = (int) w; rect.height = (int) h; return rect; }
static enum ShapeType { RECTANGLE(gc -> gc.fillRect(0, 0, CIRCLE_RADIUS, CIRCLE_RADIUS)), CIRCLE(gc -> gc.fillOval(0, 0, CIRCLE_RADIUS, CIRCLE_RADIUS)), ELLIPSE( gc -> gc.fillOval( ELLIPSE_CENTER.getX(), ELLIPSE_CENTER.getY(), ELLIPSE_RADIUS.getWidth(), ELLIPSE_RADIUS.getHeight())); ShapeType(Consumer<GraphicsContext> drawAlgorithm) { this.drawAlgorithm = drawAlgorithm; } public void drawWith(GraphicsContext gc) { drawAlgorithm.accept(gc); } public void clearAndDrawWith(GraphicsContext gc) { gc.clearRect(0, 0, CIRCLE_RADIUS, CIRCLE_RADIUS); gc.setFill(Color.TAN); drawAlgorithm.accept(gc); } private Consumer<GraphicsContext> drawAlgorithm; }
private static Bounds translateBounds(Bounds bounds, Point2D offset) { return new BoundingBox( bounds.getMinX() + offset.getX(), bounds.getMinY() + offset.getY(), bounds.getWidth(), bounds.getHeight()); }
private void showContextMenu(double x, double y) { if (contextMenu != null && contextMenu.isShowing()) contextMenu.hide(); contextMenu = new ContextMenu(); // contextMenu.getStyleClass().add("background"); Color bg = cfg.colorProperty().getValue(); Color fg = bg.getBrightness() < 0.5f ? Color.WHITE : Color.BLACK; contextMenu.setStyle(background(bg, true)); contextMenu.setOnHidden( value -> { if (cfg.autoHideProperty().get() && !arePopupsOpen()) maybeHideDock(); }); if (!cfg.autoHideProperty().get()) { MenuItem hide = new MenuItem(resources.getString("menu.hide")); hide.setOnAction(value -> getStage().setIconified(true)); hide.setStyle(textFill(fg)); contextMenu.getItems().add(hide); } MenuItem close = new MenuItem(resources.getString("menu.exit")); close.setOnAction( value -> { context.confirmExit(); maybeHideDock(); }); close.setStyle(textFill(fg)); contextMenu.getItems().add(close); Point2D loc = new Point2D(x + getStage().getX(), y + getStage().getY()); contextMenu.show(dockContent, loc.getX(), loc.getY()); }
protected GameState.SystemSides findSystemSide( Point2D point, double rotationAngleInDegrees, double fieldWidth, double fieldHeight) { double centerX = point.getX(), centerY = point.getY(); double angle = (rotationAngleInDegrees + 360) % 360; double angleLimits[] = new double[4]; angleLimits[0] = Math.atan2(-centerY, fieldWidth - centerX); angleLimits[1] = Math.atan2(fieldHeight - centerY, fieldWidth - centerX); angleLimits[2] = Math.atan2(fieldHeight - centerY, -centerX); angleLimits[3] = Math.atan2(-centerY, -centerX); for (int i = 0; i < 4; i++) { angleLimits[i] = (Math.toDegrees(angleLimits[i]) + 360) % 360; } if (angle >= angleLimits[0] || angle <= angleLimits[1]) { return GameState.SystemSides.Right; } else if (angle < angleLimits[2]) { return GameState.SystemSides.Down; } else if (angle <= angleLimits[3]) { return GameState.SystemSides.Left; } else { return GameState.SystemSides.Up; } }
private SequentialTransition createTransition( final Point2D pntStartPoint, final Point2D pntEndPoint, ImageView imView) { imView = new ImageView( new Image(getClass().getResourceAsStream("/res/img/b1fh.png"), 75, 75, true, true)); imView.setX(pntStartPoint.getX()); imView.setY(pntStartPoint.getY() - 30); APMainScreen.getChildren().add(imView); TranslateTransition translateTransition = new TranslateTransition(Duration.millis(300), imView); translateTransition.setFromX(0); translateTransition.setToX(pntEndPoint.getX() - pntStartPoint.getX()); translateTransition.setFromY(0); translateTransition.setToY(pntEndPoint.getY() - pntStartPoint.getY()); translateTransition.setCycleCount(1); translateTransition.setAutoReverse(false); int rnd = randInt(1, 3); RotateTransition rotateTransition = new RotateTransition(Duration.millis(150), imView); rotateTransition.setByAngle(90F); rotateTransition.setCycleCount(rnd); rotateTransition.setAutoReverse(false); ParallelTransition parallelTransition = new ParallelTransition(); parallelTransition.getChildren().addAll(translateTransition, rotateTransition); SequentialTransition seqTrans = new SequentialTransition(); seqTrans.getChildren().addAll(parallelTransition); final ImageView ivRemove = imView; seqTrans.setOnFinished( new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent arg0) { APMainScreen.getChildren().remove(ivRemove); } }); return seqTrans; }
private boolean areWithinDistance(final Optional<Point> pos, final Point2D point) { if (pos.isPresent()) { final double dx = Math.abs(pos.get().x - point.getX()); final double dy = Math.abs(pos.get().y - point.getY()); return dx * dx + dy * dy <= X_RADIUS * X_RADIUS; } return false; }
private static Point2D calcIntersection(Point2D ap1, Point2D ap2, Point2D bp1, Point2D bp2) { final double a1 = ap1.getY() - ap2.getY(); final double b1 = ap2.getX() - ap1.getX(); final double c1 = ap1.getX() * ap2.getY() - ap2.getX() * ap1.getY(); final double a2 = bp1.getY() - bp2.getY(); final double b2 = bp2.getX() - bp1.getX(); final double c2 = bp1.getX() * bp2.getY() - bp2.getX() * bp1.getY(); final double d = a1 * b2 - a2 * b1; return new Point2D((b1 * c2 - b2 * c1) / d, (c1 * a2 - c2 * a1) / d); }
public Point2D sceneGraphObjectToDecoration(double x, double y, boolean snapToPixel) { Point2D result = sceneGraphObjectToDecoration(x, y); if (snapToPixel) { final double rx = Math.round(result.getX()); final double ry = Math.round(result.getY()); result = new Point2D(rx, ry); } return result; }
public void show(Control ownerControl) { Point2D point = ownerControl.localToScene(ownerControl.getWidth() / 2, ownerControl.getHeight()); double x = point.getX() + ownerControl.getScene().getX() + ownerControl.getScene().getWindow().getX(); double y = point.getY() + ownerControl.getScene().getY() + ownerControl.getScene().getWindow().getY(); popup.show(ownerControl, x - getPopoverPointX(), y - getPopoverPointY()); }
public double getValue(Point2D p) { switch (this) { case X: return p.getX(); case Y: return p.getY(); } assert false; return 0; }
public void moveTo(Robot robot, Node visual, double localX, double localY) throws InterruptedException { Point2D localToScene = visual.localToScene(localX, localY); double x = scene.getWindow().getX() + localToScene.getX(); double y = scene.getWindow().getY() + localToScene.getY(); EventSynchronizer<MouseEvent> synchronizer = new EventSynchronizer<MouseEvent>(visual, MouseEvent.MOUSE_ENTERED); synchronizer.register(); robot.mouseMove((int) x, (int) y); synchronizer.await(); }
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()); }
public static Point2D latLongToPixel( final Dimension2D MAP_DIMENSION, final Point2D UPPER_LEFT, final Point2D LOWER_RIGHT, final Point2D LOCATION) { final double LATITUDE = LOCATION.getX(); final double LONGITUDE = LOCATION.getY(); final double MAP_WIDTH = MAP_DIMENSION.getWidth(); final double MAP_HEIGHT = MAP_DIMENSION.getHeight(); final double WORLD_MAP_WIDTH = ((MAP_WIDTH / (LOWER_RIGHT.getY() - UPPER_LEFT.getY())) * 360) / (2 * Math.PI); final double MAP_OFFSET_Y = (WORLD_MAP_WIDTH / 2 * Math.log10( (1 + Math.sin(Math.toRadians(LOWER_RIGHT.getX()))) / (1 - Math.sin(Math.toRadians(LOWER_RIGHT.getX()))))); final double X = (LONGITUDE - UPPER_LEFT.getY()) * (MAP_WIDTH / (LOWER_RIGHT.getY() - UPPER_LEFT.getY())); final double Y = MAP_HEIGHT - ((WORLD_MAP_WIDTH / 2 * Math.log10( (1 + Math.sin(Math.toRadians(LATITUDE))) / (1 - Math.sin(Math.toRadians(LATITUDE))))) - MAP_OFFSET_Y); return new Point2D(X, Y); }
/** * Check if the provided mouse location would select the annotation * * @param point Location of mouse on screen * @return <code>true</code> if this annotation gets selected at that mouse location */ boolean isSelected(final Point2D point) { final Optional<Rectangle> rect = screen_box; if (rect.isPresent() && rect.get().contains(point.getX(), point.getY())) { selected = Selection.Body; return true; } if (areWithinDistance(screen_pos, point)) { selected = Selection.Reference; return true; } return false; }
public PerspectiveTransform build() { double w = cell.prefWidth(50); double h = cell.prefHeight(50); double viewDistance = getWidth() * getSkinnable().getViewDistanceRatio() + getWidth() * getSkinnable().getRadiusRatio(); double fov = getSkinnable().getFieldOfViewRatio() * getSkinnable().getWidth(); double cw = w / 2; double ch = h / 2; Point2D ul2d = project(ul, viewDistance, fov, cw, ch); Point2D ur2d = project(ur, viewDistance, fov, cw, ch); Point2D ll2d = project(ll, viewDistance, fov, cw, ch); Point2D lr2d = project(lr, viewDistance, fov, cw, ch); perspectiveTransform = new PerspectiveTransform( ul2d.getX(), ul2d.getY(), ur2d.getX(), ur2d.getY(), lr2d.getX(), lr2d.getY(), ll2d.getX(), ll2d.getY()); if (reflection != null) { ulReflection2d = project(ulReflection, viewDistance, fov, cw, ch); urReflection2d = project(urReflection, viewDistance, fov, cw, ch); perspectiveTransform.setInput(reflection); } return perspectiveTransform; }
/* */ public WCPoint screenToWindow(WCPoint paramWCPoint) { /* 108 */ WebView localWebView = this.accessor.getView(); /* 109 */ Scene localScene = localWebView.getScene(); /* 110 */ Window localWindow = null; /* */ /* 112 */ if ((localScene != null) && ((localWindow = localScene.getWindow()) != null)) /* */ { /* 115 */ Point2D localPoint2D = localWebView.sceneToLocal( paramWCPoint.getX() - localWindow.getX() - localScene.getX(), paramWCPoint.getY() - localWindow.getY() - localScene.getY()); /* */ /* 118 */ return new WCPoint((float) localPoint2D.getX(), (float) localPoint2D.getY()); /* */ } /* 120 */ return new WCPoint(0.0F, 0.0F); /* */ }
/* */ public WCPoint windowToScreen(WCPoint paramWCPoint) /* */ { /* 125 */ WebView localWebView = this.accessor.getView(); /* 126 */ Scene localScene = localWebView.getScene(); /* 127 */ Window localWindow = null; /* */ /* 129 */ if ((localScene != null) && ((localWindow = localScene.getWindow()) != null)) /* */ { /* 132 */ Point2D localPoint2D = localWebView.localToScene(paramWCPoint.getX(), paramWCPoint.getY()); /* 133 */ return new WCPoint( (float) (localPoint2D.getX() + localScene.getX() + localWindow.getX()), (float) (localPoint2D.getY() + localScene.getY() + localWindow.getY())); /* */ } /* */ /* 136 */ return new WCPoint(0.0F, 0.0F); /* */ }
/** 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()); } }
/** * @param node * @return */ public static Tooltip create(Node node) { Tooltip tooltip = new Tooltip(); node.setOnMouseEntered( (MouseEvent event) -> { Point2D p = node.localToScreen( node.getLayoutBounds().getMaxX() / 2, node.getLayoutBounds().getMaxY() + 5); // I position the tooltip at bottom right of the node (see below for // explanation) tooltip.show(node, p.getX(), p.getY()); }); node.setOnMouseExited( (MouseEvent event) -> { tooltip.hide(); }); return tooltip; }
protected FxRobot moveTo(Node node) { baseFXRobot.waitForIdle(); double x; double y; do { try { Thread.sleep(100); } catch (InterruptedException ignored) { } Point2D position = robot.point(node).getPosition(); x = position.getX(); y = position.getY(); robot.moveTo(node); try { Thread.sleep(100); } catch (InterruptedException ignored) { } baseFXRobot.waitForIdle(); } while (hasMoved(node, x, y)); return robot.moveTo(node); }
private Point2D validateCoordinate(Point2D possibleNewLocation) { double maxY = armyAllegiance.getScene().getHeight(); double maxX = armyAllegiance.getScene().getWidth(); double myX = possibleNewLocation.getX(); double myY = possibleNewLocation.getY(); double newX = 0.0; double newY = 0.0; if ((myX < 0) && (myY < 0)) { newX = SingletonRandom.instance.getNormalDistribution(0.0, (0.25 * maxX), 2.0); newY = SingletonRandom.instance.getNormalDistribution(0.0, (0.25 * maxY), 2.0); return new Point2D(newX, newY); } else if ((0 < myX) && (myX < maxX) && (myY < 0)) { newY = SingletonRandom.instance.getNormalDistribution(0.0, (0.25 * maxY), 2.0); return new Point2D(myX, newY); } else if ((myX > maxX) && (myY < 0)) { newX = SingletonRandom.instance.getNormalDistribution(0.0, (0.75 * maxX), 2.0); newY = SingletonRandom.instance.getNormalDistribution(0.0, (0.75 * maxY), 2.0); return new Point2D(newX, newY); } else if ((0 < myX) && (myY < maxY) && (myY > 0)) { newX = SingletonRandom.instance.getNormalDistribution(0.0, (0.25 * maxX), 2.0); return new Point2D(newX, myY); } else if ((myX > maxX) && (myY < maxY) && (myY > 0)) { newX = SingletonRandom.instance.getNormalDistribution(0.0, (0.75 * maxX), 2.0); return new Point2D(newX, myY); } if ((myX < 0) && (myY > maxY)) { newX = SingletonRandom.instance.getNormalDistribution(0.0, (0.25 * maxX), 2.0); newY = SingletonRandom.instance.getNormalDistribution(0.0, (0.75 * maxY), 2.0); return new Point2D(newX, newY); } else if ((0 < myX) && (myX < maxX) && (myY > maxY)) { newY = SingletonRandom.instance.getNormalDistribution(0.0, (0.75 * maxY), 2.0); return new Point2D(myX, newY); } else if ((myX > maxX) && (myY > maxY)) { newX = SingletonRandom.instance.getNormalDistribution(0.0, (0.75 * maxX), 2.0); newY = SingletonRandom.instance.getNormalDistribution(0.0, (0.75 * maxY), 2.0); return new Point2D(newX, newY); } else { return new Point2D(myX, myY); } }
private void updatePopup() { if (getText() == null) { return; } LinkedList<String> searchResult = new LinkedList<>(); searchResult.addAll( entries.stream().filter(x -> x.startsWith(getText())).collect(Collectors.toList())); if (entries.size() > 0) { populatePopup(searchResult); if (!popup.isShowing()) { Point2D p = this.localToScene(0.0, 0.0); double x = p.getX() + this.getScene().getX() + this.getScene().getWindow().getX(); double y = p.getY() + this.getScene().getY() + this.getScene().getWindow().getY() + this.getHeight(); popup.show(AutoCompleteTextField.this, x, y); } } else { popup.hide(); } }
@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); }