add org.abego.treelayout

[git-p4: depot-paths = "//depot/code/antlr4/main/": change = 9044]
This commit is contained in:
Udo Borkowski 2011-09-05 10:50:57 -08:00
parent 528d6df1e3
commit f93bc7aa7a
51 changed files with 2375 additions and 0 deletions

View File

@ -0,0 +1,169 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.abego.treelayout;
/**
* Used to configure the tree layout algorithm.
*
* @author Udo Borkowski (ub@abego.org)
*
* @param <TreeNode>
*/
public interface Configuration<TreeNode> {
// ------------------------------------------------------------------------
// rootLocation
/**
* Identifies the sides of a rectangle (top, left, ...)
*/
public enum Location {
Top, Left, Bottom, Right
}
// ------------------------------------------------------------------------
// alignmentInLevel
/**
* Returns the position of the root node in the diagram.
* <p>
* By default the root of the tree is located at the top of the diagram.
* However one may also put it at the left, right or bottom of the diagram.
* <p>
* <table border="1">
* <tr>
* <th>Top (Default)</th>
* <th>Left</th>
* <th>Right</th>
* <th>Bottom</th>
* </tr>
* <tr>
* <td style="padding:10px;"><img src="doc-files/TreeGraphView-Top.png"></td>
* <td style="padding:10px;"><img src="doc-files/TreeGraphView-Left.png"></td>
* <td style="padding:10px;"><img src="doc-files/TreeGraphView-Right.png"></td>
* <td style="padding:10px;"><img src="doc-files/TreeGraphView-Bottom.png"></td>
* </tr>
* </table>
*
* @return the position of the root node in the diagram
*/
Location getRootLocation();
/**
* Possible alignments of a node within a level (centered, towards or away
* from root)
*/
public enum AlignmentInLevel {
Center, TowardsRoot, AwayFromRoot
}
/**
* Returns the alignment of "smaller" nodes within a level.
* <p>
* By default all nodes of one level are centered in the level. However one
* may also align them "towards the root" or "away from the root". When the
* root is located at the top this means the nodes are aligned "to the top
* of the level" or "to the bottom of the level".
* <p>
* <table border="1">
* <tr>
* <th>Center (Default)</th>
* <th>TowardsRoot ("top of level")</th>
* <th>AwayFromRoot ("bottom of level")</th>
* </tr>
* <tr>
* <td style="padding:10px;"><img src="doc-files/TreeGraphView-Center.png"></td>
* <td style="padding:10px;"><img
* src="doc-files/TreeGraphView-TowardsRoot.png"></td>
* <td style="padding:10px;"><img
* src="doc-files/TreeGraphView-AwayFromRoot.png"></td>
* </tr>
* </table>
* <p>
* Alignment in level when root is at the left:
* </p>
* <table border="1">
* <tr>
* <th>Center (Default)</th>
* <th>TowardsRoot ("left of level")</th>
* <th>AwayFromRoot<br>
* ("right of level")</th>
* </tr>
* <tr>
* <td style="padding:10px;"><img
* src="doc-files/TreeGraphView-Center-RootLeft.png"></td>
* <td style="padding:10px;"><img
* src="doc-files/TreeGraphView-TowardsRoot-RootLeft.png"></td>
* <td style="padding:10px;"><img
* src="doc-files/TreeGraphView-AwayFromRoot-RootLeft.png"></td>
* </tr>
* </table>
*
* <p>
* Of cause the alignment also works when the root is at the bottom or at
* the right side.
* </p>
*
* @return the alignment of "smaller" nodes within a level
*/
AlignmentInLevel getAlignmentInLevel();
// ------------------------------------------------------------------------
// gapBetweenLevels/Nodes
/**
* Returns the size of the gap between subsequent levels.
* <p>
* <img src="doc-files/gapBetweenLevels.png">
*
* @param nextLevel
* [nextLevel > 0]
*
* @return the size of the gap between level (nextLevel-1) and nextLevel
* [result >= 0]
*/
double getGapBetweenLevels(int nextLevel);
/**
* Returns the size of the minimal gap of nodes within a level.
* <p>
* In the layout there will be a gap of at least the returned size between
* both given nodes.
* <p>
* <img src="doc-files/gapBetweenNodes.png">
* <p>
* node1 and node2 are at the same level and are placed next to each other.
*
* @param node1
* @param node2
* @return the minimal size of the gap between node1 and node2 [result >= 0]
*/
double getGapBetweenNodes(TreeNode node1, TreeNode node2);
}

View File

@ -0,0 +1,55 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.abego.treelayout;
/**
* Provides the extent (width and height) of a tree node.
*
* @author Udo Borkowski (ub@abego.org)
*
* @param <TreeNode>
*/
public interface NodeExtentProvider<TreeNode> {
/**
* Returns the width of the given treeNode.
*
* @param treeNode
* @return [result >= 0]
*/
double getWidth(TreeNode treeNode);
/**
* Returns the height of the given treeNode.
*
* @param treeNode
* @return [result >= 0]
*/
double getHeight(TreeNode treeNode);
}

View File

@ -0,0 +1,123 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.abego.treelayout;
/**
* Represents a tree to be used by the {@link TreeLayout}.
* <p>
* The TreeForTreeLayout interface is designed to best match the implemented
* layout algorithm and to ensure the algorithm's time complexity promises in
* all possible cases. However in most situation a client must not deal with all
* details of this interface and can directly use the
* {@link org.abego.treelayout.util.AbstractTreeForTreeLayout} to implement this
* interface or even use the
* {@link org.abego.treelayout.util.DefaultTreeForTreeLayout} class directly.
*
* @author Udo Borkowski (ub@abego.org)
*
* @param <TreeNode>
*/
public interface TreeForTreeLayout<TreeNode> {
/**
* Returns the the root of the tree.
* <p>
* Time Complexity: O(1)
*
* @return the root of the tree
*/
TreeNode getRoot();
/**
* Tells if a node is a leaf in the tree.
* <p>
* Time Complexity: O(1)
*
* @param node
* @return true iff node is a leaf in the tree, i.e. has no children.
*/
boolean isLeaf(TreeNode node);
/**
* Tells if a node is a child of a given parentNode.
* <p>
* Time Complexity: O(1)
*
* @param node
* @param parentNode
* @return true iff the node is a child of the given parentNode
*/
boolean isChildOfParent(TreeNode node, TreeNode parentNode);
/**
* Returns the children of a parent node.
* <p>
* Time Complexity: O(1)
*
* @param parentNode
* [!isLeaf(parentNode)]
* @return the children of the given parentNode, from first to last
*/
Iterable<TreeNode> getChildren(TreeNode parentNode);
/**
* Returns the children of a parent node, in reverse order.
* <p>
* Time Complexity: O(1)
*
* @param parentNode
* [!isLeaf(parentNode)]
* @return the children of given parentNode, from last to first
*/
Iterable<TreeNode> getChildrenReverse(TreeNode parentNode);
/**
* Returns the first child of a parent node.
* <p>
* Time Complexity: O(1)
*
* @param parentNode
* [!isLeaf(parentNode)]
* @return the first child of the parentNode
*/
TreeNode getFirstChild(TreeNode parentNode);
/**
* Returns the last child of a parent node.
* <p>
*
* Time Complexity: O(1)
*
* @param parentNode
* [!isLeaf(parentNode)]
* @return the last child of the parentNode
*/
TreeNode getLastChild(TreeNode parentNode);
}

