use of org.eclipse.elk.alg.layered.graph.LLabel in project elk by eclipse.
the class InvertedPortProcessor method createWestPortSideDummies.
/**
* Creates the necessary dummy nodes for an output port on the west side of a node, provided
* that the edge connects two different nodes.
*
* @param layeredGraph
* the layered graph
* @param westwardPort
* the offending port.
* @param edge
* the edge connected to the port.
* @param layerNodeList
* list of unassigned nodes belonging to the layer of the node the port belongs to.
* The new dummy node is added to this list and must be assigned to the layer later.
*/
private void createWestPortSideDummies(final LGraph layeredGraph, final LPort westwardPort, final LEdge edge, final List<LNode> layerNodeList) {
assert edge.getSource() == westwardPort;
// Ignore self loops
if (edge.getTarget().getNode() == westwardPort.getNode()) {
return;
}
// Dummy node in the same layer
LNode dummy = new LNode(layeredGraph);
dummy.setType(NodeType.LONG_EDGE);
dummy.setProperty(InternalProperties.ORIGIN, edge);
dummy.setProperty(LayeredOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_POS);
layerNodeList.add(dummy);
LPort dummyInput = new LPort();
dummyInput.setNode(dummy);
dummyInput.setSide(PortSide.WEST);
LPort dummyOutput = new LPort();
dummyOutput.setNode(dummy);
dummyOutput.setSide(PortSide.EAST);
// Reroute the original edge
LPort originalTarget = edge.getTarget();
edge.setTarget(dummyInput);
// Connect the dummy with the original port
LEdge dummyEdge = new LEdge();
dummyEdge.copyProperties(edge);
dummyEdge.setProperty(LayeredOptions.JUNCTION_POINTS, null);
dummyEdge.setSource(dummyOutput);
dummyEdge.setTarget(originalTarget);
// Move any head labels over to the new dummy edge
ListIterator<LLabel> labelIterator = edge.getLabels().listIterator();
while (labelIterator.hasNext()) {
LLabel label = labelIterator.next();
if (label.getProperty(LayeredOptions.EDGE_LABELS_PLACEMENT) == EdgeLabelPlacement.HEAD) {
// Remember which edge the label belonged to originally
assert !label.hasProperty(InternalProperties.END_LABEL_EDGE);
label.setProperty(InternalProperties.END_LABEL_EDGE, edge);
labelIterator.remove();
dummyEdge.getLabels().add(label);
}
}
// Set LONG_EDGE_SOURCE and LONG_EDGE_TARGET properties on the LONG_EDGE dummy
setLongEdgeSourceAndTarget(dummy, dummyInput, dummyOutput, westwardPort);
}
use of org.eclipse.elk.alg.layered.graph.LLabel in project elk by eclipse.
the class LabelAndNodeSizeProcessor method placeExternalPortDummyLabels.
/**
* Places the labels of the given external port dummy such that it results in correct node margins later on that
* will reserve enough space for the labels to be placed once label and node placement is called on the graph.
*/
private void placeExternalPortDummyLabels(final LNode dummy, final Set<PortLabelPlacement> graphPortLabelPlacement, final boolean placeNextToPortIfPossible, final boolean treatAsGroup) {
double labelPortSpacing = dummy.getProperty(LayeredOptions.SPACING_LABEL_PORT);
double labelLabelSpacing = dummy.getProperty(LayeredOptions.SPACING_LABEL_LABEL);
KVector dummySize = dummy.getSize();
// External port dummies have exactly one port (see ElkGraphImporter)
LPort dummyPort = dummy.getPorts().get(0);
KVector dummyPortPos = dummyPort.getPosition();
ElkRectangle portLabelBox = computePortLabelBox(dummyPort, labelLabelSpacing);
if (portLabelBox == null) {
return;
}
// TODO We could handle FIXED here as well
if (graphPortLabelPlacement.contains(PortLabelPlacement.INSIDE)) {
// (port label placement has to support this case first, though)
switch(dummy.getProperty(InternalProperties.EXT_PORT_SIDE)) {
case NORTH:
portLabelBox.x = (dummySize.x - portLabelBox.width) / 2 - dummyPortPos.x;
portLabelBox.y = labelPortSpacing;
break;
case SOUTH:
portLabelBox.x = (dummySize.x - portLabelBox.width) / 2 - dummyPortPos.x;
portLabelBox.y = -labelPortSpacing - portLabelBox.height;
break;
case EAST:
if (labelNextToPort(dummyPort, true, placeNextToPortIfPossible)) {
double labelHeight = treatAsGroup ? portLabelBox.height : dummyPort.getLabels().get(0).getSize().y;
portLabelBox.y = (dummySize.y - labelHeight) / 2 - dummyPortPos.y;
} else {
portLabelBox.y = dummySize.y + labelPortSpacing - dummyPortPos.y;
}
portLabelBox.x = -labelPortSpacing - portLabelBox.width;
break;
case WEST:
if (labelNextToPort(dummyPort, true, placeNextToPortIfPossible)) {
double labelHeight = treatAsGroup ? portLabelBox.height : dummyPort.getLabels().get(0).getSize().y;
portLabelBox.y = (dummySize.y - labelHeight) / 2 - dummyPortPos.y;
} else {
portLabelBox.y = dummySize.y + labelPortSpacing - dummyPortPos.y;
}
portLabelBox.x = labelPortSpacing;
break;
}
} else if (graphPortLabelPlacement.contains(PortLabelPlacement.OUTSIDE)) {
switch(dummy.getProperty(InternalProperties.EXT_PORT_SIDE)) {
case NORTH:
case SOUTH:
portLabelBox.x = dummyPortPos.x + labelPortSpacing;
break;
case EAST:
case WEST:
if (labelNextToPort(dummyPort, false, placeNextToPortIfPossible)) {
double labelHeight = treatAsGroup ? portLabelBox.height : dummyPort.getLabels().get(0).getSize().y;
portLabelBox.y = (dummySize.y - labelHeight) / 2 - dummyPortPos.y;
} else {
portLabelBox.y = dummyPortPos.y + labelPortSpacing;
}
break;
}
}
// Place the labels
double currentY = portLabelBox.y;
for (LLabel label : dummyPort.getLabels()) {
KVector labelPos = label.getPosition();
labelPos.x = portLabelBox.x;
labelPos.y = currentY;
currentY += label.getSize().y + labelLabelSpacing;
}
}
use of org.eclipse.elk.alg.layered.graph.LLabel in project elk by eclipse.
the class LabelDummyInserter method process.
@Override
public void process(final LGraph layeredGraph, final IElkProgressMonitor monitor) {
monitor.begin("Label dummy insertions", 1);
// We cannot add the nodes to the graph while we're iterating over it, so remember the dummy nodes we create
List<LNode> newDummyNodes = Lists.newArrayList();
double edgeLabelSpacing = layeredGraph.getProperty(LayeredOptions.SPACING_EDGE_LABEL);
double labelLabelSpacing = layeredGraph.getProperty(LayeredOptions.SPACING_LABEL_LABEL);
Direction layoutDirection = layeredGraph.getProperty(LayeredOptions.DIRECTION);
for (LNode node : layeredGraph.getLayerlessNodes()) {
for (LEdge edge : node.getOutgoingEdges()) {
if (edgeNeedsToBeProcessed(edge)) {
double thickness = retrieveThickness(edge);
// Create dummy node and remember represented labels (to be filled below)
List<LLabel> representedLabels = Lists.newArrayListWithCapacity(edge.getLabels().size());
LNode dummyNode = createLabelDummy(layeredGraph, edge, thickness, representedLabels);
newDummyNodes.add(dummyNode);
// Determine the size of the dummy node and move labels over to it
KVector dummySize = dummyNode.getSize();
ListIterator<LLabel> iterator = edge.getLabels().listIterator();
while (iterator.hasNext()) {
LLabel label = iterator.next();
if (label.getProperty(LayeredOptions.EDGE_LABELS_PLACEMENT) == EdgeLabelPlacement.CENTER) {
// The way we stack labels depends on the layout direction
if (layoutDirection.isVertical()) {
dummySize.x += label.getSize().x + labelLabelSpacing;
dummySize.y = Math.max(dummySize.y, label.getSize().y);
} else {
dummySize.x = Math.max(dummySize.x, label.getSize().x);
dummySize.y += label.getSize().y + labelLabelSpacing;
}
// Move the label over to the dummy node's REPRESENTED_LABELS property
representedLabels.add(label);
iterator.remove();
}
}
// edge-label spacing yet
if (layoutDirection.isVertical()) {
dummySize.x -= labelLabelSpacing;
dummySize.y += edgeLabelSpacing + thickness;
} else {
dummySize.y += edgeLabelSpacing - labelLabelSpacing + thickness;
}
}
}
}
// Add created dummies to graph
layeredGraph.getLayerlessNodes().addAll(newDummyNodes);
monitor.done();
}
use of org.eclipse.elk.alg.layered.graph.LLabel in project elk by eclipse.
the class LabelDummyRemover method placeLabelsForVerticalLayout.
private void placeLabelsForVerticalLayout(final List<LLabel> labels, final KVector labelPos, final double labelSpacing, final KVector labelSpace, final boolean leftAligned, final Direction layoutDirection) {
// We may have to override the alignment if all labels here are inline labels
boolean inline = labels.stream().allMatch(label -> label.getProperty(LayeredOptions.EDGE_LABELS_INLINE));
// Due to the way layout directions work, we need to pay attention to the order in which we place labels. While
// we can simply place them as they come for the DOWN direction, doing the same for the UP direction will
// reverse the label order in the final result. Thus, in that case we iterate over the reversed label list
List<LLabel> effectiveLabels = labels;
if (layoutDirection == Direction.UP) {
effectiveLabels = Lists.reverse(effectiveLabels);
}
for (LLabel label : effectiveLabels) {
label.getPosition().x = labelPos.x;
if (inline) {
label.getPosition().y = labelPos.y + (labelSpace.y - label.getSize().y) / 2;
} else if (leftAligned) {
label.getPosition().y = labelPos.y;
} else {
label.getPosition().y = labelPos.y + labelSpace.y - label.getSize().y;
}
labelPos.x += label.getSize().x + labelSpacing;
}
}
use of org.eclipse.elk.alg.layered.graph.LLabel in project elk by eclipse.
the class LabelDummyRemover method process.
@Override
public void process(final LGraph layeredGraph, final IElkProgressMonitor monitor) {
monitor.begin("Label dummy removal", 1);
double edgeLabelSpacing = layeredGraph.getProperty(LayeredOptions.SPACING_EDGE_LABEL);
double labelLabelSpacing = layeredGraph.getProperty(LayeredOptions.SPACING_LABEL_LABEL);
Direction layoutDirection = layeredGraph.getProperty(LayeredOptions.DIRECTION);
for (Layer layer : layeredGraph.getLayers()) {
// An iterator is necessary for traversing nodes, since dummy nodes might be removed
ListIterator<LNode> nodeIterator = layer.getNodes().listIterator();
while (nodeIterator.hasNext()) {
LNode node = nodeIterator.next();
if (node.getType() == NodeType.LABEL) {
// First, place labels on position of dummy node
LEdge originEdge = (LEdge) node.getProperty(InternalProperties.ORIGIN);
double thickness = originEdge.getProperty(LayeredOptions.EDGE_THICKNESS).doubleValue();
boolean labelsBelowEdge = node.getProperty(InternalProperties.LABEL_SIDE) == LabelSide.BELOW;
KVector currLabelPos = new KVector(node.getPosition());
// position down a bit to respect the label spacing
if (labelsBelowEdge) {
currLabelPos.y += thickness + edgeLabelSpacing;
}
// Calculate the space available for the placement of labels
KVector labelSpace = new KVector(node.getSize().x, node.getSize().y - thickness - edgeLabelSpacing);
// Place labels
List<LLabel> representedLabels = node.getProperty(InternalProperties.REPRESENTED_LABELS);
if (layoutDirection.isVertical()) {
placeLabelsForVerticalLayout(representedLabels, currLabelPos, labelLabelSpacing, labelSpace, labelsBelowEdge, layoutDirection);
} else {
placeLabelsForHorizontalLayout(representedLabels, currLabelPos, labelLabelSpacing, labelSpace);
}
// Add represented labels back to the original edge
originEdge.getLabels().addAll(representedLabels);
// Whether we need to add unnecessary bend points around the label dummy depends on the edge
// router. For orthogonal edge routing, they are not necessary. For splines, they may even be
// harmful. For polylines, they are necessary to keep the routes edges take close to their labels
LongEdgeJoiner.joinAt(node, layeredGraph.getProperty(LayeredOptions.EDGE_ROUTING) == EdgeRouting.POLYLINE);
// Remove the node
nodeIterator.remove();
}
}
}
monitor.done();
}
Aggregations