use of org.eclipse.elk.alg.layered.graph.LNode in project elk by eclipse.
the class InteractiveLayerer method process.
@Override
public void process(final LGraph layeredGraph, final IElkProgressMonitor monitor) {
monitor.begin("Interactive node layering", 1);
// create layers with a start and an end position, merging when they overlap with others
List<LayerSpan> currentSpans = Lists.newArrayList();
for (LNode node : layeredGraph.getLayerlessNodes()) {
double minx = node.getPosition().x;
double maxx = minx + node.getSize().x;
// for the following code we have to guarantee that every node has a width,
// which, for instance, might not be the case for external dummy nodes
maxx = Math.max(minx + 1, maxx);
// look for a position in the sorted list where the node can be inserted
ListIterator<LayerSpan> spanIter = currentSpans.listIterator();
LayerSpan foundSpan = null;
while (spanIter.hasNext()) {
LayerSpan span = spanIter.next();
if (span.start >= maxx) {
// the next layer span is further right, so insert the node here
spanIter.previous();
break;
} else if (span.end > minx) {
// the layer span has an intersection with the node
if (foundSpan == null) {
// add the node to the current layer span
span.nodes.add(node);
span.start = Math.min(span.start, minx);
span.end = Math.max(span.end, maxx);
foundSpan = span;
} else {
// merge the previously found layer span with the current one
foundSpan.nodes.addAll(span.nodes);
foundSpan.end = Math.max(foundSpan.end, span.end);
spanIter.remove();
}
}
}
if (foundSpan == null) {
// no intersecting span was found, so create a new one
foundSpan = new LayerSpan();
foundSpan.start = minx;
foundSpan.end = maxx;
spanIter.add(foundSpan);
foundSpan.nodes.add(node);
}
}
// create real layers from the layer spans
List<Layer> layers = layeredGraph.getLayers();
int nextIndex = 0;
for (LayerSpan span : currentSpans) {
Layer layer = new Layer(layeredGraph);
layer.id = nextIndex++;
layers.add(layer);
for (LNode node : span.nodes) {
node.setLayer(layer);
node.id = 0;
}
}
// correct the layering respecting the graph topology, so edges point from left to right
for (LNode node : layeredGraph.getLayerlessNodes()) {
if (node.id == 0) {
checkNode(node, layeredGraph);
}
}
// remove empty layers, which can happen when the layering has to be corrected
ListIterator<Layer> layerIterator = layers.listIterator();
while (layerIterator.hasNext()) {
if (layerIterator.next().getNodes().isEmpty()) {
layerIterator.remove();
}
}
// clear the list of nodes that have no layer, since now they all have one
layeredGraph.getLayerlessNodes().clear();
monitor.done();
}
use of org.eclipse.elk.alg.layered.graph.LNode in project elk by eclipse.
the class SweepCopy method assertCorrectPortSides.
/**
* Corrects the {@link PortSide} of dummy's origin.
* @return The {@link LNode} ('origin') whose port {@code dummy} represents.
*/
private LNode assertCorrectPortSides(final LNode dummy) {
assert dummy.getType() == NodeType.NORTH_SOUTH_PORT;
LNode origin = dummy.getProperty(InternalProperties.IN_LAYER_LAYOUT_UNIT);
// a north south port dummy has exactly one port
List<LPort> dummyPorts = dummy.getPorts();
LPort dummyPort = dummyPorts.get(0);
// find the corresponding port on the regular node
for (LPort port : origin.getPorts()) {
if (port.equals(dummyPort.getProperty(InternalProperties.ORIGIN))) {
// switch the port's side if necessary
if ((port.getSide() == PortSide.NORTH) && (dummy.id > origin.id)) {
port.setSide(PortSide.SOUTH);
if (port.isExplicitlySuppliedPortAnchor()) {
// Set new coordinates for port anchor since it was switched from NORTH to SOUTH.
// The y coordinate is updated by mirroring the y coordinate
double portHeight = port.getSize().y;
double anchorY = port.getAnchor().y;
port.getAnchor().y = portHeight - anchorY;
}
} else if ((port.getSide() == PortSide.SOUTH) && (origin.id > dummy.id)) {
port.setSide(PortSide.NORTH);
if (port.isExplicitlySuppliedPortAnchor()) {
// Set new coordinates for port anchor since it was switched from NORTH to SOUTH.
// The y coordinate is updated by mirroring the y coordinate
double portHeight = port.getSize().y;
double anchorY = port.getAnchor().y;
port.getAnchor().y = -(portHeight - anchorY);
}
}
break;
}
}
return origin;
}
use of org.eclipse.elk.alg.layered.graph.LNode in project elk by eclipse.
the class AllCrossingsCounter method initAtNodeLevel.
@Override
public void initAtNodeLevel(final int l, final int n, final LNode[][] nodeOrder) {
LNode node = nodeOrder[l][n];
hasNorthSouthPorts[l] |= node.getType() == NodeType.NORTH_SOUTH_PORT;
}
use of org.eclipse.elk.alg.layered.graph.LNode in project elk by eclipse.
the class CrossingsCounter method initPositionsForNorthSouthCounting.
private List<LPort> initPositionsForNorthSouthCounting(final LNode[] nodes) {
final List<LPort> ports = Lists.newArrayList();
final Deque<LNode> stack = new ArrayDeque<>();
LNode lastLayoutUnit = null;
int index = 0;
for (int i = 0; i < nodes.length; ++i) {
LNode current = nodes[i];
if (isLayoutUnitChanged(lastLayoutUnit, current)) {
// work the stack (filled with southern dummies)
index = emptyStack(stack, ports, STACK_SIDE, index);
}
if (current.hasProperty(InternalProperties.IN_LAYER_LAYOUT_UNIT)) {
lastLayoutUnit = current.getProperty(InternalProperties.IN_LAYER_LAYOUT_UNIT);
}
switch(current.getType()) {
// what we consider normal
case NORMAL:
// index the northern ports west-to-east
for (LPort p : getNorthSouthPortsWithIncidentEdges(current, PortSide.NORTH)) {
portPositions[p.id] = index++;
ports.add(p);
}
// work the stack (filled with northern dummies)
index = emptyStack(stack, ports, STACK_SIDE, index);
// index the southern ports in regular clock-wise order
for (LPort p : getNorthSouthPortsWithIncidentEdges(current, PortSide.SOUTH)) {
portPositions[p.id] = index++;
ports.add(p);
}
break;
case NORTH_SOUTH_PORT:
if (!current.getPortSideView(INDEXING_SIDE).isEmpty()) {
// should be only one
LPort p = current.getPortSideView(INDEXING_SIDE).get(0);
portPositions[p.id] = index++;
ports.add(p);
}
if (!current.getPortSideView(STACK_SIDE).isEmpty()) {
stack.push(current);
}
break;
case LONG_EDGE:
for (LPort p : current.getPortSideView(PortSide.WEST)) {
portPositions[p.id] = index++;
ports.add(p);
}
current.getPortSideView(PortSide.EAST).forEach(p -> stack.push(current));
break;
// nothing to do here
default:
}
}
// are there any southern dummy nodes left on the stack?
emptyStack(stack, ports, STACK_SIDE, index);
return ports;
}
use of org.eclipse.elk.alg.layered.graph.LNode in project elk by eclipse.
the class CrossingsCounter method countNorthSouthCrossingsOnPorts.
private int countNorthSouthCrossingsOnPorts(final Iterable<LPort> ports) {
int crossings = 0;
final List<Pair<LPort, Integer>> targetsAndDegrees = Lists.newArrayList();
for (LPort port : ports) {
indexTree.removeAll(positionOf(port));
targetsAndDegrees.clear();
// which is a bit tedious since north/south ports have no physical edge within the graph at this point
switch(port.getNode().getType()) {
case NORMAL:
LNode dummy = (LNode) port.getProperty(InternalProperties.PORT_DUMMY);
// guarded in #initPositionsForNorthSouthCounting(...)
assert dummy != null;
// western and eastern
dummy.getPorts().forEach(p -> targetsAndDegrees.add(Pair.of(p, p.getDegree())));
break;
case LONG_EDGE:
port.getNode().getPorts().stream().filter(p -> p != port).findFirst().ifPresent(p -> targetsAndDegrees.add(Pair.of(p, p.getDegree())));
break;
case NORTH_SOUTH_PORT:
LPort dummyPort = (LPort) port.getProperty(InternalProperties.ORIGIN);
targetsAndDegrees.add(Pair.of(dummyPort, port.getDegree()));
break;
}
// First get crossings for all edges.
for (Pair<LPort, Integer> targetAndDegree : targetsAndDegrees) {
int endPosition = positionOf(targetAndDegree.getFirst());
if (endPosition > positionOf(port)) {
crossings += indexTree.rank(endPosition) * targetAndDegree.getSecond();
ends.push(endPosition);
}
}
// Then add end points.
while (!ends.isEmpty()) {
indexTree.add(ends.pop());
}
}
return crossings;
}
Aggregations