View File

@ -0,0 +1,732 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.abego.treelayout;
import static org.abego.treelayout.internal.util.Contract.checkArg;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.abego.treelayout.Configuration.AlignmentInLevel;
import org.abego.treelayout.Configuration.Location;
/**
* Implements the actual tree layout algorithm.
* <p>
* The nodes with their final layout can be retrieved through
* {@link #getNodeBounds()}.
* <p>
* See <a href="package-summary.html">this summary</a> to get an overview how to
* use TreeLayout.
*
*
* @author Udo Borkowski (ub@abego.org)
*
* @param <TreeNode>
*/
public class TreeLayout<TreeNode> {
/*
* Differences between this implementation and original algorithm
* --------------------------------------------------------------
*
* For easier reference the same names (or at least similar names) as in the
* paper of Buchheim, J&uuml;nger, and Leipert are used in this
* implementation. However in the external interface "first" and "last" are
* used instead of "left most" and "right most". The implementation also
* supports tree layouts with the root at the left (or right) side. In that
* case using "left most" would refer to the "top" child, i.e. using "first"
* is less confusing.
*
* Also the y coordinate is not the level but directly refers the y
* coordinate of a level, taking node's height and gapBetweenLevels into
* account. When the root is at the left or right side the y coordinate
* actually becomes an x coordinate.
*
* Instead of just using a constant "distance" to calculate the position to
* the next node we refer to the "size" (width or height) of the node and a
* "gapBetweenNodes".
*/
// ------------------------------------------------------------------------
// tree
private final TreeForTreeLayout<TreeNode> tree;
/**
* Returns the Tree the layout is created for.
*/
public TreeForTreeLayout<TreeNode> getTree() {
return tree;
}
// ------------------------------------------------------------------------
// nodeExtentProvider
private final NodeExtentProvider<TreeNode> nodeExtentProvider;
/**
* Returns the {@link NodeExtentProvider} used by this {@link TreeLayout}.
*/
public NodeExtentProvider<TreeNode> getNodeExtentProvider() {
return nodeExtentProvider;
}
private double getNodeHeight(TreeNode node) {
return nodeExtentProvider.getHeight(node);
}
private double getNodeWidth(TreeNode node) {
return nodeExtentProvider.getWidth(node);
}
private double getWidthOrHeightOfNode(TreeNode treeNode, boolean returnWidth) {
return returnWidth ? getNodeWidth(treeNode) : getNodeHeight(treeNode);
}
/**
* When the level changes in Y-axis (i.e. root location Top or Bottom) the
* height of a node is its thickness, otherwise the node's width is its
* thickness.
* <p>
* The thickness of a node is used when calculating the locations of the
* levels.
*
* @param treeNode
* @return
*/
private double getNodeThickness(TreeNode treeNode) {
return getWidthOrHeightOfNode(treeNode, !isLevelChangeInYAxis());
}
/**
* When the level changes in Y-axis (i.e. root location Top or Bottom) the
* width of a node is its size, otherwise the node's height is its size.
* <p>
* The size of a node is used when calculating the distance between two
* nodes.
*
* @param treeNode
* @return
*/
private double getNodeSize(TreeNode treeNode) {
return getWidthOrHeightOfNode(treeNode, isLevelChangeInYAxis());
}
// ------------------------------------------------------------------------
// configuration
private final Configuration<TreeNode> configuration;
/**
* Returns the Configuration used by this {@link TreeLayout}.
*/
public Configuration<TreeNode> getConfiguration() {
return configuration;
}
private boolean isLevelChangeInYAxis() {
Location rootLocation = configuration.getRootLocation();
return rootLocation == Location.Top || rootLocation == Location.Bottom;
}
private int getLevelChangeSign() {
Location rootLocation = configuration.getRootLocation();
return rootLocation == Location.Bottom
|| rootLocation == Location.Right ? -1 : 1;
}
// ------------------------------------------------------------------------
// bounds
private double boundsLeft = Double.MAX_VALUE;
private double boundsRight = Double.MIN_VALUE;
private double boundsTop = Double.MAX_VALUE;
private double boundsBottom = Double.MIN_VALUE;
private void updateBounds(TreeNode node, double centerX, double centerY) {
double width = getNodeWidth(node);
double height = getNodeHeight(node);
double left = centerX - width / 2;
double right = centerX + width / 2;
double top = centerY - height / 2;
double bottom = centerY + height / 2;
if (boundsLeft > left) {
boundsLeft = left;
}
if (boundsRight < right) {
boundsRight = right;
}
if (boundsTop > top) {
boundsTop = top;
}
if (boundsBottom < bottom) {
boundsBottom = bottom;
}
}
/**
* Returns the bounds of the tree layout.
* <p>
* The bounds of a TreeLayout is the smallest rectangle containing the
* bounds of all nodes in the layout. It always starts at (0,0).
*
* @return the bounds of the tree layout
*/
public Rectangle2D getBounds() {
return new Rectangle2D.Double(0, 0, boundsRight - boundsLeft,
boundsBottom - boundsTop);
}
// ------------------------------------------------------------------------
// size of level
private final List<Double> sizeOfLevel = new ArrayList<Double>();
private void calcSizeOfLevels(TreeNode node, int level) {
double oldSize;
if (sizeOfLevel.size() <= level) {
sizeOfLevel.add(Double.valueOf(0));
oldSize = 0;
} else {
oldSize = sizeOfLevel.get(level);
}
double size = getNodeThickness(node);
// size = nodeExtentProvider.getHeight(node);
if (oldSize < size) {
sizeOfLevel.set(level, size);
}
if (!tree.isLeaf(node)) {
for (TreeNode child : tree.getChildren(node)) {
calcSizeOfLevels(child, level + 1);
}
}
}
/**
* Returns the number of levels of the tree.
*
* @return [level > 0]
*/
public int getLevelCount() {
return sizeOfLevel.size();
}
/**
* Returns the size of a level.
* <p>
* When the root is located at the top or bottom the size of a level is the
* maximal height of the nodes of that level. When the root is located at
* the left or right the size of a level is the maximal width of the nodes
* of that level.
*
* @param level
* @return the size of the level [level >= 0 && level < levelCount]
*/
public double getSizeOfLevel(int level) {
checkArg(level >= 0, "level must be >= 0");
checkArg(level < getLevelCount(), "level must be < levelCount");
return sizeOfLevel.get(level);
}
// ------------------------------------------------------------------------
// NormalizedPosition
/**
* The algorithm calculates the position starting with the root at 0. I.e.
* the left children will get negative positions. However we want the result
* to be normalized to (0,0).
* <p>
* {@link NormalizedPosition} will normalize the position (given relative to
* the root position), taking the current bounds into account. This way the
* left most node bounds will start at x = 0, the top most node bounds at y
* = 0.
*/
private class NormalizedPosition extends Point2D {
private double x_relativeToRoot;
private double y_relativeToRoot;
public NormalizedPosition(double x_relativeToRoot,
double y_relativeToRoot) {
setLocation(x_relativeToRoot, y_relativeToRoot);
}
@Override
public double getX() {
return x_relativeToRoot - boundsLeft;
}
@Override
public double getY() {
return y_relativeToRoot - boundsTop;
}
@Override
// never called from outside
public void setLocation(double x_relativeToRoot, double y_relativeToRoot) {
this.x_relativeToRoot = x_relativeToRoot;
this.y_relativeToRoot = y_relativeToRoot;
}
}
// ------------------------------------------------------------------------
// The Algorithm
private final Map<TreeNode, Double> mod = new HashMap<TreeNode, Double>();
private final Map<TreeNode, TreeNode> thread = new HashMap<TreeNode, TreeNode>();
private final Map<TreeNode, Double> prelim = new HashMap<TreeNode, Double>();
private final Map<TreeNode, Double> change = new HashMap<TreeNode, Double>();
private final Map<TreeNode, Double> shift = new HashMap<TreeNode, Double>();
private final Map<TreeNode, TreeNode> ancestor = new HashMap<TreeNode, TreeNode>();
private final Map<TreeNode, Integer> number = new HashMap<TreeNode, Integer>();
private final Map<TreeNode, Point2D> positions = new HashMap<TreeNode, Point2D>();
private double getMod(TreeNode node) {
Double d = mod.get(node);
return d != null ? d.doubleValue() : 0;
}
private void setMod(TreeNode node, double d) {
mod.put(node, d);
}
private TreeNode getThread(TreeNode node) {
TreeNode n = thread.get(node);
return n != null ? n : null;
}
private void setThread(TreeNode node, TreeNode thread) {
this.thread.put(node, thread);
}
private TreeNode getAncestor(TreeNode node) {
TreeNode n = ancestor.get(node);
return n != null ? n : node;
}
private void setAncestor(TreeNode node, TreeNode ancestor) {
this.ancestor.put(node, ancestor);
}
private double getPrelim(TreeNode node) {
Double d = prelim.get(node);
return d != null ? d.doubleValue() : 0;
}
private void setPrelim(TreeNode node, double d) {
prelim.put(node, d);
}
private double getChange(TreeNode node) {
Double d = change.get(node);
return d != null ? d.doubleValue() : 0;
}
private void setChange(TreeNode node, double d) {
change.put(node, d);
}
private double getShift(TreeNode node) {
Double d = shift.get(node);
return d != null ? d.doubleValue() : 0;
}
private void setShift(TreeNode node, double d) {
shift.put(node, d);
}
/**
* The distance of two nodes is the distance of the centers of both noded.
* <p>
* I.e. the distance includes the gap between the nodes and half of the
* sizes of the nodes.
*
* @param v
* @param w
* @return the distance between node v and w
*/
private double getDistance(TreeNode v, TreeNode w) {
double sizeOfNodes = getNodeSize(v) + getNodeSize(w);
double distance = sizeOfNodes / 2
+ configuration.getGapBetweenNodes(v, w);
return distance;
}
private TreeNode nextLeft(TreeNode v) {
return tree.isLeaf(v) ? getThread(v) : tree.getFirstChild(v);
}
private TreeNode nextRight(TreeNode v) {
return tree.isLeaf(v) ? getThread(v) : tree.getLastChild(v);
}
/**
*
* @param node
* [tree.isChildOfParent(node, parentNode)]
* @param parentNode
* parent of node
* @return
*/
private int getNumber(TreeNode node, TreeNode parentNode) {
Integer n = number.get(node);
if (n == null) {
int i = 1;
for (TreeNode child : tree.getChildren(parentNode)) {
number.put(child, i++);
}
n = number.get(node);
}
return n.intValue();
}
/**
*
* @param vIMinus
* @param v
* @param parentOfV
* @param defaultAncestor
* @return the greatest distinct ancestor of vIMinus and its right neighbor
* v
*/
private TreeNode ancestor(TreeNode vIMinus, TreeNode v, TreeNode parentOfV,
TreeNode defaultAncestor) {
TreeNode ancestor = getAncestor(vIMinus);
// when the ancestor of vIMinus is a sibling of v (i.e. has the same
// parent as v) it is also the greatest distinct ancestor vIMinus and
// v. Otherwise it is the defaultAncestor
return tree.isChildOfParent(ancestor, parentOfV) ? ancestor
: defaultAncestor;
}
private void moveSubtree(TreeNode wMinus, TreeNode wPlus, TreeNode parent,
double shift) {
int subtrees = getNumber(wPlus, parent) - getNumber(wMinus, parent);
setChange(wPlus, getChange(wPlus) - shift / subtrees);
setShift(wPlus, getShift(wPlus) + shift);
setChange(wMinus, getChange(wMinus) + shift / subtrees);
setPrelim(wPlus, getPrelim(wPlus) + shift);
setMod(wPlus, getMod(wPlus) + shift);
}
/**
* In difference to the original algorithm we also pass in the leftSibling
* and the parent of v.
* <p>
* <b>Why adding the parameter 'parent of v' (parentOfV) ?</b>
* <p>
* In this method we need access to the parent of v. Not every tree
* implementation may support efficient (i.e. constant time) access to it.
* On the other hand the (only) caller of this method can provide this
* information with only constant extra time.
* <p>
* Also we need access to the "left most sibling" of v. Not every tree
* implementation may support efficient (i.e. constant time) access to it.
* On the other hand the "left most sibling" of v is also the "first child"
* of the parent of v. The first child of a parent node we can get in
* constant time. As we got the parent of v we can so also get the
* "left most sibling" of v in constant time.
* <p>
* <b>Why adding the parameter 'leftSibling' ?</b>
* <p>
* In this method we need access to the "left sibling" of v. Not every tree
* implementation may support efficient (i.e. constant time) access to it.
* However it is easy for the caller of this method to provide this
* information with only constant extra time.
* <p>
* <p>
* <p>
* In addition these extra parameters avoid the need for
* {@link TreeForTreeLayout} to include extra methods "getParent",
* "getLeftSibling", or "getLeftMostSibling". This keeps the interface
* {@link TreeForTreeLayout} small and avoids redundant implementations.
*
* @param v
* @param defaultAncestor
* @param leftSibling
* [nullable] the left sibling v, if there is any
* @param parentOfV
* the parent of v
* @return the (possibly changes) defaultAncestor
*/
private TreeNode apportion(TreeNode v, TreeNode defaultAncestor,
TreeNode leftSibling, TreeNode parentOfV) {
TreeNode w = leftSibling;
if (w == null) {
// v has no left sibling
return defaultAncestor;
}
// v has left sibling w
// The following variables "v..." are used to traverse the contours to
// the subtrees. "Minus" refers to the left, "Plus" to the right
// subtree. "I" refers to the "inside" and "O" to the outside contour.
TreeNode vOPlus = v;
TreeNode vIPlus = v;
TreeNode vIMinus = w;
// get leftmost sibling of vIPlus, i.e. get the leftmost sibling of
// v, i.e. the leftmost child of the parent of v (which is passed
// in)
TreeNode vOMinus = tree.getFirstChild(parentOfV);
Double sIPlus = getMod(vIPlus);
Double sOPlus = getMod(vOPlus);
Double sIMinus = getMod(vIMinus);
Double sOMinus = getMod(vOMinus);
TreeNode nextRightVIMinus = nextRight(vIMinus);
TreeNode nextLeftVIPlus = nextLeft(vIPlus);
while (nextRightVIMinus != null && nextLeftVIPlus != null) {
vIMinus = nextRightVIMinus;
vIPlus = nextLeftVIPlus;
vOMinus = nextLeft(vOMinus);
vOPlus = nextRight(vOPlus);
setAncestor(vOPlus, v);
double shift = (getPrelim(vIMinus) + sIMinus)
- (getPrelim(vIPlus) + sIPlus)
+ getDistance(vIMinus, vIPlus);
if (shift > 0) {
moveSubtree(ancestor(vIMinus, v, parentOfV, defaultAncestor),
v, parentOfV, shift);
sIPlus = sIPlus + shift;
sOPlus = sOPlus + shift;
}
sIMinus = sIMinus + getMod(vIMinus);
sIPlus = sIPlus + getMod(vIPlus);
sOMinus = sOMinus + getMod(vOMinus);
sOPlus = sOPlus + getMod(vOPlus);
nextRightVIMinus = nextRight(vIMinus);
nextLeftVIPlus = nextLeft(vIPlus);
}
if (nextRightVIMinus != null && nextRight(vOPlus) == null) {
setThread(vOPlus, nextRightVIMinus);
setMod(vOPlus, getMod(vOPlus) + sIMinus - sOPlus);
}
if (nextLeftVIPlus != null && nextLeft(vOMinus) == null) {
setThread(vOMinus, nextLeftVIPlus);
setMod(vOMinus, getMod(vOMinus) + sIPlus - sOMinus);
defaultAncestor = v;
}
return defaultAncestor;
}
/**
*
* @param v
* [!tree.isLeaf(v)]
*/
private void executeShifts(TreeNode v) {
double shift = 0;
double change = 0;
for (TreeNode w : tree.getChildrenReverse(v)) {
change = change + getChange(w);
setPrelim(w, getPrelim(w) + shift);
setMod(w, getMod(w) + shift);
shift = shift + getShift(w) + change;
}
}
/**
* In difference to the original algorithm we also pass in the leftSibling
* (see {@link #apportion(Object, Object, Object, Object)} for a
* motivation).
*
* @param v
* @param leftSibling
* [nullable] the left sibling v, if there is any
*/
private void firstWalk(TreeNode v, TreeNode leftSibling) {
if (tree.isLeaf(v)) {
// No need to set prelim(v) to 0 as the getter takes care of this.
TreeNode w = leftSibling;
if (w != null) {
// v has left sibling
setPrelim(v, getPrelim(w) + getDistance(v, w));
}
} else {
// v is not a leaf
TreeNode defaultAncestor = tree.getFirstChild(v);
TreeNode previousChild = null;
for (TreeNode w : tree.getChildren(v)) {
firstWalk(w, previousChild);
defaultAncestor = apportion(w, defaultAncestor, previousChild,
v);
previousChild = w;
}
executeShifts(v);
double midpoint = (getPrelim(tree.getFirstChild(v)) + getPrelim(tree
.getLastChild(v))) / 2.0;
TreeNode w = leftSibling;
if (w != null) {
// v has left sibling
setPrelim(v, getPrelim(w) + getDistance(v, w));
setMod(v, getPrelim(v) - midpoint);
} else {
// v has no left sibling
setPrelim(v, midpoint);
}
}
}
/**
* In difference to the original algorithm we also pass in extra level
* information.
*
* @param v
* @param m
* @param level
* @param levelStart
*/
private void secondWalk(TreeNode v, double m, int level, double levelStart) {
// construct the position from the prelim and the level information
// The rootLocation affects the way how x and y are changed and in what
// direction.
double levelChangeSign = getLevelChangeSign();
boolean levelChangeOnYAxis = isLevelChangeInYAxis();
double levelSize = getSizeOfLevel(level);
double x = getPrelim(v) + m;
double y;
AlignmentInLevel alignment = configuration.getAlignmentInLevel();
if (alignment == AlignmentInLevel.Center) {
y = levelStart + levelChangeSign * (levelSize / 2);
} else if (alignment == AlignmentInLevel.TowardsRoot) {
y = levelStart + levelChangeSign * (getNodeThickness(v) / 2);
} else {
y = levelStart + levelSize - levelChangeSign
* (getNodeThickness(v) / 2);
}
if (!levelChangeOnYAxis) {
double t = x;
x = y;
y = t;
}
positions.put(v, new NormalizedPosition(x, y));
// update the bounds
updateBounds(v, x, y);
// recurse
if (!tree.isLeaf(v)) {
double nextLevelStart = levelStart
+ (levelSize + configuration.getGapBetweenLevels(level + 1))
* levelChangeSign;
for (TreeNode w : tree.getChildren(v)) {
secondWalk(w, m + getMod(v), level + 1, nextLevelStart);
}
}
}
// ------------------------------------------------------------------------
// nodeBounds
private Map<TreeNode, Rectangle2D.Double> nodeBounds;
/**
* Returns the layout of the tree nodes by mapping each node of the tree to
* its bounds (position and size).
* <p>
* For each rectangle x and y will be >= 0. At least one rectangle will have
* an x == 0 and at least one rectangle will have an y == 0.
*
* @return maps each node of the tree to its bounds (position and size).
*/
public Map<TreeNode, Rectangle2D.Double> getNodeBounds() {
if (nodeBounds == null) {
nodeBounds = new HashMap<TreeNode, Rectangle2D.Double>();
for (Entry<TreeNode, Point2D> entry : positions.entrySet()) {
TreeNode node = entry.getKey();
Point2D pos = entry.getValue();
double w = getNodeWidth(node);
double h = getNodeHeight(node);
double x = pos.getX() - w / 2;
double y = pos.getY() - h / 2;
nodeBounds.put(node, new Rectangle2D.Double(x, y, w, h));
}
}
return nodeBounds;
}
// ------------------------------------------------------------------------
// constructor
/**
* Creates a TreeLayout for a given tree.
* <p>
* In addition to the tree the {@link NodeExtentProvider} and the
* {@link Configuration} must be given.
*/
public TreeLayout(TreeForTreeLayout<TreeNode> tree,
NodeExtentProvider<TreeNode> nodeExtentProvider,
Configuration<TreeNode> configuration) {
this.tree = tree;
this.nodeExtentProvider = nodeExtentProvider;
this.configuration = configuration;
// No need to explicitly set mod, thread and ancestor as their getters
// are taking care of the initial values. This avoids a full tree walk
// through and saves some memory as no entries are added for
// "initial values".
TreeNode r = tree.getRoot();
firstWalk(r, null);
calcSizeOfLevels(r, 0);
secondWalk(r, -getPrelim(r), 0, 0);
}
}

