br.jabuti.graph.layout.jung.HierachicalGraphLayout.java Source code

Java tutorial

Introduction

Here is the source code for br.jabuti.graph.layout.jung.HierachicalGraphLayout.java

Source

/*
 * Copyright (c) 2005, the JUNG Project and the Regents of the University of
 * California All rights reserved.
 *
 * This software is open-source under the BSD license; see either "license.txt"
 * or http://jung.sourceforge.net/license.txt for a description.
 *
 * Created on Jul 9, 2005
 */

package br.jabuti.graph.layout.jung;

import java.awt.Dimension;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.map.LazyMap;

import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.DirectedGraph;
import edu.uci.ics.jung.graph.Graph;

/**
 * @author Karlheinz Toni
 * @author Tom Nelson - converted to jung2
 * 
 */
public class HierachicalGraphLayout<V, E> implements Layout<V, E> {

    protected Dimension size = new Dimension(600, 600);
    protected DirectedGraph<V, E> graph;
    protected Map<V, Integer> basePositions = new HashMap<V, Integer>();

    protected Map<V, Point2D> locations = LazyMap.decorate(new HashMap<V, Point2D>(),
            new Transformer<V, Point2D>() {
                public Point2D transform(V arg0) {
                    return new Point2D.Double();
                }
            });

    protected transient Set<V> alreadyDone = new HashSet<V>();

    /**
     * The default horizontal vertex spacing. Initialized to 50.
     */
    public static int DEFAULT_DISTX = 50;

    /**
     * The default vertical vertex spacing. Initialized to 50.
     */
    public static int DEFAULT_DISTY = 50;

    /**
     * The horizontal vertex spacing. Defaults to {@code DEFAULT_XDIST}.
     */
    protected int distX = 50;

    /**
     * The vertical vertex spacing. Defaults to {@code DEFAULT_YDIST}.
     */
    protected int distY = 50;

    protected transient Point m_currentPoint = new Point();

    private V rootVertex;

    /**
     * Creates an instance for the specified graph with default X and Y distances.
     */
    public HierachicalGraphLayout(DirectedGraph<V, E> g, V rootVertex) {
        this(g, rootVertex, DEFAULT_DISTX, DEFAULT_DISTY);
    }

    /**
     * Creates an instance for the specified graph and X distance with default Y distance.
     */
    public HierachicalGraphLayout(DirectedGraph<V, E> g, V rootVertex, int distx) {
        this(g, rootVertex, distx, DEFAULT_DISTY);
    }

    /**
     * Creates an instance for the specified graph, X distance, and Y distance.
     */
    public HierachicalGraphLayout(DirectedGraph<V, E> g, V rootVertex, int distx, int disty) {
        if (g == null)
            throw new IllegalArgumentException("Graph must be non-null");
        if (distx < 1 || disty < 1)
            throw new IllegalArgumentException("X and Y distances must each be positive");
        this.graph = g;
        this.distX = distx;
        this.distY = disty;
        this.rootVertex = rootVertex;
        buildTree();
    }

    protected void buildTree() {
        m_currentPoint = new Point(0, 20);
        Collection<V> roots = new ArrayList<V>();
        roots.add(rootVertex);
        if (roots.size() > 0) {
            calculateDimensionX(roots);
            for (V v : roots) {
                calculateDimensionX(v);
                m_currentPoint.x += basePositions.get(v) / 2 + 50;
                buildTree(v, m_currentPoint.x);
            }
        }

        int width = 0;
        for (V v : roots) {
            width += basePositions.get(v);
        }
    }

    protected void buildTree(V v, int x) {
        if (!alreadyDone.contains(v)) {
            alreadyDone.add(v);

            // go one level further down
            m_currentPoint.y += this.distY;
            m_currentPoint.x = x;

            setCurrentPositionFor(v);

            int sizeXofCurrent = basePositions.get(v);
            int lastX = x - sizeXofCurrent / 2;
            int sizeXofChild;
            int startXofChild;
            for (V element : graph.getSuccessors(v)) {
                try {
                    sizeXofChild = basePositions.get(element);
                    startXofChild = lastX + sizeXofChild / 2;
                    buildTree(element, startXofChild);
                    lastX = lastX + sizeXofChild + distX;
                } catch (NullPointerException e) {

                }
            }
            m_currentPoint.y -= this.distY;
        }
    }

    private int calculateDimensionX(V v) {

        int size = 0;
        int childrenNum = graph.getSuccessors(v).size();

        if (childrenNum != 0) {
            for (V element : graph.getSuccessors(v)) {
                // size += calculateDimensionX(element) + distX;
                size += 30 + distX;
            }
        }
        size = Math.max(0, size - distX);
        basePositions.put(v, size);

        return size;
    }

    private int calculateDimensionX(Collection<V> roots) {

        int size = 0;
        for (V v : roots) {
            int childrenNum = graph.getSuccessors(v).size();

            if (childrenNum != 0) {
                for (V element : graph.getSuccessors(v)) {
                    size += calculateDimensionX(element) + distX;
                }
            }
            size = Math.max(0, size - distX);
            basePositions.put(v, size);
        }

        return size;
    }

    /**
     * This method is not supported by this class. The size of the layout is determined by the
     * topology of the tree, and by the horizontal and vertical spacing (optionally set by the
     * constructor).
     */
    public void setSize(Dimension size) {
        throw new UnsupportedOperationException("Size of TreeLayout is set" + " by vertex spacing in constructor");
    }

    protected void setCurrentPositionFor(V vertex) {
        int x = m_currentPoint.x;
        int y = m_currentPoint.y;
        if (x < 0)
            size.width -= x;

        if (x > size.width - distX)
            size.width = x + distX;

        if (y < 0)
            size.height -= y;
        if (y > size.height - distY)
            size.height = y + distY;
        locations.get(vertex).setLocation(m_currentPoint);

    }

    public Graph<V, E> getGraph() {
        return graph;
    }

    public Dimension getSize() {
        return size;
    }

    public void initialize() {

    }

    public boolean isLocked(V v) {
        return false;
    }

    public void lock(V v, boolean state) {
    }

    public void reset() {
    }

    public void setGraph(Graph<V, E> graph) {
        if (graph instanceof DirectedGraph) {
            this.graph = (DirectedGraph<V, E>) graph;
            buildTree();
        } else {
            throw new IllegalArgumentException("Graph must be a directed graph");
        }
    }

    public void setInitializer(Transformer<V, Point2D> initializer) {
    }

    /**
     * Returns the center of this layout's area.
     */
    public Point2D getCenter() {
        return new Point2D.Double(size.getWidth() / 2, size.getHeight() / 2);
    }

    public void setLocation(V v, Point2D location) {
        locations.get(v).setLocation(location);
    }

    public Point2D transform(V v) {
        return locations.get(v);
    }
}