diff --git a/api.visual/apichanges.xml b/api.visual/apichanges.xml --- a/api.visual/apichanges.xml +++ b/api.visual/apichanges.xml @@ -739,6 +739,21 @@ + + + Enhancement of the old layout and adding the possibility of minimizing the gaps between nodes. + + + + + + The old layout was improved in a way that nodes in a subtree are vertically aligned by the center of the super nodes. Also the possibility of overlapping in the old layout was removed by adding an approach based on the envelope of the (sub)trees. + The second improvement was done by adding the possibility of minimizing the gaps between nodes. If a tree was very wide, the old layout used a lot of space. To prevent this the new algorithm with the possibility of minimization of gaps is based on the envelope. Therefore subtrees move as close together as possible. + + + + + diff --git a/api.visual/manifest.mf b/api.visual/manifest.mf --- a/api.visual/manifest.mf +++ b/api.visual/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.api.visual OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/visual/resources/Bundle.properties -OpenIDE-Module-Specification-Version: 2.23 +OpenIDE-Module-Specification-Version: 2.24 AutoUpdate-Essential-Module: true diff --git a/api.visual/src/org/netbeans/api/visual/graph/layout/GraphLayoutFactory.java b/api.visual/src/org/netbeans/api/visual/graph/layout/GraphLayoutFactory.java --- a/api.visual/src/org/netbeans/api/visual/graph/layout/GraphLayoutFactory.java +++ b/api.visual/src/org/netbeans/api/visual/graph/layout/GraphLayoutFactory.java @@ -71,6 +71,43 @@ } /** + * Creates a tree graph layout. + * Use GraphLayoutSupport.setTreeGraphLayoutRootNode method to set the root node of the graph. + * If not set/found, then layout is not executed. + * Note: Use GraphLayoutSupport.setTreeGraphLayoutProperties method to set the parameters of the layout later. + * @param originX the x-axis origin + * @param originY the y-axis origin + * @param verticalGap the vertical gap between cells + * @param horizontalGap the horizontal gap between cells + * @param vertical if true, then layout organizes the graph vertically; if false, then horizontally + * @param minimizeGap if true, then minimize the gap between cells; if false do the normal tree layout + * @return the tree graph layout + * @since 2.24 + */ + public static GraphLayout createTreeGraphLayout(int originX, int originY, int verticalGap, int horizontalGap, boolean vertical, boolean minimizeGap) { + return createTreeGraphLayout(originX, originY, verticalGap, horizontalGap, vertical, minimizeGap, TreeGraphLayoutAlignment.TOP); + } + + /** + * Creates a tree graph layout. + * Use GraphLayoutSupport.setTreeGraphLayoutRootNode method to set the root node of the graph. + * If not set/found, then layout is not executed. + * Note: Use GraphLayoutSupport.setTreeGraphLayoutProperties method to set the parameters of the layout later. + * @param originX the x-axis origin + * @param originY the y-axis origin + * @param verticalGap the vertical gap between cells + * @param horizontalGap the horizontal gap between cells + * @param vertical if true, then layout organizes the graph vertically; if false, then horizontally + * @param minimizeGap if true, then minimize the gap between cells; if false do the normal tree layout + * @param alignment the alignment of the nodes in their level. Choose wheter {@code TreeGraphLayout.Alignment.TOP}, {@code TreeGraphLayout.Alignment.CENTER} or {@code TreeGraphLayout.Alignment.BOTTOM} + * @return the tree graph layout + * @since 2.24 + */ + public static GraphLayout createTreeGraphLayout(int originX, int originY, int verticalGap, int horizontalGap, boolean vertical, boolean minimizeGap, TreeGraphLayoutAlignment alignment) { + return new TreeGraphLayout(originX, originY, verticalGap, horizontalGap, vertical, minimizeGap, alignment); + } + + /** * * @param the node class for the nodes in the graph. * @param the edge class for the edges in the graph. diff --git a/api.visual/src/org/netbeans/api/visual/graph/layout/GraphLayoutSupport.java b/api.visual/src/org/netbeans/api/visual/graph/layout/GraphLayoutSupport.java --- a/api.visual/src/org/netbeans/api/visual/graph/layout/GraphLayoutSupport.java +++ b/api.visual/src/org/netbeans/api/visual/graph/layout/GraphLayoutSupport.java @@ -74,4 +74,37 @@ ((TreeGraphLayout) graph).setProperties (originX, originY, verticalGap, horizontalGap, vertical); } + /** + * Sets properties to a tree graph layout. + * @param graph the tree graph layout + * @param originX the x-axis origin + * @param originY the y-axis origin + * @param verticalGap the vertical gap between cells + * @param horizontalGap the horizontal gap between cells + * @param vertical if true, then layout organizes the graph vertically; if false, then horizontally + * @param minimizeGap if true, then minimize the gap between cells; if false do the normal tree layout. + * @since 2.24 + */ + public static void setTreeGraphLayoutProperties(GraphLayout graph, int originX, int originY, int verticalGap, int horizontalGap, boolean vertical, boolean minimizeGap) { + if (graph instanceof TreeGraphLayout) + setTreeGraphLayoutProperties(graph, originX, originY, verticalGap, horizontalGap, vertical, minimizeGap, TreeGraphLayoutAlignment.TOP); + } + + /** + * Sets properties to a tree graph layout. + * @param graph the tree graph layout + * @param originX the x-axis origin + * @param originY the y-axis origin + * @param verticalGap the vertical gap between cells + * @param horizontalGap the horizontal gap between cells + * @param vertical if true, then layout organizes the graph vertically; if false, then horizontally + * @param minimizeGap if true, then minimize the gap between cells; if false do the normal tree layout. + * @param alignment alignment the alignment of the nodes in their level. Choose wheter {@code TreeGraphLayout.Alignment.TOP}, {@code TreeGraphLayout.Alignment.CENTER} or {@code TreeGraphLayout.Alignment.BOTTOM} + * @since 2.24 + */ + public static void setTreeGraphLayoutProperties(GraphLayout graph, int originX, int originY, int verticalGap, int horizontalGap, boolean vertical, boolean minimizeGap, TreeGraphLayoutAlignment alignment) { + if (graph instanceof TreeGraphLayout) + ((TreeGraphLayout) graph).setProperties(originX, originY, verticalGap, horizontalGap, vertical,minimizeGap, alignment); + } + } diff --git a/api.visual/src/org/netbeans/api/visual/graph/layout/TreeGraphLayoutAlignment.java b/api.visual/src/org/netbeans/api/visual/graph/layout/TreeGraphLayoutAlignment.java new file mode 100644 --- /dev/null +++ b/api.visual/src/org/netbeans/api/visual/graph/layout/TreeGraphLayoutAlignment.java @@ -0,0 +1,54 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.api.visual.graph.layout; + +/** + * This Enumeration is used to do the alignment in a TreeGraphLayout. It indicates the position within a level. + * @since 2.24 + * @author Erhard Pointl + */ +public enum TreeGraphLayoutAlignment { + /** Alignment at the top of each level. */ + TOP, + /** Alignment at the center of each level. */ + CENTER, + /** Alignment at the bottom of each level. */ + BOTTOM +} diff --git a/api.visual/src/org/netbeans/modules/visual/graph/layout/TreeGraphLayout.java b/api.visual/src/org/netbeans/modules/visual/graph/layout/TreeGraphLayout.java --- a/api.visual/src/org/netbeans/modules/visual/graph/layout/TreeGraphLayout.java +++ b/api.visual/src/org/netbeans/modules/visual/graph/layout/TreeGraphLayout.java @@ -40,15 +40,23 @@ */ package org.netbeans.modules.visual.graph.layout; +import org.netbeans.api.visual.graph.layout.TreeGraphLayoutAlignment; import org.netbeans.api.visual.graph.layout.GraphLayout; import org.netbeans.api.visual.graph.layout.UniversalGraph; import org.netbeans.api.visual.widget.Widget; -import java.awt.*; -import java.util.*; +import java.awt.Point; +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; /** * @author David Kaspar + * @author Erhard Pointl */ public final class TreeGraphLayout extends GraphLayout { @@ -57,15 +65,46 @@ private int verticalGap; private int horizontalGap; private boolean vertical; + /** Indicates whether the gap between two nodes of the same level should be minimized. */ + private boolean minimizeGap; + /** Inicates where the nodes should be aligned within the level. */ + private TreeGraphLayoutAlignment alignment; private N rootNode; + /** + * Constructor + * @param originX the x-axis origin + * @param originY the y-axis origin + * @param verticalGap the vertical gap between cells + * @param horizontalGap the horizontal gap between cells + * @param vertical if true, then layout organizes the graph vertically; if false, then horizontally + */ public TreeGraphLayout (int originX, int originY, int verticalGap, int horizontalGap, boolean vertical) { this.originX = originX; this.originY = originY; this.verticalGap = verticalGap; this.horizontalGap = horizontalGap; this.vertical = vertical; + this.minimizeGap = false; + this.alignment = TreeGraphLayoutAlignment.TOP; + } + + /** + * Constructor + * @param originX the x-axis origin + * @param originY the y-axis origin + * @param verticalGap the vertical gap between cells + * @param horizontalGap the horizontal gap between cells + * @param vertical if true, then layout organizes the graph vertically; if false, then horizontally + * @param minimizeGap if true, then minimize the gap between cells; if false do the normal tree layout. + * @param alignment alignment the alignment of the nodes in their level. Choose wheter {@code TreeGraphLayout.Alignment.TOP}, {@code TreeGraphLayout.Alignment.CENTER} or {@code TreeGraphLayout.Alignment.BOTTOM} + * @since 2.24 + */ + public TreeGraphLayout(int originX, int originY, int verticalGap, int horizontalGap, boolean vertical, boolean minimizeGap, TreeGraphLayoutAlignment alignment) { + this(originX, originY, verticalGap, horizontalGap, vertical); + this.minimizeGap = minimizeGap; + this.alignment = alignment; } public void setRootNode (N rootNode) { @@ -73,13 +112,40 @@ } public void setProperties (int originX, int originY, int verticalGap, int horizontalGap, boolean vertical) { - this.originX = originX; - this.originY = originY; - this.verticalGap = verticalGap; - this.horizontalGap = horizontalGap; + if (vertical) { + this.originX = originX; + this.originY = originY; + this.verticalGap = verticalGap; + this.horizontalGap = horizontalGap; + } else { + this.originX = originY; + this.originY = originX; + this.verticalGap = horizontalGap; + this.horizontalGap = verticalGap; + } this.vertical = vertical; + this.minimizeGap = false; + this.alignment = TreeGraphLayoutAlignment.TOP; } + /** + * Method to set the properties of this {@link TreeGraphLayout}. + * @param originX the x-axis origin + * @param originY the y-axis origin + * @param verticalGap the vertical gap between cells + * @param horizontalGap the horizontal gap between cells + * @param vertical if true, then layout organizes the graph vertically; if false, then horizontally + * @param minimizeGap if true, then minimize the gap between cells; if false do the normal tree layout. + * @param alignment alignment the alignment of the nodes in their level. Choose wheter {@code TreeGraphLayout.Alignment.TOP}, {@code TreeGraphLayout.Alignment.CENTER} or {@code TreeGraphLayout.Alignment.BOTTOM} + * @since 2.24 + */ + public void setProperties(int originX, int originY, int verticalGap, int horizontalGap, boolean vertical, boolean minimizeGap, TreeGraphLayoutAlignment alignment) { + this.setProperties(originX, originY, verticalGap, horizontalGap, vertical); + this.minimizeGap = minimizeGap; + this.alignment = alignment; + } + + @Override protected void performGraphLayout (UniversalGraph graph) { if (rootNode == null) return; @@ -91,12 +157,28 @@ HashSet loadedSet = new HashSet (); Node root = new Node (graph, rootNode, loadedSet); nodesToResolve.removeAll (loadedSet); - if (vertical) { - root.allocateHorizontally (graph); - root.resolveVertically (originX, originY); + + Map map = root.getMaxSpaceForEveryLevel(graph); + + List envelope = root.layout(originX, originY, map, 0); + + // correction of originX, if needed + int moveDistance = Integer.MIN_VALUE; + // get the most left position and determine the distance to move + for (Node.LeftRight leftRight : envelope) { + if (leftRight.getLeft() < originX && originX - leftRight.getLeft() > moveDistance) { + moveDistance = originX - leftRight.getLeft(); + } + } + + if (moveDistance == Integer.MIN_VALUE) { + moveDistance = 0; + } + + if (!vertical) { + root.invert(moveDistance); } else { - root.allocateVertically (graph); - root.resolveHorizontally (originX, originY); + root.relativeBoundsCorrectionX(moveDistance); } final HashMap resultPosition = new HashMap (); @@ -112,6 +194,7 @@ setResolvedNodeLocation (graph, entry.getKey (), entry.getValue ()); } + @Override protected void performNodesLayout (UniversalGraph universalGraph, Collection nodes) { throw new UnsupportedOperationException (); // TODO } @@ -123,7 +206,6 @@ private Rectangle relativeBounds; private int space; - private int totalSpace; private Point point; private Node (UniversalGraph graph, N node, HashSet loadedSet) { @@ -138,51 +220,317 @@ } } - private int allocateHorizontally (UniversalGraph graph) { - Widget widget = graph.getScene ().findWidget (node); - widget.getLayout ().layout (widget); - relativeBounds = widget.getPreferredBounds (); - space = 0; - for (int i = 0; i < children.size (); i++) { - if (i > 0) - space += horizontalGap; - space += children.get (i).allocateHorizontally (graph); + /** + * This class represents an element of the envelope of a (sub)tree. + * @since 2.24 + */ + private class LeftRight { + + /** the most left coordinate of this element of the envelope. */ + private int left; + /** hte most right coordinate of this element of the envelope. */ + private int right; + + /** + * Constructor + * + * @param left the most left coordinate of this {@link LeftRight}. + * @param right the most right coordinate of this {@link LeftRight}. + * @since 2.24 + */ + public LeftRight(int left, int right) { + this.left = left; + this.right = right; } - totalSpace = Math.max (space, relativeBounds.width); - return totalSpace; - } - private void resolveVertically (int x, int y) { - point = new Point (x + totalSpace / 2, y - relativeBounds.y); - x += (totalSpace - space) / 2; - y += relativeBounds.height + verticalGap; - for (Node child : children) { - child.resolveVertically (x, y); - x += child.totalSpace + horizontalGap; + /** + * Getter of the most left coordinate of this {@link LeftRight}. + * @return the most left coordinate of this {@link LeftRight}. + * @since 2.24 + */ + public int getLeft() { + return left; + } + + /** + * Getter of the most right coordinate of this {@link LeftRight}. + * @return the most right coordinate of this {@link LeftRight}. + * @since 2.24 + */ + public int getRight() { + return right; + } + + /** + * Setter of the most left coordinate of this {@link LeftRight}. + * @param left the most left coordinate to set. + * @since 2.24 + */ + public void setLeft(int left) { + this.left = left; + } + + /** + * Setter of the most right coordinate of this {@link LeftRight}. + * @param left the most right coordinate to set. + * @since 2.24 + */ + public void setRight(int right) { + this.right = right; } } - private int allocateVertically (UniversalGraph graph) { - Widget widget = graph.getScene ().findWidget (node); - widget.getLayout ().layout (widget); - relativeBounds = widget.getPreferredBounds (); - space = 0; - for (int i = 0; i < children.size (); i++) { - if (i > 0) - space += verticalGap; - space += children.get (i).allocateVertically (graph); - } - totalSpace = Math.max (space, relativeBounds.height); - return totalSpace; + /** + * This method calculates the maximal space for every level and returns the result as a {@link Map} containing the level (which is equal to the depth of the tree) and the height/width of the biggest node in this level. + * @param graph + * @return the result as a {@link Map} containing the level (which is equal to the depth of the tree) and the height/width of the biggest node in this level. + * @since 2.24 + */ + private Map getMaxSpaceForEveryLevel(UniversalGraph graph) { + Map map = new HashMap(); + calculateMaxSpace(graph, map, 0); + return map; } - private void resolveHorizontally (int x, int y) { - point = new Point (x - relativeBounds.x, y + totalSpace / 2); - x += relativeBounds.width + horizontalGap; - y += (totalSpace - space) / 2; - for (Node child : children) { - child.resolveHorizontally (x, y); - y += child.totalSpace + verticalGap; + /** + * Method to determine the maximal space (horizontaly or verticaly) of each level in the tree. + * @param map the {@link Map} to which the maximal space for each level should be stored. + * @param lvl the level to analyse. + * @return the result of the maximal space calculation for the given level and the sublevels. + * @since 2.24 + */ + private Map calculateMaxSpace(UniversalGraph graph, Map map, int lvl) { + + Widget widget = graph.getScene().findWidget(node); + widget.getLayout().layout(widget); + relativeBounds = widget.getPreferredBounds(); + + if (vertical) { + space = relativeBounds.height; + } else { + space = relativeBounds.width; + } + + if (map.get(lvl) != null) { + // lvl is in list, but height is greater than the old one. + if (map.get(lvl) < space) { + map.put(lvl, space); + } + } else { + // lvl isn't in the map right now. + map.put(lvl, space); + } + + lvl++; + + // do iteration over all children of the current node and calculate + // the maxSpace + for (Node n : children) { + n.calculateMaxSpace(graph, map, lvl); + } + return map; + } + + /** + * Method which is doing the layout based on the given x - position, y - position, map of levels and their maximal used space and the actual level. + * @param x the x position for doing the layout. + * @param y the y position for doing the layout. + * @param lvl the auctual level for doing the layout. + * @return the envelope of the subtrees for which the layout is already done. + * @since 2.24 + */ + private List layout(int x, int y, Map map, int lvl) { + + List leftright = null; + + // if this node is a leaf (has no childs) ==> place the node at x,y and x and x + width to the envelope. + if (children.size() == 0) { + leftright = new ArrayList(); + + // do the horizontal alignment + y = doHorizontalPlacementInLevel(y, map, lvl); + + if (vertical) { + y = y - relativeBounds.y; + leftright.add(new LeftRight(x, x + relativeBounds.width)); + } else { + y = y - relativeBounds.x; + leftright.add(new LeftRight(x, x + relativeBounds.height)); + } + + point = new Point(x, y); + return leftright; + } + + lvl++; + + for (int i = 0; i < children.size(); i++) { + if (i == 0) { + leftright = children.get(i).layout(x, (int) (y + map.get(lvl - 1) + verticalGap), map, lvl); + } else { + List secound = children.get(i).layout(x, (int) (y + map.get(lvl - 1) + verticalGap), map, lvl); + + int leftlength = leftright.size(); + int rightlength = secound.size(); + + int diff = rightlength - leftlength; + + // Variable used for calculation of the distance to move the right subtree. + int moveDist = Integer.MIN_VALUE; + + // Try to minimize the gap and move subtrees as close as possible to each other. + if (minimizeGap) { + // Caluculate the max distance to move + for (int k = leftlength - 1; k >= 0; k--) { + if (k + diff >= 0 && k >= 0) { + int tmpmaxoverlap = leftright.get(k).right - secound.get(k + diff).left; + if (tmpmaxoverlap > moveDist) { + moveDist = tmpmaxoverlap; + } + } + } + } else { + int maxRight = Integer.MIN_VALUE; + + for (LeftRight l : leftright) { + maxRight = Math.max(maxRight, l.right); + } + + int minLeft = Integer.MAX_VALUE; + + for (LeftRight s : secound) { + minLeft = Math.min(minLeft, s.left); + } + moveDist = maxRight - minLeft; + } + + if (moveDist > Integer.MIN_VALUE) { + int dx = moveDist + horizontalGap; + children.get(i).point.x += dx; + children.get(i).moveChildrenHorizontally(dx); + } + + // update moved tree envelope + for (LeftRight lr : secound) { + lr.setLeft(lr.getLeft() + moveDist + horizontalGap); + lr.setRight(lr.getRight() + moveDist + horizontalGap); + } + + // store the overall envelope of leftright and secound in leftright + for (int j = rightlength - 1; j >= 0; j--) { + // leftright wasn't as long as secound, so these elements have to be added to the envelope + if (j < secound.size() - leftright.size()) { + leftright.add(0, secound.get(j)); + } else if ((j - diff) >= 0 && j >= 0) { + // if the left envelope of secound is smaller than the left envelope of leftright, might never happen. + if (leftright.get(j - diff).left > secound.get(j).left) { + leftright.get(j - diff).setLeft(secound.get(j).left); + } + // if the right envelope of secound is greater than the right envelope of leftright, might happen every time when a subtree was added by a move to the right. + if (leftright.get(j - diff).right < secound.get(j).right) { + leftright.get(j - diff).setRight(secound.get(j).right); + } + } + } + } + } + + lvl--; + + // do the horizontal alignment + int yAlignment = doHorizontalPlacementInLevel(y, map, lvl); + + if (minimizeGap) { + if (vertical) { + point = new Point(((leftright.get(leftright.size() - 1).right + leftright.get(leftright.size() - 1).left) / 2) - (relativeBounds.width / 2), yAlignment - relativeBounds.y); + leftright.add(new LeftRight(point.x, point.x + relativeBounds.width)); + } else { + point = new Point(((leftright.get(leftright.size() - 1).right + leftright.get(leftright.size() - 1).left) / 2) - (relativeBounds.height / 2), yAlignment - relativeBounds.x); + leftright.add(new LeftRight(point.x, point.x + relativeBounds.height)); + } + } else { + int leftMin = Integer.MAX_VALUE; + int rightMax = Integer.MIN_VALUE; + for (LeftRight l : leftright) { + leftMin = Math.min(leftMin, l.left); + rightMax = Math.max(rightMax, l.right); + } + + assert leftMin != Integer.MAX_VALUE : "whether envelope was empty or it had no valid leftMin value!"; + assert rightMax != Integer.MIN_VALUE : "whether envelope was empty or it had no vaild rightMin value!"; + + if (vertical) { + point = new Point(((leftMin + rightMax) / 2) - (relativeBounds.width / 2), yAlignment - relativeBounds.y); + leftright.add(new LeftRight(point.x, point.x + relativeBounds.width)); + } else { + point = new Point(((leftMin + rightMax) / 2) - (relativeBounds.height / 2), yAlignment - relativeBounds.x); + leftright.add(new LeftRight(point.x, point.x + relativeBounds.height)); + } + } + + return leftright; + } + + /** + * Method to move the children of the actual {@link Node} horizontally + * by the given distance dx. + * @param dx the distance to move the children horizontally. + * @since 2.24 + */ + private void moveChildrenHorizontally(int dx) { + for (Node n : children) { + n.point.x += dx; + n.moveChildrenHorizontally(dx); + } + } + + /** + * Doing the alignment whether on TOP, in the CENTER or at the BOTTOM of the level. + * @param y the top y coordinate of the level. + * @param map the map of levels and their maximal spaces. + * @param lvl the level for which the alignment should be done. + * @return the y coordinate of the aligned node. + * @since 2.24 + */ + private int doHorizontalPlacementInLevel(int y, Map map, int lvl) { + int yAlignment = 0; + + // do the alignment + if (alignment == TreeGraphLayoutAlignment.TOP) { + // nothing to do + yAlignment = y; + } else if (alignment == TreeGraphLayoutAlignment.CENTER) { + // place the widget in the center of the max. + yAlignment = y + ((map.get(lvl) - space) / 2); + } else if (alignment == TreeGraphLayoutAlignment.BOTTOM) { + yAlignment = y + (map.get(lvl) - space); + } + return yAlignment; + } + + /** + * Method to invert x and y. Also relativeBounds correction is done and a moveDistance (x-axis) can be given. + * @param moveDistance the distance to move the subtree (x-axis). + * @since 2.24 + */ + private void invert(int moveDistance) { + int tmpx = point.x + moveDistance; + point.x = point.y; + point.y = tmpx - relativeBounds.y; + for (Node n : children) { + n.invert(moveDistance); + } + } + + /** + * Method to do a relativeBoundsCorrection (x-axis) and also to move the Node for a given distance (also x-axis). + * @param moveDistance the distance to move the subtree (x-axis). + * @since 2.24 + */ + private void relativeBoundsCorrectionX(int moveDistance) { + point.x = point.x - relativeBounds.x + moveDistance; + for (Node n : children) { + n.relativeBoundsCorrectionX(moveDistance); } }