forcedirectedlayout.java

来自「用applet实现很多应用小程序」· Java 代码 · 共 450 行 · 第 1/2 页

JAVA
450
字号
package prefuse.action.layout.graph;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;

import prefuse.action.layout.Layout;
import prefuse.data.Graph;
import prefuse.data.Schema;
import prefuse.data.tuple.TupleSet;
import prefuse.util.PrefuseLib;
import prefuse.util.force.DragForce;
import prefuse.util.force.ForceItem;
import prefuse.util.force.ForceSimulator;
import prefuse.util.force.NBodyForce;
import prefuse.util.force.SpringForce;
import prefuse.visual.EdgeItem;
import prefuse.visual.NodeItem;
import prefuse.visual.VisualItem;


/**
 * <p>Layout that positions graph elements based on a physics simulation of
 * interacting forces; by default, nodes repel each other, edges act as
 * springs, and drag forces (similar to air resistance) are applied. This
 * algorithm can be run for multiple iterations for a run-once layout
 * computation or repeatedly run in an animated fashion for a dynamic and
 * interactive layout.</p>
 * 
 * <p>The running time of this layout algorithm is the greater of O(N log N)
 * and O(E), where N is the number of nodes and E the number of edges.
 * The addition of custom force calculation modules may, however, increase
 * this value.</p>
 * 
 * <p>The {@link prefuse.util.force.ForceSimulator} used to drive this layout
 * can be set explicitly, allowing any number of custom force directed layouts
 * to be created through the user's selection of included
 * {@link prefuse.util.force.Force} components. Each node in the layout is
 * mapped to a {@link prefuse.util.force.ForceItem} instance and each edge
 * to a {@link prefuse.util.force.Spring} instance for storing the state
 * of the simulation. See the {@link prefuse.util.force} package for more.</p>
 * 
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class ForceDirectedLayout extends Layout {
    
    private ForceSimulator m_fsim;
    private long m_lasttime = -1L;
    private long m_maxstep = 50L;
    private boolean m_runonce;
    private int m_iterations = 100;
    private boolean m_enforceBounds;
    
    protected transient VisualItem referrer;
    
    protected String m_nodeGroup;
    protected String m_edgeGroup;
    
    /**
     * Create a new ForceDirectedLayout. By default, this layout will not
     * restrict the layout to the layout bounds and will assume it is being
     * run in animated (rather than run-once) fashion.
     * @param graph the data group to layout. Must resolve to a Graph instance.
     */
    public ForceDirectedLayout(String graph)
    {
        this(graph, false, false);
    }

    /**
     * Create a new ForceDirectedLayout. The layout will assume it is being
     * run in animated (rather than run-once) fashion.
     * @param group the data group to layout. Must resolve to a Graph instance.
     * @param enforceBounds indicates whether or not the layout should require
     * that all node placements stay within the layout bounds.
     */
    public ForceDirectedLayout(String group, boolean enforceBounds)
    {
        this(group, enforceBounds, false);
    }
    
    /**
     * Create a new ForceDirectedLayout.
     * @param group the data group to layout. Must resolve to a Graph instance.
     * @param enforceBounds indicates whether or not the layout should require
     * that all node placements stay within the layout bounds.
     * @param runonce indicates if the layout will be run in a run-once or
     * animated fashion. In run-once mode, the layout will run for a set number
     * of iterations when invoked. In animation mode, only one iteration of the
     * layout is computed.
     */
    public ForceDirectedLayout(String group,
            boolean enforceBounds, boolean runonce)
    {
        super(group);
        m_nodeGroup = PrefuseLib.getGroupName(group, Graph.NODES);
        m_edgeGroup = PrefuseLib.getGroupName(group, Graph.EDGES);
        
        m_enforceBounds = enforceBounds;
        m_runonce = runonce;
        m_fsim = new ForceSimulator();
        m_fsim.addForce(new NBodyForce());
        m_fsim.addForce(new SpringForce());
        m_fsim.addForce(new DragForce());
    }
    
    /**
     * Create a new ForceDirectedLayout. The layout will assume it is being
     * run in animated (rather than run-once) fashion.
     * @param group the data group to layout. Must resolve to a Graph instance.
     * @param fsim the force simulator used to drive the layout computation
     * @param enforceBounds indicates whether or not the layout should require
     * that all node placements stay within the layout bounds.
     */
    public ForceDirectedLayout(String group,
            ForceSimulator fsim, boolean enforceBounds) {
        this(group, fsim, enforceBounds, false);
    }
    
    /**
     * Create a new ForceDirectedLayout.
     * @param group the data group to layout. Must resolve to a Graph instance.
     * @param fsim the force simulator used to drive the layout computation
     * @param enforceBounds indicates whether or not the layout should require
     * that all node placements stay within the layout bounds.
     * @param runonce indicates if the layout will be run in a run-once or
     * animated fashion. In run-once mode, the layout will run for a set number
     * of iterations when invoked. In animation mode, only one iteration of the
     * layout is computed.
     */
    public ForceDirectedLayout(String group, ForceSimulator fsim,
            boolean enforceBounds, boolean runonce)
    {
        super(group);
        m_nodeGroup = PrefuseLib.getGroupName(group, Graph.NODES);
        m_edgeGroup = PrefuseLib.getGroupName(group, Graph.EDGES);
        
        m_enforceBounds = enforceBounds;
        m_runonce = runonce;
        m_fsim = fsim;
    }
    
    // ------------------------------------------------------------------------
    
    /**
     * Get the maximum timestep allowed for integrating node settings between
     * runs of this layout. When computation times are longer than desired,
     * and node positions are changing dramatically between animated frames,
     * the max step time can be lowered to suppress node movement.
     * @return the maximum timestep allowed for integrating between two
     * layout steps.
     */
    public long getMaxTimeStep() {
        return m_maxstep;
    }

    /**
     * Set the maximum timestep allowed for integrating node settings between
     * runs of this layout. When computation times are longer than desired,
     * and node positions are changing dramatically between animated frames,
     * the max step time can be lowered to suppress node movement.
     * @param maxstep the maximum timestep allowed for integrating between two
     * layout steps
     */
    public void setMaxTimeStep(long maxstep) {
        this.m_maxstep = maxstep;
    }
    
    /**
     * Get the force simulator driving this layout.
     * @return the force simulator
     */
    public ForceSimulator getForceSimulator() {
        return m_fsim;
    }
    
    /**
     * Set the force simulator driving this layout.
     * @param fsim the force simulator
     */
    public void setForceSimulator(ForceSimulator fsim) {
        m_fsim = fsim;
    }
    
    /**
     * Get the number of iterations to use when computing a layout in
     * run-once mode.
     * @return the number of layout iterations to run
     */
    public int getIterations() {
        return m_iterations;
    }

    /**
     * Set the number of iterations to use when computing a layout in
     * run-once mode.
     * @param iter the number of layout iterations to run
     */
    public void setIterations(int iter) {
        if ( iter < 1 )
            throw new IllegalArgumentException(
                    "Iterations must be a positive number!");
        m_iterations = iter;
    }
    
    /**
     * Explicitly sets the node and edge groups to use for this layout,
     * overriding the group setting passed to the constructor.
     * @param nodeGroup the node data group
     * @param edgeGroup the edge data group
     */
    public void setDataGroups(String nodeGroup, String edgeGroup) {
        m_nodeGroup = nodeGroup;
        m_edgeGroup = edgeGroup;
    }
    
    // ------------------------------------------------------------------------
    
    /**
     * @see prefuse.action.Action#run(double)
     */
    public void run(double frac) {
        // perform different actions if this is a run-once or
        // run-continuously layout
        if ( m_runonce ) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?