View File

@ -0,0 +1,27 @@
[The "BSD license"]
Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the abego Software GmbH nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,46 @@
<?xml version="1.0" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="260" height="176" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<line x1="177" y1="10" x2="110" y2="80" style="stroke:black; stroke-width:2px;" />
<line x1="110" y1="80" x2="40" y2="158" style="stroke:black; stroke-width:2px;" />
<line x1="110" y1="80" x2="110" y2="158" style="stroke:black; stroke-width:2px;" />
<line x1="110" y1="80" x2="180" y2="158" style="stroke:black; stroke-width:2px;" />
<line x1="177" y1="10" x2="245" y2="80" style="stroke:black; stroke-width:2px;" />
<line x1="245" y1="80" x2="245" y2="158" style="stroke:black; stroke-width:2px;" />
<rect x="91" y="149" width="38" height="18" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
<text x="98" y="161" style="font-family:sans-serif;font-size:12px;">
n1.2
</text>
<rect x="1" y="141" width="78" height="34" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
<text x="8" y="153" style="font-family:sans-serif;font-size:12px;">
n1.1
</text>
<text x="8" y="165" style="font-family:sans-serif;font-size:12px;">
(first node)
</text>
<rect x="231" y="71" width="28" height="18" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
<text x="238" y="83" style="font-family:sans-serif;font-size:12px;">
n2
</text>
<rect x="158" y="1" width="38" height="18" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
<text x="165" y="13" style="font-family:sans-serif;font-size:12px;">
root
</text>
<rect x="231" y="149" width="28" height="18" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
<text x="238" y="161" style="font-family:sans-serif;font-size:12px;">
n2
</text>
<rect x="96" y="71" width="28" height="18" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
<text x="103" y="83" style="font-family:sans-serif;font-size:12px;">
n1
</text>
<rect x="141" y="141" width="78" height="34" style="fill:orange; stroke:rgb(0,0,0);" rx="10"/>
<text x="148" y="153" style="font-family:sans-serif;font-size:12px;">
n1.3
</text>
<text x="148" y="165" style="font-family:sans-serif;font-size:12px;">
(last node)
</text>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,54 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.abego.treelayout.internal.util;
/**
* A set of methods to support some sort of "Design by Contract" programming.
*
* @author Udo Borkowski (ub@abego.org)
*
*
*/
public class Contract {
public static void checkArg(boolean isOK, String s) {
if (!isOK) {
throw new IllegalArgumentException(s);
}
}
public static void checkState(boolean isOK, String s) {
if (!isOK) {
throw new IllegalStateException(s);
}
}
}

