use of org.eclipse.elk.core.options.Direction in project elk by eclipse.
the class NodeLabelAndSizeCalculator method process.
/**
* Processes the given node which is assumed to be a child of the given graph. Note that this method does not check
* whether or not this is the case. The worst that can happen, however, is that wrong spacing values are applied
* during processing.
*
* @param graph
* the node's parent graph.
* @param node
* the node to process.
* @param applyStuff
* {@code true} if the node should actually be resized and have its ports and labels positioned,
* {@code false} if we should only return the size that would be applied.
* @param ignoreInsidePortLabels
* if {@code true}, we don't place port labels that should be placed inside. This is usually used in
* conjunction with {@code applyStuff} by layout algorithms that want to get a lower bound on a
* hierarchical node's size, but that handle inside port labels themselves.
* @return the node's size that was or would be applied.
*/
public static KVector process(final GraphAdapter<?> graph, final NodeAdapter<?> node, final boolean applyStuff, final boolean ignoreInsidePortLabels) {
// Note that, upon Miro's request, each phase of the algorithm was given a code name in the first version of
// this code. We happily carry on fulfilling this request in this, the second version.
/* PREPARATORY PREPARATIONS
*
* Create the context objects that hold all of the information relevant to our calculations, including pointers
* to all the components of the cell system. The different method calls will often just update information in
* the context object (or nested objects) that subsequent method calls will make use of. Creating the port
* contexts will also create label cells for each port that has labels.
*/
NodeContext nodeContext = new NodeContext(graph, node);
PortContextCreator.createPortContexts(nodeContext, ignoreInsidePortLabels);
/* PHASE 1: WONDEROUS WATERFOWL
* Setup All Cells
*
* Create all the label cells that will hold node labels, as well as the cell containers that will hold them,
* for both inside and outside node labels. Also, assign node labels to the relevant label cells. If port
* labels are to be placed on the inside, setup the inside port label cells with the appropriate paddings, and
* set the width of the eastern and western ones to the maximum width of the labels they will contain. We can't
* do that for the northern and southern cells yet because port label placement is more complicated there.
*/
boolean horizontalLayoutMode = true;
// use horizontal layout mode (which yields vertically stacked labels).
if (graph != null && graph.hasProperty(CoreOptions.DIRECTION)) {
final Direction layoutDirection = graph.getProperty(CoreOptions.DIRECTION);
horizontalLayoutMode = layoutDirection == Direction.UNDEFINED || layoutDirection.isHorizontal();
}
NodeLabelCellCreator.createNodeLabelCells(nodeContext, false, horizontalLayoutMode);
InsidePortLabelCellCreator.createInsidePortLabelCells(nodeContext);
/* PHASE 2: DEFECTIVE DUCK
* Setup Client Area Space and Node Cell Padding
*
* Apply the minimum client area size to the central grid container cell. Also, reserve space for ports that
* extend inside the node due to negative port border offsets by setting up appropriate paddings on the main
* node container cell.
*/
NodeLabelAndSizeUtilities.setupMinimumClientAreaSize(nodeContext);
NodeLabelAndSizeUtilities.setupNodePaddingForPortsWithOffset(nodeContext);
/* PHASE 3: SALVAGEABLE SWAN
* Minimum Space Required to Place Ports
*
* It is now time to find out how large the node needs to be if all ports are to be placed in a way that
* satisfies all spacing constraints. This may or may not include the labels of ports. We remember these
* information by setting the minimum width of north / south inside port label cells and the minimum height of
* east / west inside port label cells. Since the east / west cells are surrounded by the north / south cells,
* their height may be updated later once we know how high the north / south cells will be.
*/
HorizontalPortPlacementSizeCalculator.calculateHorizontalPortPlacementSize(nodeContext);
VerticalPortPlacementSizeCalculator.calculateVerticalPortPlacementSize(nodeContext);
/* PHASE 4: DAMNABLE DUCKLING
* Setup Cell System Size Contribution Flags
*
* Depending on the size constraints, the different cells may contribute to the height or to the width of the
* node. In this phase, we setup the size contribution flags according to the size constraints. This lays the
* groundwork for letting the cell system calculate stuff.
*/
CellSystemConfigurator.configureCellSystemSizeContributions(nodeContext);
/* PHASE 5: DUCK AND COVER
* Set Node Width and Place Horizontal Ports
*
* We can now set the node's width and place the ports (and port labels) along the horizontal sides. Since we
* have no idea how high the node is going to be yet, we place southern ports with the assumption that the
* node has a height of 0. We will later have to offset those ports by the node's height. Setting the node
* width has the side effect of computing a horizontal layout for the cell system.
*/
NodeSizeCalculator.setNodeWidth(nodeContext);
PortPlacementCalculator.placeHorizontalPorts(nodeContext);
PortLabelPlacementCalculator.placeHorizontalPortLabels(nodeContext);
/* PHASE 6: GIGANTIC GOOSE
* Set Node Height and Place Vertical Ports
*
* We can now calculate the node's height and place the ports (and port labels) along the vertical sides. Also,
* since we now know the node's height, we can finally correct the southern port positions. Before we can do
* all that, however, we might need to update the height and padding of the eastern and western inside port
* label cells to be sure that free ports are positioned properly.
*
* Note that if we are to not apply stuff, we're done once we know the node's height. Which is why we stop at
* that point.
*/
CellSystemConfigurator.updateVerticalInsidePortLabelCellPadding(nodeContext);
NodeSizeCalculator.setNodeHeight(nodeContext);
if (!applyStuff) {
return nodeContext.nodeSize;
}
NodeLabelAndSizeUtilities.offsetSouthernPortsByNodeSize(nodeContext);
PortPlacementCalculator.placeVerticalPorts(nodeContext);
PortLabelPlacementCalculator.placeVerticalPortLabels(nodeContext);
/* PHASE 7: THANKSGIVING
* Place Labels and Apply Stuff
*
* Since we now have the node's final size, we can now calculate the positions of the containers for outer node
* labels and place all inner and outer node labels. Also, the port label cells have positions assigned to them
* and can be told to apply positions to their labels. Finally, we can apply the node's size and all of the
* port positions we have calculated.
*/
LabelPlacer.placeLabels(nodeContext);
NodeLabelAndSizeUtilities.setNodePadding(nodeContext);
NodeLabelAndSizeUtilities.applyStuff(nodeContext);
// Return the size
return nodeContext.nodeSize;
}
use of org.eclipse.elk.core.options.Direction in project elk by eclipse.
the class CompactionTest method testVeticalSpacings.
/**
* Vertical spacing should be preserved when compaction upwards or downwards.
*/
@Test
public void testVeticalSpacings() {
CGraph graph = new CGraph(EnumSet.allOf(Direction.class));
// test horizontal direction
CTestNodeSpacing one = new CTestNodeSpacing(new ElkRectangle(0, 0, 20, 20), 0d, 5d);
graph.cNodes.add(one);
CTestNodeSpacing two = new CTestNodeSpacing(new ElkRectangle(0, 50, 20, 20), 0d, 7d);
graph.cNodes.add(two);
CTestNodeSpacing three = new CTestNodeSpacing(new ElkRectangle(0, 150, 20, 20), 0d, 10d);
graph.cNodes.add(three);
compacter(graph).changeDirection(Direction.UP).compact().finish();
assertEquals(0, one.hitbox.y, EPSILON);
assertEquals(27, two.hitbox.y, EPSILON);
assertEquals(57, three.hitbox.y, EPSILON);
compacter(graph).changeDirection(Direction.DOWN).compact().finish();
assertEquals(0, one.hitbox.y, EPSILON);
assertEquals(27, two.hitbox.y, EPSILON);
assertEquals(57, three.hitbox.y, EPSILON);
compacter(graph).changeDirection(Direction.UP).compact().finish();
assertEquals(0, one.hitbox.y, EPSILON);
assertEquals(27, two.hitbox.y, EPSILON);
assertEquals(57, three.hitbox.y, EPSILON);
}
use of org.eclipse.elk.core.options.Direction in project elk by eclipse.
the class CompactionTest method testSubsequentDirectionsCompaction.
/* --------------------------------------------------
* Testing subsequent calls with different directions
* -------------------------------------------------- */
@Test
public void testSubsequentDirectionsCompaction() {
CGraph graph = new CGraph(EnumSet.allOf(Direction.class));
CTestNode one = new CTestNode(new ElkRectangle(0, 0, 20, 20));
graph.cNodes.add(one);
CTestNode two = new CTestNode(new ElkRectangle(25, 0, 20, 20));
graph.cNodes.add(two);
CTestNode three = new CTestNode(new ElkRectangle(0, 25, 20, 20));
graph.cNodes.add(three);
CTestNode four = new CTestNode(new ElkRectangle(25, 25, 20, 20));
graph.cNodes.add(four);
Set<Direction> directions = EnumSet.of(Direction.LEFT, Direction.RIGHT, Direction.UP, Direction.DOWN);
// subsequently apply all combinations of four subsequent compaction steps
for (Direction d1 : directions) {
for (Direction d2 : directions) {
for (Direction d3 : directions) {
for (Direction d4 : directions) {
compacter(graph).changeDirection(d1).compact().changeDirection(d2).compact().changeDirection(d3).compact().changeDirection(d4).compact().finish();
// the way we modeled the graph, every node should stay where it is
String currentDirections = d1 + " " + d2 + " " + d3 + " " + d4;
assertEquals(currentDirections, 0, one.hitbox.x, EPSILON);
assertEquals(currentDirections, 0, one.hitbox.y, EPSILON);
assertEquals(currentDirections, 25, two.hitbox.x, EPSILON);
assertEquals(currentDirections, 0, two.hitbox.y, EPSILON);
assertEquals(currentDirections, 0, three.hitbox.x, EPSILON);
assertEquals(currentDirections, 25, three.hitbox.y, EPSILON);
assertEquals(currentDirections, 25, four.hitbox.x, EPSILON);
assertEquals(currentDirections, 25, four.hitbox.y, EPSILON);
}
}
}
}
}
use of org.eclipse.elk.core.options.Direction in project elk by eclipse.
the class CompactionTest method testHorizontalSpacings.
/* --------------------------------------------------
* Testing different kinds of spacings.
* --------------------------------------------------*/
/*
* We support different spacing values between nodes and edges in vertical and horizontal
* direction. Thereby "vertical" and "horizontal" depends on the compaction direction.
* Furthermore, we support individual spacings between any pair of nodes.
*/
/**
* Horizontal spacing should be preserved when compaction leftwards or rightwards.
*/
@Test
public void testHorizontalSpacings() {
CGraph graph = new CGraph(EnumSet.allOf(Direction.class));
// test horizontal direction
CTestNodeSpacing one = new CTestNodeSpacing(new ElkRectangle(0, 0, 20, 20), 5d, 0d);
graph.cNodes.add(one);
CTestNodeSpacing two = new CTestNodeSpacing(new ElkRectangle(50, 0, 20, 20), 7d, 0d);
graph.cNodes.add(two);
CTestNodeSpacing three = new CTestNodeSpacing(new ElkRectangle(150, 0, 20, 20), 10d, 0d);
graph.cNodes.add(three);
compacter(graph).changeDirection(Direction.LEFT).compact().finish();
assertEquals(0, one.hitbox.x, EPSILON);
assertEquals(27, two.hitbox.x, EPSILON);
assertEquals(57, three.hitbox.x, EPSILON);
compacter(graph).changeDirection(Direction.RIGHT).compact().finish();
assertEquals(0, one.hitbox.x, EPSILON);
assertEquals(27, two.hitbox.x, EPSILON);
assertEquals(57, three.hitbox.x, EPSILON);
compacter(graph).changeDirection(Direction.LEFT).compact().finish();
assertEquals(0, one.hitbox.x, EPSILON);
assertEquals(27, two.hitbox.x, EPSILON);
assertEquals(57, three.hitbox.x, EPSILON);
}
use of org.eclipse.elk.core.options.Direction in project elk by eclipse.
the class GraphConfigurator method configureGraphProperties.
/**
* Set special layout options for the layered graph.
*
* @param lgraph a new layered graph
*/
private void configureGraphProperties(final LGraph lgraph) {
// check the bounds of some layout options
// TODO Find a new concept for checking validity of bounds
// lgraph.checkProperties(InternalProperties.SPACING, InternalProperties.BORDER_SPACING,
// Properties.THOROUGHNESS, InternalProperties.ASPECT_RATIO);
double edgeSpacing = lgraph.getProperty(LayeredOptions.SPACING_EDGE_EDGE);
if (edgeSpacing < MIN_EDGE_SPACING) {
// Make sure the resulting edge spacing is at least 2 in order to avoid overlapping edges.
lgraph.setProperty(LayeredOptions.SPACING_EDGE_EDGE, MIN_EDGE_SPACING);
}
Direction direction = lgraph.getProperty(LayeredOptions.DIRECTION);
if (direction == Direction.UNDEFINED) {
lgraph.setProperty(LayeredOptions.DIRECTION, LGraphUtil.getDirection(lgraph));
}
// set the random number generator based on the random seed option
Integer randomSeed = lgraph.getProperty(LayeredOptions.RANDOM_SEED);
if (randomSeed == 0) {
lgraph.setProperty(InternalProperties.RANDOM, new Random());
} else {
lgraph.setProperty(InternalProperties.RANDOM, new Random(randomSeed));
}
Boolean favorStraightness = lgraph.getProperty(LayeredOptions.NODE_PLACEMENT_FAVOR_STRAIGHT_EDGES);
if (favorStraightness == null) {
lgraph.setProperty(LayeredOptions.NODE_PLACEMENT_FAVOR_STRAIGHT_EDGES, lgraph.getProperty(LayeredOptions.EDGE_ROUTING) == EdgeRouting.ORTHOGONAL);
}
// copy the port constraints to keep a list of original port constraints
copyPortContraints(lgraph);
// pre-calculate spacing information
Spacings spacings = new Spacings(lgraph);
lgraph.setProperty(InternalProperties.SPACINGS, spacings);
}
Aggregations