Package com.mxgraph.layout.hierarchical

Source Code of com.mxgraph.layout.hierarchical.mxHierarchicalLayout

/*
* $Id: mxHierarchicalLayout.java,v 1.12 2010-12-01 18:08:40 david Exp $
* Copyright (c) 2005-2010, David Benson, Gaudenz Alder
*/
package com.mxgraph.layout.hierarchical;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.SwingConstants;

import com.mxgraph.layout.mxGraphLayout;
import com.mxgraph.layout.hierarchical.model.mxGraphHierarchyModel;
import com.mxgraph.layout.hierarchical.stage.mxCoordinateAssignment;
import com.mxgraph.layout.hierarchical.stage.mxHierarchicalLayoutStage;
import com.mxgraph.layout.hierarchical.stage.mxMedianHybridCrossingReduction;
import com.mxgraph.layout.hierarchical.stage.mxMinimumCycleRemover;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.view.mxGraph;

/**
* The top level compound layout of the hierarchical layout. The individual
* elements of the layout are called in sequence.
*/
public class mxHierarchicalLayout extends mxGraphLayout/*,
JGraphLayout.Stoppable*/
{
  /** The root nodes of the layout */
  protected List<Object> roots = null;

  /**
   * Specifies if the parent should be resized after the layout so that it
   * contains all the child cells. Default is false. @See parentBorder.
   */
  protected boolean resizeParent = false;

  /**
   * Specifies if the parnent should be moved if resizeParent is enabled.
   * Default is false. @See resizeParent.
   */
  protected boolean moveParent = false;

  /**
   * The border to be added around the children if the parent is to be
   * resized using resizeParent. Default is 0. @See resizeParent.
   */
  protected int parentBorder = 0;

  /**
   * The spacing buffer added between cells on the same layer
   */
  protected double intraCellSpacing = 30.0;

  /**
   * The spacing buffer added between cell on adjacent layers
   */
  protected double interRankCellSpacing = 50.0;

  /**
   * The spacing buffer between unconnected hierarchies
   */
  protected double interHierarchySpacing = 60.0;

  /**
   * The distance between each parallel edge on each ranks for long edges
   */
  protected double parallelEdgeSpacing = 10.0;

  /**
   * The position of the root node(s) relative to the laid out graph in.
   * Default is <code>SwingConstants.NORTH</code>, i.e. top-down.
   */
  protected int orientation = SwingConstants.NORTH;

  /**
   *  Specifies if the STYLE_NOEDGESTYLE flag should be set on edges that are
   * modified by the result. Default is true.
   */
  protected boolean disableEdgeStyle = true;

  /**
   * Whether or not to perform local optimisations and iterate multiple times
   * through the algorithm
   */
  protected boolean fineTuning = true;

  /**
   * Whether or not cells are ordered according to the order in the graph
   * model. Defaults to false since sorting usually produces quadratic
   * performance. Note that since mxGraph returns edges in a deterministic
   * order, it might be that this layout is always deterministic using that
   * JGraph regardless of this flag setting (i.e. leave it false in that
   * case). Default is true.
   */
  protected boolean deterministic;

  /**
   * Whether or not to fix the position of the root cells. Keep in mind to
   * turn off features such as move to origin when fixing the roots, move
   * to origin usually overrides this flag (in JGraph it does).
   */
  protected boolean fixRoots = false;

  /**
   * Whether or not the initial scan of the graph to determine the layer
   * assigned to each vertex starts from the sinks or source (the sinks
   * being vertices with the fewest, preferable zero, outgoing edges and
   * sources same with incoming edges). Starting from either direction
   * can tight the layout up and also produce better results for certain
   * types of graphs. If the result for the default is not good enough
   * try a few sample layouts with the value false to see if they improve
   */
  protected boolean layoutFromSinks = true;

  /**
   * The internal model formed of the layout
   */
  protected mxGraphHierarchyModel model = null;

  /**
   * The layout progress bar
   */
  //protected JGraphLayoutProgress progress = new JGraphLayoutProgress();
  /** The logger for this class */
  private static Logger logger = Logger
      .getLogger("com.jgraph.layout.hierarchical.JGraphHierarchicalLayout");

  /**
   * Constructs a hierarchical layout
   * @param graph the graph to lay out
   *
   */
  public mxHierarchicalLayout(mxGraph graph)
  {
    this(graph, SwingConstants.NORTH);
  }

  /**
   * Constructs a hierarchical layout
   * @param graph the graph to lay out
   * @param orientation <code>SwingConstants.NORTH, SwingConstants.EAST, SwingConstants.SOUTH</code> or <code> SwingConstants.WEST</code>
   *
   */
  public mxHierarchicalLayout(mxGraph graph, int orientation)
  {
    super(graph);
    this.orientation = orientation;
  }

  /**
   * Returns the model for this layout algorithm.
   */
  public mxGraphHierarchyModel getModel()
  {
    return model;
  }

  /**
   * Executes the layout for the children of the specified parent.
   *
   * @param parent Parent cell that contains the children to be laid out.
   */
  public void execute(Object parent)
  {
    execute(parent, null);
  }

  /**
   * Executes the layout for the children of the specified parent.
   *
   * @param parent Parent cell that contains the children to be laid out.
   * @param roots the starting roots of the layout
   */
  public void execute(Object parent, List<Object> roots)
  {
    if (roots == null)
    {
      roots = graph.findTreeRoots(parent);
    }

    this.roots = roots;
    mxIGraphModel model = graph.getModel();

    model.beginUpdate();
    try
    {
      run(parent);

      if (isResizeParent() && !graph.isCellCollapsed(parent))
      {
        graph.updateGroupBounds(new Object[] { parent },
            getParentBorder(), isMoveParent());
      }
    }
    finally
    {
      model.endUpdate();
    }
  }

  /**
   * The API method used to exercise the layout upon the graph description
   * and produce a separate description of the vertex position and edge
   * routing changes made.
   */
  public void run(Object parent)
  {
    // Separate out unconnected hierarchies
    List<Set<Object>> hierarchyVertices = new ArrayList<Set<Object>>();

    // Keep track of one root in each hierarchy in case it's fixed position
    List<Object> fixedRoots = null;
    List<Point2D> rootLocations = null;
    List<Set<Object>> affectedEdges = null;

    if (fixRoots)
    {
      fixedRoots = new ArrayList<Object>();
      rootLocations = new ArrayList<Point2D>();
      affectedEdges = new ArrayList<Set<Object>>();
    }

    for (int i = 0; i < roots.size(); i++)
    {
      // First check if this root appears in any of the previous vertex
      // sets
      boolean newHierarchy = true;
      Iterator<Set<Object>> iter = hierarchyVertices.iterator();

      while (newHierarchy && iter.hasNext())
      {
        if (iter.next().contains(roots.get(i)))
        {
          newHierarchy = false;
        }
      }

      if (newHierarchy)
      {
        // Obtains set of vertices connected to this root
        Stack<Object> cellsStack = new Stack<Object>();
        cellsStack.push(roots.get(i));
        Set<Object> edgeSet = null;

        if (fixRoots)
        {
          fixedRoots.add(roots.get(i));
          Point2D location = getVertexBounds(roots.get(i)).getPoint();
          rootLocations.add(location);
          edgeSet = new HashSet<Object>();
        }

        Set<Object> vertexSet = new HashSet<Object>();

        while (!cellsStack.isEmpty())
        {
          Object cell = cellsStack.pop();

          if (!vertexSet.contains(cell))
          {
            vertexSet.add(cell);

            if (fixRoots)
            {
              edgeSet.addAll(Arrays.asList(graph
                  .getIncomingEdges(cell, parent)));
            }

            Object[] conns = graph.getConnections(cell, parent);
            Object[] cells = graph.getOpposites(conns, cell);

            for (int j = 0; j < cells.length; j++)
            {
              if (!vertexSet.contains(cells[j]))
              {
                cellsStack.push(cells[j]);
              }
            }
          }
        }

        hierarchyVertices.add(vertexSet);

        if (fixRoots)
        {
          affectedEdges.add(edgeSet);
        }
      }
    }

    // Perform a layout for each seperate hierarchy
    // Track initial coordinate x-positioning
    double initialX = 0;
    Iterator<Set<Object>> iter = hierarchyVertices.iterator();
    int i = 0;

    while (iter.hasNext())
    {
      Set<Object> vertexSet = iter.next();

      model = new mxGraphHierarchyModel(this, vertexSet.toArray(), roots,
          parent, false, deterministic, layoutFromSinks);

      cycleStage(parent);
      layeringStage();
      crossingStage(parent);
      initialX = placementStage(initialX, parent);

      if (fixRoots)
      {
        // Reposition roots and their hierarchies using their bounds
        // stored earlier
        Object root = fixedRoots.get(i);
        Point2D oldLocation = rootLocations.get(i);
        Point2D newLocation = graph.getModel().getGeometry(root)
            .getPoint();

        double diffX = oldLocation.getX() - newLocation.getX();
        double diffY = oldLocation.getY() - newLocation.getY();
        graph.moveCells(vertexSet.toArray(), diffX, diffY);

        // Also translate connected edges
        Set<Object> connectedEdges = affectedEdges.get(i++);
        graph.moveCells(connectedEdges.toArray(), diffX, diffY);
      }
    }
  }

  /**
   * Executes the cycle stage. This implementation uses the
   * mxMinimumCycleRemover.
   */
  public void cycleStage(Object parent)
  {
    mxHierarchicalLayoutStage cycleStage = new mxMinimumCycleRemover(this);
    cycleStage.execute(parent);
  }

  /**
   * Implements first stage of a Sugiyama layout.
   */
  public void layeringStage()
  {
    model.initialRank();
    model.fixRanks();
  }

  /**
   * Executes the crossing stage using mxMedianHybridCrossingReduction.
   */
  public void crossingStage(Object parent)
  {
    mxHierarchicalLayoutStage crossingStage = new mxMedianHybridCrossingReduction(
        this);
    crossingStage.execute(parent);
  }

  /**
   * Executes the placement stage using mxCoordinateAssignment.
   */
  public double placementStage(double initialX, Object parent)
  {
    mxCoordinateAssignment placementStage = new mxCoordinateAssignment(
        this, intraCellSpacing, interRankCellSpacing, orientation,
        initialX, parallelEdgeSpacing);
    placementStage.setFineTuning(fineTuning);
    placementStage.execute(parent);

    return placementStage.getLimitX() + interHierarchySpacing;
  }

  /**
   * Returns the resizeParent flag.
   */
  public boolean isResizeParent()
  {
    return resizeParent;
  }

  /**
   * Sets the resizeParent flag.
   */
  public void setResizeParent(boolean value)
  {
    resizeParent = value;
  }

  /**
   * Returns the moveParent flag.
   */
  public boolean isMoveParent()
  {
    return moveParent;
  }

  /**
   * Sets the moveParent flag.
   */
  public void setMoveParent(boolean value)
  {
    moveParent = value;
  }

  /**
   * Returns parentBorder.
   */
  public int getParentBorder()
  {
    return parentBorder;
  }

  /**
   * Sets parentBorder.
   */
  public void setParentBorder(int value)
  {
    parentBorder = value;
  }

  /**
   * @return Returns the intraCellSpacing.
   */
  public double getIntraCellSpacing()
  {
    return intraCellSpacing;
  }

  /**
   * @param intraCellSpacing
   *            The intraCellSpacing to set.
   */
  public void setIntraCellSpacing(double intraCellSpacing)
  {
    this.intraCellSpacing = intraCellSpacing;
  }

  /**
   * @return Returns the interRankCellSpacing.
   */
  public double getInterRankCellSpacing()
  {
    return interRankCellSpacing;
  }

  /**
   * @param interRankCellSpacing
   *            The interRankCellSpacing to set.
   */
  public void setInterRankCellSpacing(double interRankCellSpacing)
  {
    this.interRankCellSpacing = interRankCellSpacing;
  }

  /**
   * @return Returns the orientation.
   */
  public int getOrientation()
  {
    return orientation;
  }

  /**
   * @param orientation
   *            The orientation to set.
   */
  public void setOrientation(int orientation)
  {
    this.orientation = orientation;
  }

  /**
   * @return Returns the interHierarchySpacing.
   */
  public double getInterHierarchySpacing()
  {
    return interHierarchySpacing;
  }

  /**
   * @param interHierarchySpacing
   *            The interHierarchySpacing to set.
   */
  public void setInterHierarchySpacing(double interHierarchySpacing)
  {
    this.interHierarchySpacing = interHierarchySpacing;
  }

  public double getParallelEdgeSpacing()
  {
    return parallelEdgeSpacing;
  }

  public void setParallelEdgeSpacing(double parallelEdgeSpacing)
  {
    this.parallelEdgeSpacing = parallelEdgeSpacing;
  }

  /**
   * @return Returns the fineTuning.
   */
  public boolean isFineTuning()
  {
    return fineTuning;
  }

  /**
   * @param fineTuning
   *            The fineTuning to set.
   */
  public void setFineTuning(boolean fineTuning)
  {
    this.fineTuning = fineTuning;
  }

  /**
   *
   */
  public boolean isDisableEdgeStyle()
  {
    return disableEdgeStyle;
  }

  /**
   *
   * @param disableEdgeStyle
   */
  public void setDisableEdgeStyle(boolean disableEdgeStyle)
  {
    this.disableEdgeStyle = disableEdgeStyle;
  }

  /**
   * @return Returns the deterministic.
   */
  public boolean isDeterministic()
  {
    return deterministic;
  }

  /**
   * @param deterministic The deterministic to set.
   */
  public void setDeterministic(boolean deterministic)
  {
    this.deterministic = deterministic;
  }

  /**
   * @return Returns the fixRoots.
   */
  public boolean isFixRoots()
  {
    return fixRoots;
  }

  /**
   * @param fixRoots The fixRoots to set.
   */
  public void setFixRoots(boolean fixRoots)
  {
    this.fixRoots = fixRoots;
  }

  public boolean isLayoutFromSinks()
  {
    return layoutFromSinks;
  }

  public void setLayoutFromSinks(boolean layoutFromSinks)
  {
    this.layoutFromSinks = layoutFromSinks;
  }

  /**
   * Sets the logging level of this class
   * @param level the logging level to set
   */
  public void setLoggerLevel(Level level)
  {
    try
    {
      logger.setLevel(level);
    }
    catch (SecurityException e)
    {
      // Probably running in an applet
    }
  }

  /**
   * Returns <code>Hierarchical</code>, the name of this algorithm.
   */
  public String toString()
  {
    return "Hierarchical";
  }

}
TOP

Related Classes of com.mxgraph.layout.hierarchical.mxHierarchicalLayout

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.