View File

@ -0,0 +1,77 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.abego.treelayout.internal.util.java.lang;
import java.util.Iterator;
import java.util.List;
import org.abego.treelayout.internal.util.java.util.IteratorUtil;
/**
* Util (general purpose) methods dealing with {@link Iterable}.
*
* @author Udo Borkowski (ub@abego.org)
*
*
*/
public class IterableUtil {
private static class ReverseIterable<T> implements Iterable<T> {
private List<T> list;
public ReverseIterable(List<T> list) {
this.list = list;
}
@Override
public Iterator<T> iterator() {
return IteratorUtil.createReverseIterator(list);
}
};
/**
* Returns an {@link Iterable} with an iterator iterating the given list
* from the end to the start.
* <p>
* I.e. the iterator does the reverse of the {@link List#iterator()}.
*
* @param <T>
* @param list
* @return a reverse {@link Iterable} of the list
*/
public static <T> Iterable<T> createReverseIterable(List<T> list) {
// When the list is empty we can use the "forward" iterable (i.e. the
// list itself) also as the reverseIterable as it will do nothing.
if (list.size() == 0) {
return list;
}
return new ReverseIterable<T>(list);
}
}

View File

@ -0,0 +1,39 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Internally used classes - subject to change without notice; client code must
* not use these classes.
*
* @author Udo Borkowski (ub@abego.org)
*
*/
package org.abego.treelayout.internal.util.java.lang;

