use of org.eclipse.elk.alg.layered.graph.LEdge in project elk by eclipse.
the class NodePromotion method promoteNode.
/**
* Node promotion heuristic of the paper. Works on an array of integers which represents the
* nodes and their position within the layers to avoid difficulties while creating and deleting
* new layers over the course of the discarded promotions.
*
* @param node
* Node that shall be promoted.
* @return A Pair consisting of an Integer and a Boolean.
* <ol>
* <li>The Integer is the estimated difference of dummy nodes after applying the node
* promotion. A negative value indicates a reduction of dummy nodes.</li>
* <li>The Boolean gives information about whether the maximal accepted width has been
* exceeded while promoting the given node or not.</li>
* </ol>
*/
private Pair<Integer, Boolean> promoteNode(final LNode node) {
boolean maxWidthNotExceeded = true;
int dummydiff = 0;
int nodeLayerPos = layers[node.id];
double nodeSize = node.getSize().y + nodeSizeAffix;
int dummiesBuilt = degreeDiff[node.id][2];
// Calculating the current width of the layer the node came from by subtracting its size
// from the size of this layer and adding its outgoing edges to this layer because of the
// then resulting new dummy nodes.
currentWidth.set(nodeLayerPos, currentWidth.get(nodeLayerPos) - 1 + dummiesBuilt);
currentWidthPixel.set(nodeLayerPos, currentWidthPixel.get(nodeLayerPos) - nodeSize + dummiesBuilt * dummySize);
// Calculate index of the layer for the promoted node.
nodeLayerPos++;
if (nodeLayerPos >= maxHeight) {
maxHeight++;
currentWidth.add(1);
currentWidthPixel.add(nodeSize);
} else {
// Calculating the current width of the layer the node is promoted to by adding its size
// to the size of this layer and subtracting its incoming edges to this layer due to the
// resulting potentially reduced dummy nodes. If it's a new layer only the size itself will
// be added to it.
int dummiesReduced = degreeDiff[node.id][1];
currentWidth.set(nodeLayerPos, currentWidth.get(nodeLayerPos) + 1 - dummiesReduced);
currentWidthPixel.set(nodeLayerPos, currentWidthPixel.get(nodeLayerPos) + nodeSize - dummiesReduced * dummySize);
}
// so, the promotion will not be applied.
if ((promotionStrategy == NodePromotionStrategy.NIKOLOV && (currentWidth.get(nodeLayerPos) > maxWidth || currentWidth.get(nodeLayerPos - 1) > maxWidth)) || (promotionStrategy == NodePromotionStrategy.NIKOLOV_PIXEL && (currentWidthPixel.get(nodeLayerPos) > maxWidthPixel || currentWidthPixel.get(nodeLayerPos - 1) > maxWidthPixel))) {
maxWidthNotExceeded = false;
}
// neighboring layer recursively and calculating the difference of dummy nodes.
for (LEdge edge : node.getIncomingEdges()) {
LNode masterNode = edge.getSource().getNode();
if (layers[masterNode.id] == nodeLayerPos) {
Pair<Integer, Boolean> promotion = promoteNode(masterNode);
dummydiff = dummydiff + promotion.getFirst();
maxWidthNotExceeded = maxWidthNotExceeded && promotion.getSecond();
}
}
layers[node.id] = nodeLayerPos;
dummydiff = dummydiff + degreeDiff[node.id][0];
return new Pair<Integer, Boolean>(dummydiff, maxWidthNotExceeded);
}
use of org.eclipse.elk.alg.layered.graph.LEdge in project elk by eclipse.
the class NorthSouthPortPostprocessor method processSelfLoop.
/**
* Reroutes and reconnects the self-loop edge represented by the given dummy.
*
* @param dummy the dummy representing the self-loop edge.
*/
private void processSelfLoop(final LNode dummy) {
// Get the edge and the ports it was originally connected to
LEdge selfLoop = (LEdge) dummy.getProperty(InternalProperties.ORIGIN);
LPort inputPort = dummy.getPorts(PortSide.WEST).iterator().next();
LPort outputPort = dummy.getPorts(PortSide.EAST).iterator().next();
LPort originInputPort = (LPort) inputPort.getProperty(InternalProperties.ORIGIN);
LPort originOutputPort = (LPort) outputPort.getProperty(InternalProperties.ORIGIN);
// Reconnect the edge
selfLoop.setSource(originOutputPort);
selfLoop.setTarget(originInputPort);
// Add two bend points
KVector bendPoint = new KVector(outputPort.getNode().getPosition());
bendPoint.x = originOutputPort.getAbsoluteAnchor().x;
selfLoop.getBendPoints().add(bendPoint);
bendPoint = new KVector(inputPort.getNode().getPosition());
bendPoint.x = originInputPort.getAbsoluteAnchor().x;
selfLoop.getBendPoints().add(bendPoint);
}
use of org.eclipse.elk.alg.layered.graph.LEdge in project elk by eclipse.
the class BreakingPointProcessor method performWrapping.
/**
* Main wrapping procedure, placing the determined chunks one below the other.
*/
private void performWrapping(final LGraph graph) {
final List<Layer> layers = graph.getLayers();
final ListIterator<Layer> layerIt = layers.listIterator();
// add initial empty layer to account for break point start dummies
layerIt.add(new Layer(graph));
// iterate the layers from left to right
// as soon as a layer with a BREAKING POINT start dummy is encountered,
// the nodes of the next layer should be moved to the very first layer
// consequently 'wrapping' the graph
boolean reverse = false;
int idx = 1;
while (layerIt.hasNext()) {
Layer layer = layerIt.next();
Layer newLayer = layers.get(idx);
List<LNode> nodesToMove = Lists.newArrayList(layer.getNodes());
// remember an offset used for adding in-layer dummies later
int offset = nodesToMove.size();
// move the nodes to their new layer
for (LNode n : nodesToMove) {
n.setLayer(newLayer);
}
if (reverse) {
// important to introduce the chains of long edge dummies in reversed order
for (LNode n : Lists.reverse(nodesToMove)) {
for (LEdge e : Lists.newArrayList(n.getIncomingEdges())) {
// reverse the edge
e.reverse(graph, true);
graph.setProperty(InternalProperties.CYCLIC, true);
// insert proper dummy nodes for the newly created long edge
List<LEdge> dummyEdges = CuttingUtils.insertDummies(graph, e, offset);
// ameliorate breaking point info
BPInfo bpi = n.getProperty(InternalProperties.BREAKING_POINT_INFO);
LEdge startInLayerEdge = dummyEdges.get(dummyEdges.size() - 1);
bpi.startInLayerDummy = startInLayerEdge.getSource().getNode();
bpi.startInLayerEdge = startInLayerEdge;
bpi.endInLayerDummy = e.getTarget().getNode();
bpi.endInLayerEdge = e;
}
}
reverse = false;
} else {
if (!nodesToMove.isEmpty()) {
LNode aNode = nodesToMove.get(0);
if (aNode.getType() == NodeType.BREAKING_POINT) {
// next layer should be moved (it contains the breaking point end dummies)
reverse = true;
// start moving nodes to the very first layer
idx = -1;
}
}
}
idx++;
}
// remove old layers that are now empty
ListIterator<Layer> it = graph.getLayers().listIterator();
while (it.hasNext()) {
Layer l = it.next();
if (l.getNodes().isEmpty()) {
it.remove();
}
}
}
use of org.eclipse.elk.alg.layered.graph.LEdge in project elk by eclipse.
the class BreakingPointRemover method remove.
private void remove(final LGraph graph, final BPInfo bpi) {
// assemble the new edge route
KVectorChain newBends = new KVectorChain();
// depending on the edge routing style we have to act differently
switch(edgeRouting) {
// so far, only the intermediate nubsplines have been attached via properties
case SPLINES:
// gather the computed spline information
List<SplineSegment> s1 = bpi.nodeStartEdge.getProperty(InternalProperties.SPLINE_ROUTE_START);
List<SplineSegment> s2 = bpi.startEndEdge.getProperty(InternalProperties.SPLINE_ROUTE_START);
List<SplineSegment> s3 = bpi.originalEdge.getProperty(InternalProperties.SPLINE_ROUTE_START);
List<LEdge> e1 = bpi.nodeStartEdge.getProperty(InternalProperties.SPLINE_EDGE_CHAIN);
List<LEdge> e2 = bpi.startEndEdge.getProperty(InternalProperties.SPLINE_EDGE_CHAIN);
List<LEdge> e3 = bpi.originalEdge.getProperty(InternalProperties.SPLINE_EDGE_CHAIN);
// join them (... and remember to reverse some of the segments)
List<SplineSegment> joinedSegments = Lists.newArrayList();
joinedSegments.addAll(s1);
s2.forEach(s -> s.inverseOrder = true);
joinedSegments.addAll(Lists.reverse(s2));
joinedSegments.addAll(s3);
List<LEdge> joinedEdges = Lists.newArrayList();
joinedEdges.addAll(e1);
joinedEdges.addAll(Lists.reverse(e2));
joinedEdges.addAll(e3);
// transfer the information to the original edge
bpi.originalEdge.setProperty(InternalProperties.SPLINE_ROUTE_START, joinedSegments);
bpi.originalEdge.setProperty(InternalProperties.SPLINE_EDGE_CHAIN, joinedEdges);
bpi.originalEdge.setProperty(InternalProperties.SPLINE_SURVIVING_EDGE, bpi.originalEdge);
// cleanup
bpi.nodeStartEdge.setProperty(InternalProperties.SPLINE_ROUTE_START, null);
bpi.nodeStartEdge.setProperty(InternalProperties.SPLINE_EDGE_CHAIN, null);
bpi.startEndEdge.setProperty(InternalProperties.SPLINE_ROUTE_START, null);
bpi.startEndEdge.setProperty(InternalProperties.SPLINE_EDGE_CHAIN, null);
break;
// note that the positions of bpi.start and bpi.end must be added as bend points
case POLYLINE:
newBends.addAll(bpi.nodeStartEdge.getBendPoints());
newBends.add(bpi.start.getPosition());
newBends.addAll(Lists.reverse(bpi.startEndEdge.getBendPoints()));
newBends.add(bpi.end.getPosition());
newBends.addAll(bpi.originalEdge.getBendPoints());
break;
// of bpi.start and bpi.end can be dropped since they lie on a straight line
default:
// ORTHOGONAL
newBends.addAll(bpi.nodeStartEdge.getBendPoints());
newBends.addAll(Lists.reverse(bpi.startEndEdge.getBendPoints()));
newBends.addAll(bpi.originalEdge.getBendPoints());
}
// restore original edge
bpi.originalEdge.getBendPoints().clear();
bpi.originalEdge.getBendPoints().addAll(newBends);
bpi.originalEdge.setSource(bpi.nodeStartEdge.getSource());
// collect any created junction points (order can be arbitrary)
KVectorChain junctionPointsOne = bpi.nodeStartEdge.getProperty(LayeredOptions.JUNCTION_POINTS);
KVectorChain junctionPointsTwo = bpi.startEndEdge.getProperty(LayeredOptions.JUNCTION_POINTS);
KVectorChain junctionPointsThree = bpi.originalEdge.getProperty(LayeredOptions.JUNCTION_POINTS);
if (junctionPointsOne != null || junctionPointsTwo != null || junctionPointsThree != null) {
KVectorChain newJunctionPoints = new KVectorChain();
addNullSafe(newJunctionPoints, junctionPointsThree);
addNullSafe(newJunctionPoints, junctionPointsTwo);
addNullSafe(newJunctionPoints, junctionPointsOne);
bpi.originalEdge.setProperty(LayeredOptions.JUNCTION_POINTS, newJunctionPoints);
}
// remove all the dummy stuff
bpi.startEndEdge.setSource(null);
bpi.startEndEdge.setTarget(null);
bpi.nodeStartEdge.setSource(null);
bpi.nodeStartEdge.setTarget(null);
bpi.end.setLayer(null);
bpi.start.setLayer(null);
if (bpi.prev != null) {
remove(graph, bpi.prev);
}
}
use of org.eclipse.elk.alg.layered.graph.LEdge in project elk by eclipse.
the class NorthSouthPortPreprocessor method createDummyNode.
/**
* Creates a dummy node for the given ports. Edges going into {@code inPort} are rerouted to the
* dummy node's input port. Edges leaving the {@code outPort} are rerouted to the dummy node's
* output port. Both arguments may refer to the same port. The dummy's port have their
* {@code ORIGIN} property set to the port whose edges have been rerouted to them.
*
* @param layeredGraph
* the layered graph.
* @param inPort
* the input port whose edges to reroute. May be {@code null}.
* @param outPort
* the output port whose edges to reroute. May be {@code null}.
* @param dummyNodes
* list the created dummy node should be added to.
* @return the created dummy node.
*/
private LNode createDummyNode(final LGraph layeredGraph, final LPort inPort, final LPort outPort, final List<LNode> dummyNodes) {
LNode dummy = new LNode(layeredGraph);
dummy.setType(NodeType.NORTH_SOUTH_PORT);
dummy.setProperty(LayeredOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_POS);
int crossingHint = 0;
// Input port
if (inPort != null) {
// The port is expected to have edges connected to it
assert !(inPort.getIncomingEdges().isEmpty() && inPort.getOutgoingEdges().isEmpty());
LPort dummyInputPort = new LPort();
dummyInputPort.setProperty(InternalProperties.ORIGIN, inPort);
dummy.setProperty(InternalProperties.ORIGIN, inPort.getNode());
dummyInputPort.setSide(PortSide.WEST);
dummyInputPort.setNode(dummy);
// Reroute edges
LEdge[] edgeArray = LGraphUtil.toEdgeArray(inPort.getIncomingEdges());
for (LEdge edge : edgeArray) {
edge.setTarget(dummyInputPort);
}
// Make sure the inPort knows about the dummy node
inPort.setProperty(InternalProperties.PORT_DUMMY, dummy);
crossingHint++;
}
// Output port
if (outPort != null) {
// The port is expected to have edges connected to it
assert !(outPort.getIncomingEdges().isEmpty() && outPort.getOutgoingEdges().isEmpty());
LPort dummyOutputPort = new LPort();
dummy.setProperty(InternalProperties.ORIGIN, outPort.getNode());
dummyOutputPort.setProperty(InternalProperties.ORIGIN, outPort);
dummyOutputPort.setSide(PortSide.EAST);
dummyOutputPort.setNode(dummy);
// Reroute edges
LEdge[] edgeArray = LGraphUtil.toEdgeArray(outPort.getOutgoingEdges());
for (LEdge edge : edgeArray) {
edge.setSource(dummyOutputPort);
}
// Make sure the outPort knows about the dummy node
outPort.setProperty(InternalProperties.PORT_DUMMY, dummy);
crossingHint++;
}
// Set the crossing hint used for cross counting later
dummy.setProperty(InternalProperties.CROSSING_HINT, crossingHint);
dummyNodes.add(dummy);
return dummy;
}
Aggregations