package ch.hsr.ifs.pasta.tree; /** * The layout algorithm. * * @author silflow * */ class JBaum { private static float INITIAL_DISTANCE = 1f; private static <T> Node<T> firstWalk(final Node<T> node, final float siblingDistance, final float branchDistance) { if (node.children().isEmpty()) { if (node.hasLeftSibling()) { node.setX(node.leftSibling().x() + node.leftSibling().width() + siblingDistance); } else { node.setX(0.0f); } } else { Node<T> defaultAncestor = node.children().get(0); for (final Node<T> child : node.children()) { firstWalk(child, siblingDistance, branchDistance); defaultAncestor = apportion(child, defaultAncestor, branchDistance); } executeShifts(node); final float midPoint = (node.leftMostChild().x() + node.rightMostChild().x() + node.rightMostChild().width()) / 2f; if (node.hasLeftSibling()) { node.setX(node.leftSibling().x() + node.leftSibling().width() + siblingDistance); node.setMod(node.x() + node.width() / 2 - midPoint); } else { node.setX(midPoint - (node.width()) / 2); } } return node; } private static <T> Node<T> apportion(final Node<T> node, final Node<T> defaultAncestor, final float branchDistance) { if (node.hasLeftSibling()) { Node<T> innerRight = node; Node<T> outerRight = node; Node<T> innerLeft = node.leftSibling(); Node<T> outerLeft = node.leftMostSibling(); float sInnerRight = node.mod(); float sOuterRight = node.mod(); float sInnerLeft = innerLeft.mod(); float sOuterLeft = outerLeft.mod(); while (innerLeft.rightMostChild() != null && innerRight.leftMostChild() != null) { innerLeft = innerLeft.rightMostChild(); innerRight = innerRight.leftMostChild(); outerLeft = outerLeft.leftMostChild(); outerRight = outerRight.rightMostChild(); outerRight.setAncestor(node); final float shift = (innerLeft.x() + innerLeft.width() + sInnerLeft) - (innerRight.x() + sInnerRight) + branchDistance; if (shift > 0) { moveSubtree(ancestor(innerLeft, node, defaultAncestor), node, shift); sInnerRight += shift; sOuterRight += shift; } sInnerLeft += innerLeft.mod(); sInnerRight += innerRight.mod(); sOuterLeft += outerLeft.mod(); sOuterRight += outerRight.mod(); } if (innerLeft.rightMostChild() != null && outerRight.rightMostChild() == null) { outerRight.setThread(innerLeft.rightMostChild()); outerRight.setMod(outerRight.mod() + sInnerLeft - sOuterRight); } else { if (innerRight.leftMostChild() != null && outerLeft.leftMostChild() == null) { outerLeft.setThread(innerRight.leftMostChild()); outerLeft.setMod(outerLeft.mod() + sInnerRight - sOuterLeft); } return node; } } return defaultAncestor; } private static <T> Node<T> ancestor(final Node<T> innerLeft, final Node<T> node, final Node<T> defaultAncestor) { return (node.parent().children().contains(innerLeft.ancestor())) ? innerLeft.ancestor() : defaultAncestor; } private static <T> void moveSubtree(final Node<T> wl_anc, final Node<T> wr_node, final float shift) { final float subtrees = wr_node.number() - wl_anc.number(); wr_node.setChange(wr_node.change() - (shift / subtrees)); wr_node.setShift(wr_node.shift() + shift); wl_anc.setChange(wl_anc.change() + (shift / subtrees)); wr_node.setX(wr_node.x() + shift); wr_node.setMod(wr_node.mod() + shift); } private static <T> void executeShifts(final Node<T> node) { float shift = 0; float change = 0; for (int i = node.children().size() - 1; i > -1; --i) { final Node<T> child = node.children().get(i); child.setX(child.x() + shift); child.setMod(child.mod() + shift); change += child.change(); shift += child.shift() + change; } } private static <T> float secondWalk(final Node<T> node, final float m, final float depth, Float min) { node.setX(node.x() + m); node.setY(depth); if (min == null || node.x() < min) { min = node.x(); } for (final Node<T> child : node.children()) { min = secondWalk(child, m + node.mod(), depth + JBaum.INITIAL_DISTANCE, min); } return min; } private static <T> void thirdWalk(final Node<T> node, final float n) { node.setX(node.x() + n); for (final Node<T> child : node.children()) { thirdWalk(child, n); } } public static <T> Node<T> adjustTree(final Node<T> tree, final float siblingDistance, final float branchDistance) { final Node<T> intermediate = firstWalk(tree, siblingDistance, branchDistance); final float min = secondWalk(intermediate, 0f, 0f, null); if (min < 0) { thirdWalk(intermediate, -min); } return intermediate; } protected static <T> void reset(final Node<T> node) { node.visit(n -> { n.setShift(0); n.setMod(0); n.setX(0); n.setChange(0); n.setThread(null); n.setAncestor(n); return NodeVisitor.AfterVisitBehaviour.Continue; }); } }