//ShowTree Tree Visualization System
//Copyright (C) 2009 Yuvi Masory
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation, version 3 only.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
package logic.positioningAlgorithms;
import display.DrawTree;
import display.Start;
import display.components.TreePane;
import logic.Node;
import logic.treeIterators.PreorderIterator;
/*
* Implements:
* C. Buchheim, M. Junger, and S. Leipert. Drawing rooted trees in linear
* time. Software: Practice and Experience, 36(6):651�665, 2006.
*
* Original (quadratic time) algorithm due to:
* J.Q. Walker II. A node-positioning algorithm for general trees. Software:
* Practice and Experience, 20(7):685�705, 1990.
*/
// FIXME there is probably an error in this implementation, surrounding the use of the "number" field
public class W_BJL06 implements PositioningAlgorithm {
// use of global defaultAncestor different from BJL
Node defaultAncestor;
public void embed(Node n, double s) {
n.assignDepths();
initializeTree(n);
initializeNumber(n);
//assigned x/y positions will be meaningless, this is done for dimensions only
DrawTree.computeNodeRectangles(n, TreePane.getInstance().getGraphics());
firstWalk(n, s);
secondWalk(n, -n.getPrelim());
}
private void initializeTree(Node n) {
n.setModifier(0);
n.setThread(null);
n.setAncestor(n);
n.setChange(0);
n.setShift(0);
for(int i = 0; i < n.degree(); i++) {
initializeTree(n.getChildren().get(i));
}
}
/*
* Initializes the nodes' number field to depth
*/
private void initializeNumber(Node n) {
PreorderIterator iter = new PreorderIterator(n);
Node cur;
while(iter.hasNext()) {
cur = iter.next();
cur.setNumber(cur.getDepth());
}
}
private void firstWalk(Node v, double s) {
if(v.isLeaf()) {
v.setPrelim(0);
if(v.hasLeftSibling()) {
Node w = v.getLeftSibling();
v.setPrelim(w.getPrelim() + w.getGraphicalRectangle().getWidth()/2 + v.getGraphicalRectangle().getWidth()/2 + s);
}
}
else {
defaultAncestor = v.getChildren().get(0);
for(int i = 0; i < v.degree(); i++) {
Node w = v.getChildren().get(i);
firstWalk(w, s);
apportion(w, s);
}
executeShifts(v);
double midpoint = 0.5 * (v.getChildren().get(0).getPrelim() + v.getChildren().get(v.degree() - 1).getPrelim());
if(v.hasLeftSibling()) {
Node w = v.getLeftSibling();
v.setPrelim(w.getPrelim() + w.getGraphicalRectangle().getWidth()/2 + v.getGraphicalRectangle().getWidth()/2 + s);
v.setModifier(v.getPrelim() - midpoint);
}
else {
v.setPrelim(midpoint);
}
}
}
private void apportion(Node v, double s) {
Node v_inside_right = null;
Node v_outside_right = null;
Node v_inside_left = null;
Node v_outside_left = null;
double s_inside_right = 0;
double s_outside_right = 0;
double s_inside_left = 0;
double s_outside_left = 0;
if(v.hasLeftSibling()) {
Node w = v.getLeftSibling();
v_inside_right = v;
v_outside_right = v;
v_inside_left = w;
v_outside_left = v_inside_right.getParent().getChildren().get(0);
s_inside_right = v_inside_right.getModifier();
s_outside_right = v_outside_right.getModifier();
s_inside_left = v_inside_left.getModifier();
s_outside_left = v_outside_left.getModifier();
while(nextRight(v_inside_left) != null && nextLeft(v_inside_right) != null) {
v_inside_left = nextRight(v_inside_left);
v_inside_right = nextLeft(v_inside_right);
v_outside_left = nextLeft(v_outside_left);
v_outside_right = nextRight(v_outside_right);
v_outside_right.setAncestor(v);
double shift = (v_inside_left.getPrelim() + s_inside_left)
- (v_inside_right.getPrelim() + s_inside_right)
+ s
+ v_inside_left.getGraphicalRectangle().getWidth()/2
+ v_inside_right.getGraphicalRectangle().getWidth()/2;
if (shift > 0) {
moveSubtree(ancestor(v_inside_left, v), v, shift);
s_inside_right += shift;
s_outside_right += shift;
}
s_inside_left += v_inside_left.getModifier();
s_inside_right += v_inside_right.getModifier();
s_outside_left += v_outside_left.getModifier();
s_outside_right += v_outside_right.getModifier();
}
//BJL has these next two conditionals one block in
if(nextRight(v_inside_left) != null && nextRight(v_outside_right) == null) {
v_outside_right.setThread(nextRight(v_inside_left));
v_outside_right.setModifier(v_outside_right.getModifier() + s_inside_left - s_outside_right);
}
if (nextLeft(v_inside_right) != null && nextLeft(v_outside_left) == null) {
v_outside_left.setThread(nextLeft(v_inside_right));
v_outside_left.setModifier(v_outside_left.getModifier() + s_inside_right - s_outside_left);
defaultAncestor = v;
}
}
}
private Node nextLeft(Node v) {
if (v.isLeaf() == false) {
return v.getChildren().get(0);
}
else {
return v.getThread();
}
}
private Node nextRight(Node v) {
if (v.isLeaf() == false) {
return v.getChildren().get(v.degree() - 1);
}
else {
return v.getThread();
}
}
private void moveSubtree(Node w_left, Node w_right, double shift) {
// different from BJL
double subtrees = Math.max(1, w_right.getNumber() - w_left.getNumber());
w_right.setChange(w_right.getChange() - shift/subtrees);
w_right.setShift(w_right.getShift() + shift);
w_left.setChange(w_left.getChange() + shift/subtrees);
w_right.setPrelim(w_right.getPrelim() + shift);
w_right.setModifier(w_right.getModifier() + shift);
}
private void executeShifts(Node v) {
double shift = 0;
double change = 0;
for(int i = v.degree() - 1; i >= 0; i--) {
Node w = v.getChildren().get(i);
w.setPrelim(w.getPrelim() + shift);
w.setModifier(w.getModifier() + shift);
change += w.getChange();
shift += w.getShift() + change;
}
}
private Node ancestor(Node v_inside_left, Node v) {
if (v_inside_left.getAncestor().getParent() == v.getParent() && v_inside_left != v) {
return v_inside_left.getAncestor();
}
else {
return defaultAncestor;
}
}
private void secondWalk(Node v, double m) {
v.setX(v.getPrelim() + m);
v.setY(v.getDepth() * Start.Y_SEPARATION);
for(int i = 0; i < v.degree(); i++) {
secondWalk(v.getChildren().get(i), m + v.getModifier());
}
}
public boolean isBinary() {
return false;
}
public boolean handlesNodeWidths() {
return true;
}
public String toString() {
return "n: W_BJL06";
}
}