This project has moved and is read-only. For the latest updates, please go here.

Automatic multi-level layout

Sep 30, 2014 at 8:53 PM

Is there possible (and what is the proposed approach) to create multi-level layouter where shapes in each group would be layouted in bottom-to-top approach where shapes aggregated in child group would not be separately layouted again?

For example, I have diagram which has two groups (A and B) and two shapes (S1 and S2), each group has two groups (A1/A2 and B1/B2) with five shapes. The layouter would:
  1. Create layout of five shapes in a groups A1/A2/A3/A4.
  2. Fix the bounds of all groups
  3. Create layout of two groups and two shapes
Any hints how to achieve that?

Oct 1, 2014 at 8:40 AM
Edited Oct 1, 2014 at 8:45 AM
  1. You can set the shapes that should be layouted via the ILayouter.Shapes property and
  2. the layouter has a method for fitting these shapes into a specified rectangle: ILayouter.Fit(...)
  3. You can and should combine several layouters to get a good result.
  4. Use the NShapeDesigner to play around with settings and layouter combinations.
Example (not related to your example, just for getting an idea how it works):
private static void ExecuteLayouter(ILayouter layouter, Int32 timeout) {

private void ExecuteCommand(AggregatedCommand aggregatedCommand, ICommand command) {

private void LayoutShapes() {
    const int stepTimeout = 10;
    // Layout only the selected shapes
    IEnumerable<Shape> allShapes = display.Diagram.Shapes;
    IEnumerable<Shape> shapesToLayout = display.SelectedShapes;

    // Aggregated command for executing the 4 layouting steps at once
    AggregatedCommand aggregatedCommand = new AggregatedCommand(project.Repository);

    // Align shapes
    GridLayouter gridLayouter = new GridLayouter(project);
    gridLayouter.CoarsenessX = 15;
    gridLayouter.CoarsenessY = 15;
    gridLayouter.AllShapes = allShapes;
    gridLayouter.Shapes = shapesToLayout;
    ExecuteLayouter(gridLayouter, stepTimeout);
    ExecuteCommand(aggregatedCommand, gridLayouter.CreateLayoutCommand());

    // Compact shapes (avoid growing diagrams)
    ExpansionLayouter expansionLayouter = new ExpansionLayouter(project);
    expansionLayouter.HorizontalCompression = 50;
    expansionLayouter.VerticalCompression = 50;
    expansionLayouter.AllShapes = allShapes;
    expansionLayouter.Shapes = shapesToLayout;
    ExecuteLayouter(expansionLayouter, stepTimeout);
    ExecuteCommand(aggregatedCommand, expansionLayouter.CreateLayoutCommand());

    RepulsionLayouter repulsionLayouter = new RepulsionLayouter(project);
    repulsionLayouter.Friction = 10;
    repulsionLayouter.Repulsion = 10;
    repulsionLayouter.RepulsionRange = 400;
    repulsionLayouter.SpringRate = 9;
    repulsionLayouter.Mass = 100;
    repulsionLayouter.AllShapes = allShapes;
    repulsionLayouter.Shapes = shapesToLayout;
    ExecuteLayouter(repulsionLayouter, stepTimeout);
    ExecuteCommand(aggregatedCommand, repulsionLayouter.CreateLayoutCommand());

    // Expand shapes (avoid overlapping shapes in large diagrams)
    expansionLayouter.HorizontalCompression = 200;
    expansionLayouter.VerticalCompression = 200;
    expansionLayouter.AllShapes = allShapes;
    expansionLayouter.Shapes = shapesToLayout;
    ExecuteLayouter(expansionLayouter, stepTimeout);
    ExecuteCommand(aggregatedCommand, expansionLayouter.CreateLayoutCommand());

    // Layout only shapes created from template "MyTemplate"
    List<Shape> myShapes = new List<Shape>();
    foreach (Shape shape in shapesToLayout)
    if (shape.Template.Name == "MyTemplate")

    // Align the shapes
    gridLayouter.CoarsenessX = 100;
    gridLayouter.CoarsenessY = 50;
    gridLayouter.AllShapes = allShapes;
    gridLayouter.Shapes = myShapes;
    ExecuteLayouter(gridLayouter, stepTimeout);
    ExecuteCommand(aggregatedCommand, gridLayouter.CreateLayoutCommand());

    // Add aggregated command to the history. 
    // Do not execute it as each step was executed before.