View File

@ -0,0 +1,81 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.abego.treelayout.internal.util.java.util;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* Util (general purpose) methods dealing with {@link Iterator}.
*
* @author Udo Borkowski (ub@abego.org)
*
*
*/
public class IteratorUtil {
private static class ReverseIterator<T> implements Iterator<T> {
private ListIterator<T> listIterator;
public ReverseIterator(List<T> list) {
this.listIterator = list.listIterator(list.size());
}
@Override
public boolean hasNext() {
return listIterator.hasPrevious();
}
@Override
public T next() {
return listIterator.previous();
}
@Override
public void remove() {
listIterator.remove();
}
}
/**
* Returns an {@link Iterator} iterating the given list from the end to the
* start.
* <p>
* I.e. the iterator does the reverse of the {@link List#iterator()}.
*
* @param <T>
* @param list
* @return a reverse {@link Iterator} of the list
*/
public static <T> Iterator<T> createReverseIterator(List<T> list) {
return new ReverseIterator<T>(list);
}
}

View File

@ -0,0 +1,53 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.abego.treelayout.internal.util.java.util;
import java.util.List;
/**
* Util (general purpose) methods dealing with {@link List}.
*
* @author Udo Borkowski (ub@abego.org)
*
*
*/
public class ListUtil {
/**
*
* @param <T>
* @param list [!list.isEmpty()]
* @return the last element of the list
*/
public static <T> T getLast(List<T> list) {
return list.get(list.size() - 1);
}
}

View File

@ -0,0 +1,39 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Internally used classes - subject to change without notice; client code must
* not use these classes.
*
* @author Udo Borkowski (ub@abego.org)
*
*
*/
package org.abego.treelayout.internal.util.java.util;

View File

@ -0,0 +1,39 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Internally used classes - subject to change without notice; client code must
* not use these classes.
*
* @author Udo Borkowski (ub@abego.org)
*
*
*/
package org.abego.treelayout.internal.util;

View File

@ -0,0 +1,336 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
Efficiently create compact, highly customizable tree layouts.
<h2>Introduction</h2>
The TreeLayout creates tree layouts for arbitrary trees. It is not restricted
to a specific output or format, but can be used for any kind of two dimensional
diagram. Examples are Swing based components, SVG files, and many more. This is
possible because TreeLayout separates the layout of a tree from the actual
rendering.<p>
To use the TreeLayout you mainly need to supply an instance of the
{@link org.abego.treelayout.TreeLayout TreeLayout} class with the nodes of the tree (including "children"
links), together with the "size" of each node. In addition you can configure
the layout by specifying parameters like "gap between levels" etc..<p>
Based on this information TreeLayout creates a compact, nice looking layout.
The layout has the following properties [2]:
<ol>
<li>The layout displays the hierarchical structure of the tree, i.e. the
y-coordinate of a node is given by its level. (*)</li>
<li>The edges do not cross each other and nodes on the same level have a
minimal horizontal distance.</li>
<li>The drawing of a subtree does not depend on its position in the tree, i.e.
isomorphic subtrees are drawn identically up to translation.</li>
<li>The order of the children of a node is displayed in the drawing.</li>
<li>The algorithm works symmetrically, i.e. the drawing of the reflection of a
tree is the reflected drawing of the original tree.</li>
</ol>
<i> (*) When the root is at the left or right (see "Root Location") the
x-coordinate of a node is given by its level</i><p>
Here an example tree layout:<p>
<img src="doc-files/TreeGraphView-Top.png">
<h2>Usage</h2>
To use the TreeLayout you will create a {@link org.abego.treelayout.TreeLayout TreeLayout} instance with:
<ul>
<li>a tree, accessible through the {@link org.abego.treelayout.TreeForTreeLayout TreeForTreeLayout} interface, </li>
<li>a {@link org.abego.treelayout.NodeExtentProvider NodeExtentProvider}, and</li>
<li>a {@link org.abego.treelayout.Configuration Configuration}.</li>
</ul>
Using these objects the TreeLayout will then calculate the layout and provide
the result through the method {@link org.abego.treelayout.TreeLayout#getNodeBounds() getNodeBounds}.
<h3>TreeForTreeLayout</h3>
The TreeLayout works on any kind of tree and uses the {@link org.abego.treelayout.TreeForTreeLayout TreeForTreeLayout}
interface to access such a tree.<p>
To use the TreeLayout you therefore need to provide it with a TreeForTreeLayout
implementation. In most situations you will not need to deal with all details
of that interface, but create an implementation by extending
{@link org.abego.treelayout.util.AbstractTreeForTreeLayout AbstractTreeForTreeLayout}, or even use the class
{@link org.abego.treelayout.util.DefaultTreeForTreeLayout DefaultTreeForTreeLayout} directly.
<h4>Example: Extending AbstractTreeForTreeLayout</h4>
Assume you have a tree consisting of nodes of type StringTreeNode:<p>
<img src="doc-files/StringTreeNodeUML.png"><p>
As StringTreeNode provides the children in a list and you can get the parent for
each node you can extend {@link org.abego.treelayout.util.AbstractTreeForTreeLayout AbstractTreeForTreeLayout} to create your
TreeForTreeLayout implementation. You only need to implement two methods and
the constructor:<p>
<pre>
public class StringTreeAsTreeForTreeLayout extends
&nbsp;&nbsp;&nbsp;&nbsp;AbstractTreeForTreeLayout&lt;StringTreeNode&gt; {
&nbsp;&nbsp;&nbsp;&nbsp;public StringTreeAsTreeForTreeLayout(StringTreeNode root) {
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;super(root);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;public StringTreeNode getParent(StringTreeNode node) {
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;return node.getParent();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;public List&lt;StringTreeNode&gt; getChildrenList(StringTreeNode parentNode) {
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;return parentNode.getChildren();
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</pre>
<i>(Make sure to check out the performance constraints of {@link org.abego.treelayout.util.AbstractTreeForTreeLayout#getChildrenList(Object) getChildrenList} and {@link org.abego.treelayout.util.AbstractTreeForTreeLayout#getParent(Object) getParent}.)</i>
<h4>Example: Using the DefaultTreeForTreeLayout</h4>
Assume you want to create a tree with TextInBox items as nodes:<p>
<img src="doc-files/TextInBoxUML.png"><p>
As you have no own tree implementation yet you may as well use
{@link org.abego.treelayout.util.DefaultTreeForTreeLayout DefaultTreeForTreeLayout} to create the tree:<p>
<pre>
TextInBox root = new TextInBox("root", 40, 20);
TextInBox n1 = new TextInBox("n1", 30, 20);
TextInBox n1_1 = new TextInBox("n1.1\n(first node)", 80, 36);
TextInBox n1_2 = new TextInBox("n1.2", 40, 20);
TextInBox n1_3 = new TextInBox("n1.3\n(last node)", 80, 36);
TextInBox n2 = new TextInBox("n2", 30, 20);
TextInBox n2_1 = new TextInBox("n2", 30, 20);
DefaultTreeForTreeLayout&lt;TextInBox&gt; tree =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new DefaultTreeForTreeLayout&lt;TextInBox&gt;(root);
tree.addChild(root, n1);
tree.addChild(n1, n1_1);
tree.addChild(n1, n1_2);
tree.addChild(n1, n1_3);
tree.addChild(root, n2);
tree.addChild(n2, n2_1);
</pre><p>
This will create a tree like this:<p>
<img src="doc-files/svgdemo.png">
<h3>NodeExtentProvider</h3>
TreeLayout also needs to know the extent (width and height) of each node in the
tree. This information is provided through the {@link org.abego.treelayout.NodeExtentProvider NodeExtentProvider}.<p>
If all nodes have the same size you can use a {@link org.abego.treelayout.util.FixedNodeExtentProvider FixedNodeExtentProvider}
instance with the proper width and height.<p>
In general you will create your own NodeExtentProvider implementation.
<h4>Example</h4>
Assume you want to create a tree with TextInBox items as nodes:<p>
<img src="doc-files/TextInBoxUML.png"><p>
Here each node contains its width and height. So your NodeExtentProvider may
look like this:<p><pre>
public class TextInBoxNodeExtentProvider implements
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NodeExtentProvider&lt;TextInBox&gt; {
&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;public double getWidth(TextInBox treeNode) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return treeNode.width;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;public double getHeight(TextInBox treeNode) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return treeNode.height;
&nbsp;&nbsp;&nbsp;&nbsp;}
}</pre>
<h3>Configuration</h3>
You can use a {@link org.abego.treelayout.Configuration Configuration} to customize various aspects of the
TreeLayout:
<ul>
<li>the gap between levels,</li>
<li>the minimal gap between nodes,</li>
<li>the position of the root node,</li>
<li>the alignment of smaller nodes within a level.</li>
</ul>
Most of the times using the {@link org.abego.treelayout.util.DefaultConfiguration DefaultConfiguration} class will be sufficient.
<h4>Root Position</h4>
By default the root of the tree is located at the top of the diagram. However
one may also put it at the left, right or bottom of the diagram.
<p>
<table border="1">
<tr>
<th>Top (Default)</th>
<th>Left</th>
<th>Right</th>
<th>Bottom</th>
</tr>
<tr>
<td style="padding:10px;"><img src="doc-files/TreeGraphView-Top.png"></td>
<td style="padding:10px;"><img src="doc-files/TreeGraphView-Left.png"></td>
<td style="padding:10px;"><img src="doc-files/TreeGraphView-Right.png"></td>
<td style="padding:10px;"><img src="doc-files/TreeGraphView-Bottom.png"></td>
</tr>
</table>
<p>
See {@link org.abego.treelayout.Configuration#getRootLocation() getRootLocation}.
<h4>Alignment in Level</h4>
By default all nodes of one level are centered in the level. However
one may also align them "towards the root" or "away from the root". When the
root is located at the top this means the nodes are aligned "to the top of the
level" or "to the bottom of the level".
<p>
<table border="1">
<tr>
<th>Center (Default)</th>
<th>TowardsRoot ("top of level")</th>
<th>AwayFromRoot ("bottom of level")</th>
</tr>
<tr>
<td style="padding:10px;"><img src="doc-files/TreeGraphView-Center.png"></td>
<td style="padding:10px;"><img src="doc-files/TreeGraphView-TowardsRoot.png"></td>
<td style="padding:10px;"><img src="doc-files/TreeGraphView-AwayFromRoot.png"></td>
</tr>
</table>
<p>Alignment in level when root is at the left:</p>
<table border="1">
<tr>
<th>Center (Default)</th>
<th>TowardsRoot ("left of level")</th>
<th>AwayFromRoot<br>("right of level")</th>
</tr>
<tr>
<td style="padding:10px;"><img src="doc-files/TreeGraphView-Center-RootLeft.png"></td>
<td style="padding:10px;"><img src="doc-files/TreeGraphView-TowardsRoot-RootLeft.png"></td>
<td style="padding:10px;"><img src="doc-files/TreeGraphView-AwayFromRoot-RootLeft.png"></td>
</tr>
</table>
<p>Of cause the alignment also works when the root is at the bottom or at the right side.</p>
See {@link org.abego.treelayout.Configuration#getAlignmentInLevel() getAlignmentInLevel}.
<h4 style="page-break-before:always">Gap between Levels and Nodes</h4>
The gap between subsequent levels and the minimal gap between nodes can be configured.
<p>
<img src="doc-files/gapsAndLevels.png">
<p>
See {@link org.abego.treelayout.Configuration#getGapBetweenLevels(int) getGapBetweenLevels} and
{@link org.abego.treelayout.Configuration#getGapBetweenNodes(Object, Object) getGapBetweenNodes}.
<h2>Examples</h2>
In the "demo" package you will find examples using the TreeLayout.
<ul>
<li>SVGDemo - Demonstrates how to use the TreeLayout to create a tree diagram with SVG (Scalable Vector Graphic)</li>
<li>SwingDemo - Demonstrates how to use the TreeLayout to render a tree in a Swing application</li>
</ul>
<h2 style="page-break-before:always">Performance</h2>
Based on Walker's algorithm [1] with enhancements suggested by Buchheim,
J&uuml;nger, and Leipert [2] the software builds tree layouts in linear time.
I.e. even trees with many nodes are built fast. Other than with the
ReingoldTilford algorithm [3] one is not limited to binary trees.
<p>
The following figure show the results running the TreeLayout algorithm on a
MacBook Pro 2.4 GHz Intel Core 2 Duo (2 GB Memory (-Xmx2000m)). The variously
sized trees were created randomly.
<p>
<img src="doc-files/performance.png">
<p>
The picture illustrates the linear time behavior of the algorithm and shows
the applicability also for large number of nodes. In this setting it takes
approx. 5 &micro;s to place one node.
<h2>License</h2>
TreeLayout is distributed under a BSD license of
<a href="http://www.abego-software.de">abego Software</a>.
(<a href="doc-files/LICENSE.TXT">License text</a>)
<h2>Sponsor</h2>
The development of TreeLayout was generously sponsored by Terence Parr
"The ANTLR Guy" (parrt at cs dot usfca dot edu).
<h2>References</h2>
[1] Walker JQ II. A node-positioning algorithm for general trees.
<i>SoftwarePractice and Experience</i> 1990; <b>20</b>(7):685705.
<p>
[2] Buchheim C, J&uuml;nger M, Leipert S. Drawing rooted trees in linear time.
<i>SoftwarePractice and Experience</i> 2006; <b>36</b>(6):651665
<p>
[3] Reingold EM, Tilford JS. Tidier drawings of trees.
<i>IEEE Transactions on Software Engineering</i> 1981; <b>7</b>(2):223228.
<p>
@author Udo Borkowski (ub@abego.org)
*/
package org.abego.treelayout;

View File

@ -0,0 +1,122 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.abego.treelayout.util;
import java.util.List;
import org.abego.treelayout.TreeForTreeLayout;
import org.abego.treelayout.internal.util.java.lang.IterableUtil;
import org.abego.treelayout.internal.util.java.util.ListUtil;
/**
* Provides an easy way to implement the {@link TreeForTreeLayout} interface by
* defining just two simple methods and a constructor.
* <p>
* To use this class the underlying tree must provide the children as a list
* (see {@link #getChildrenList(Object)} and give direct access to the parent of
* a node (see {@link #getParent(Object)}).
* <p>
*
* See also {@link DefaultTreeForTreeLayout}.
*
* @author Udo Borkowski (ub@abego.org)
*
* @param <TreeNode>
*/
abstract public class AbstractTreeForTreeLayout<TreeNode> implements
TreeForTreeLayout<TreeNode> {
/**
* Returns the parent of a node, if it has one.
* <p>
* Time Complexity: O(1)
*
* @param node
* @return [nullable] the parent of the node, or null when the node is a
* root.
*/
abstract public TreeNode getParent(TreeNode node);
/**
* Return the children of a node as a {@link List}.
* <p>
* Time Complexity: O(1)
* <p>
* Also the access to an item of the list must have time complexity O(1).
* <p>
* A client must not modify the returned list.
*
* @param node
* @return the children of the given node. When node is a leaf the list is
* empty.
*/
abstract public List<TreeNode> getChildrenList(TreeNode node);
private final TreeNode root;
public AbstractTreeForTreeLayout(TreeNode root) {
this.root = root;
}
@Override
public TreeNode getRoot() {
return root;
}
@Override
public boolean isLeaf(TreeNode node) {
return getChildrenList(node).isEmpty();
}
@Override
public boolean isChildOfParent(TreeNode node, TreeNode parentNode) {
return getParent(node) == parentNode;
}
@Override
public Iterable<TreeNode> getChildren(TreeNode node) {
return getChildrenList(node);
}
@Override
public Iterable<TreeNode> getChildrenReverse(TreeNode node) {
return IterableUtil.createReverseIterable(getChildrenList(node));
}
@Override
public TreeNode getFirstChild(TreeNode parentNode) {
return getChildrenList(parentNode).get(0);
}
@Override
public TreeNode getLastChild(TreeNode parentNode) {
return ListUtil.getLast(getChildrenList(parentNode));
}
}

View File

@ -0,0 +1,135 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.abego.treelayout.util;
import static org.abego.treelayout.internal.util.Contract.checkArg;
import org.abego.treelayout.Configuration;
/**
* Specify a {@link Configuration} through configurable parameters, or falling
* back to some frequently used defaults.
*
* @author Udo Borkowski (ub@abego.org)
*
*
* @param <TreeNode>
*/
public class DefaultConfiguration<TreeNode> implements
Configuration<TreeNode> {
/**
* Specifies the constants to be used for this Configuration.
*
* @param gapBetweenLevels
* @param gapBetweenNodes
* @param location
* [default: {@link org.abego.treelayout.Configuration.Location#Top Top}]
* @param alignmentInLevel
* [default: {@link org.abego.treelayout.Configuration.AlignmentInLevel#Center Center}]
*/
public DefaultConfiguration(double gapBetweenLevels,
double gapBetweenNodes, Location location,
AlignmentInLevel alignmentInLevel) {
checkArg(gapBetweenLevels >= 0, "gapBetweenLevels must be >= 0");
checkArg(gapBetweenNodes >= 0, "gapBetweenNodes must be >= 0");
this.gapBetweenLevels = gapBetweenLevels;
this.gapBetweenNodes = gapBetweenNodes;
this.location = location;
this.alignmentInLevel = alignmentInLevel;
}
/**
* Convenience constructor, using a default for the alignmentInLevel.
* <p>
* see
* {@link #DefaultConfiguration(double, double, org.abego.treelayout.Configuration.Location, org.abego.treelayout.Configuration.AlignmentInLevel)}
*/
public DefaultConfiguration(double gapBetweenLevels,
double gapBetweenNodes, Location location) {
this(gapBetweenLevels, gapBetweenNodes, location,
AlignmentInLevel.Center);
}
/**
* Convenience constructor, using a default for the rootLocation and the
* alignmentInLevel.
* <p>
* see
* {@link #DefaultConfiguration(double, double, org.abego.treelayout.Configuration.Location, org.abego.treelayout.Configuration.AlignmentInLevel)}
*/
public DefaultConfiguration(double gapBetweenLevels,
double gapBetweenNodes) {
this(gapBetweenLevels, gapBetweenNodes, Location.Top,
AlignmentInLevel.Center);
}
// -----------------------------------------------------------------------
// gapBetweenLevels
private final double gapBetweenLevels;
@Override
public double getGapBetweenLevels(int nextLevel) {
return gapBetweenLevels;
}
// -----------------------------------------------------------------------
// gapBetweenNodes
private final double gapBetweenNodes;
@Override
public double getGapBetweenNodes(TreeNode node1, TreeNode node2) {
return gapBetweenNodes;
}
// -----------------------------------------------------------------------
// location
private final Location location;
@Override
public Location getRootLocation() {
return location;
}
// -----------------------------------------------------------------------
// alignmentInLevel
private AlignmentInLevel alignmentInLevel;
@Override
public AlignmentInLevel getAlignmentInLevel() {
return alignmentInLevel;
}
}

View File

@ -0,0 +1,128 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.abego.treelayout.util;
import static org.abego.treelayout.internal.util.Contract.checkArg;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.abego.treelayout.TreeForTreeLayout;
/**
* Provides a generic implementation for the {@link TreeForTreeLayout}
* interface, applicable to any type of tree node.
* <p>
* It allows you to create a tree "from scratch", without creating any new
* class.
* <p>
* To create a tree you must provide the root of the tree (see
* {@link #DefaultTreeForTreeLayout(Object)}. Then you can incrementally
* construct the tree by adding children to the root or other nodes of the tree
* (see {@link #addChild(Object, Object)} and
* {@link #addChildren(Object, Object...)}).
*
* @author Udo Borkowski (ub@abego.org)
*
* @param <TreeNode>
*/
public class DefaultTreeForTreeLayout<TreeNode> extends
AbstractTreeForTreeLayout<TreeNode> {
private List<TreeNode> emptyList;
private List<TreeNode> getEmptyList() {
if (emptyList == null) {
emptyList = new ArrayList<TreeNode>();
}
return emptyList;
}
private Map<TreeNode, List<TreeNode>> childrenMap = new HashMap<TreeNode, List<TreeNode>>();
private Map<TreeNode, TreeNode> parents = new HashMap<TreeNode, TreeNode>();
/**
* Creates a new instance with a given node as the root
*
* @param root
* the node to be used as the root.
*/
public DefaultTreeForTreeLayout(TreeNode root) {
super(root);
}
@Override
public TreeNode getParent(TreeNode node) {
return parents.get(node);
}
@Override
public List<TreeNode> getChildrenList(TreeNode node) {
List<TreeNode> result = childrenMap.get(node);
return result == null ? getEmptyList() : result;
}
/**
*
* @param node
* @return true iff the node is in the tree
*/
public boolean hasNode(TreeNode node) {
return node == getRoot() || parents.containsKey(node);
}
/**
* @param parentNode
* [hasNode(parentNode)]
* @param node
* [!hasNode(node)]
*/
public void addChild(TreeNode parentNode, TreeNode node) {
checkArg(hasNode(parentNode), "parentNode is not in the tree");
checkArg(!hasNode(node), "node is already in the tree");
List<TreeNode> list = childrenMap.get(parentNode);
if (list == null) {
list = new ArrayList<TreeNode>();
childrenMap.put(parentNode, list);
}
list.add(node);
parents.put(node, parentNode);
}
public void addChildren(TreeNode parentNode, TreeNode... nodes) {
for (TreeNode node : nodes) {
addChild(parentNode, node);
}
}
}

View File

@ -0,0 +1,83 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.abego.treelayout.util;
import static org.abego.treelayout.internal.util.Contract.*;
import org.abego.treelayout.NodeExtentProvider;
/**
* A {@link NodeExtentProvider} returning the same width and height for each
* node.
*
* @author Udo Borkowski (ub@abego.org)
*
* @param <T>
*/
public class FixedNodeExtentProvider<T> implements NodeExtentProvider<T> {
private final double width;
private final double height;
/**
* Specifies the constants to be used as the width and height of the nodes.
*
* @param width
* [default=0]
*
* @param height
* [default=0]
*/
public FixedNodeExtentProvider(double width, double height) {
checkArg(width >= 0, "width must be >= 0");
checkArg(height >= 0, "height must be >= 0");
this.width = width;
this.height = height;
}
/**
* see {@link #FixedNodeExtentProvider(double, double)}
*/
public FixedNodeExtentProvider() {
this(0, 0);
}
@Override
public double getWidth(T treeNode) {
return width;
}
@Override
public double getHeight(T treeNode) {
return height;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@ -0,0 +1,37 @@
/*
* [The "BSD license"]
* Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the abego Software GmbH nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Various utility classes to simplify usage of the
* {@link org.abego.treelayout.TreeLayout}.
*
*/
package org.abego.treelayout.util;