use of org.eclipse.elk.core.options.Direction in project elk by eclipse.
the class ElkGraphImporter method transformPort.
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Port Transformation
/**
* Transforms the given port. The new port will be added to the given node and will be
* registered with the {@code transformMap}.
*
* @param elkport
* the port to transform.
* @param parentLNode
* the node the port should be added to.
* @param graphProperties
* the graph properties of the graph the transformed port will be part of. The graph
* properties are modified depending on the port's properties.
* @param layoutDirection
* the layout direction in the graph the port will be part of.
* @param portConstraints
* the port constraints of the port's node.
* @return the transformed port.
*/
private LPort transformPort(final ElkPort elkport, final LNode parentLNode, final Set<GraphProperties> graphProperties, final Direction layoutDirection, final PortConstraints portConstraints) {
// create layered port, copying its position
LPort lport = new LPort();
lport.copyProperties(elkport);
lport.setSide(elkport.getProperty(LayeredOptions.PORT_SIDE));
lport.setProperty(InternalProperties.ORIGIN, elkport);
lport.setNode(parentLNode);
KVector portSize = lport.getSize();
portSize.x = elkport.getWidth();
portSize.y = elkport.getHeight();
KVector portPos = lport.getPosition();
portPos.x = elkport.getX();
portPos.y = elkport.getY();
nodeAndPortMap.put(elkport, lport);
// check if the original port has any outgoing connections to descendants of its node
boolean connectionsToDescendants = elkport.getOutgoingEdges().stream().flatMap(edge -> edge.getTargets().stream()).map(ElkGraphUtil::connectableShapeToNode).anyMatch(targetNode -> ElkGraphUtil.isDescendant(targetNode, elkport.getParent()));
// there could be yet incoming connections from descendants
if (!connectionsToDescendants) {
// check if the original port has any incoming connections from descendants of its node
connectionsToDescendants = elkport.getIncomingEdges().stream().flatMap(edge -> edge.getSources().stream()).map(ElkGraphUtil::connectableShapeToNode).anyMatch(sourceNode -> ElkGraphUtil.isDescendant(sourceNode, elkport.getParent()));
}
// if there are still no connections to descendants, there might yet be inside self loops involved
if (!connectionsToDescendants) {
// check if the original port has any incoming connections from descendants of its node
connectionsToDescendants = elkport.getOutgoingEdges().stream().anyMatch(edge -> edge.isSelfloop() && edge.getProperty(LayeredOptions.INSIDE_SELF_LOOPS_YO));
}
// if we have found connections to / from descendants, mark the port accordingly
lport.setProperty(InternalProperties.INSIDE_CONNECTIONS, connectionsToDescendants);
// initialize the port's side, offset, and anchor point
LGraphUtil.initializePort(lport, portConstraints, layoutDirection, elkport.getProperty(LayeredOptions.PORT_ANCHOR));
// create the port's labels
for (ElkLabel elklabel : elkport.getLabels()) {
if (!elklabel.getProperty(LayeredOptions.NO_LAYOUT) && !Strings.isNullOrEmpty(elklabel.getText())) {
lport.getLabels().add(transformLabel(elklabel));
}
}
switch(layoutDirection) {
case LEFT:
case RIGHT:
if (lport.getSide() == PortSide.NORTH || lport.getSide() == PortSide.SOUTH) {
graphProperties.add(GraphProperties.NORTH_SOUTH_PORTS);
}
break;
case UP:
case DOWN:
if (lport.getSide() == PortSide.EAST || lport.getSide() == PortSide.WEST) {
graphProperties.add(GraphProperties.NORTH_SOUTH_PORTS);
}
break;
}
return lport;
}
use of org.eclipse.elk.core.options.Direction in project elk by eclipse.
the class ElkGraphImporter method ensureDefinedPortSide.
/**
* Ensures that the given port has a defined port side.
*/
private void ensureDefinedPortSide(final LGraph lgraph, final ElkPort elkport) {
Direction layoutDirection = lgraph.getProperty(LayeredOptions.DIRECTION);
PortSide portSide = elkport.getProperty(LayeredOptions.PORT_SIDE);
PortConstraints portConstraints = lgraph.getProperty(LayeredOptions.PORT_CONSTRAINTS);
if (!portConstraints.isSideFixed()) {
// We are free to assign ports to sides, so the port side will depend on the layout direction and the
// port's net flow
int netFlow = calculateNetFlow(elkport);
if (netFlow > 0) {
portSide = PortSide.fromDirection(layoutDirection);
} else {
portSide = PortSide.fromDirection(layoutDirection).opposed();
}
} else {
// We are not free to assign port sides. If none is set, try inferring it from the port's position
if (portSide == PortSide.UNDEFINED) {
portSide = ElkUtil.calcPortSide(elkport, layoutDirection);
// There are cases where ELK may have failed to infer the port side
if (portSide == PortSide.UNDEFINED) {
portSide = PortSide.fromDirection(layoutDirection);
}
}
}
elkport.setProperty(LayeredOptions.PORT_SIDE, portSide);
}
use of org.eclipse.elk.core.options.Direction in project elk by eclipse.
the class ElkGraphImporter method importHierarchicalGraph.
/**
* Imports the graph hierarchy rooted at the given graph.
*
* @param elkgraph
* graph to import.
* @param lgraph
* graph to add the direct children of the current hierarchy level to.
*/
private void importHierarchicalGraph(final ElkNode elkgraph, final LGraph lgraph) {
final Queue<ElkNode> elkGraphQueue = Lists.newLinkedList();
Direction parentGraphDirection = lgraph.getProperty(LayeredOptions.DIRECTION);
// Model order index for nodes
int index = 0;
// Transform the node's children
elkGraphQueue.addAll(elkgraph.getChildren());
while (!elkGraphQueue.isEmpty()) {
ElkNode elknode = elkGraphQueue.poll();
if (elkgraph.getProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY) != OrderingStrategy.NONE || elkgraph.getProperty(LayeredOptions.CYCLE_BREAKING_STRATEGY) == CycleBreakingStrategy.MODEL_ORDER) {
// Assign a model order to the nodes as they are read
elknode.setProperty(InternalProperties.MODEL_ORDER, index++);
}
// Check if the current node is to be laid out in the first place
boolean isNodeToBeLaidOut = !elknode.getProperty(LayeredOptions.NO_LAYOUT);
if (isNodeToBeLaidOut) {
// Check if there has to be an LGraph for this node (which is the case if it has children or inside
// self-loops, and if it does not have another layout algorithm configured)
boolean hasChildren = !elknode.getChildren().isEmpty();
boolean hasInsideSelfLoops = hasInsideSelfLoops(elknode);
boolean hasHierarchyHandlingEnabled = elknode.getProperty(LayeredOptions.HIERARCHY_HANDLING) == HierarchyHandling.INCLUDE_CHILDREN;
boolean usesElkLayered = !elknode.hasProperty(CoreOptions.ALGORITHM) || elknode.getProperty(CoreOptions.ALGORITHM).equals(LayeredOptions.ALGORITHM_ID);
LGraph nestedGraph = null;
if (usesElkLayered && hasHierarchyHandlingEnabled && (hasChildren || hasInsideSelfLoops)) {
nestedGraph = createLGraph(elknode);
nestedGraph.setProperty(LayeredOptions.DIRECTION, parentGraphDirection);
// Apply a spacing configuration, for details see comment int #importGraph(...)
if (nestedGraph.hasProperty(LayeredOptions.SPACING_BASE_VALUE)) {
LayeredSpacings.withBaseValue(nestedGraph.getProperty(LayeredOptions.SPACING_BASE_VALUE)).apply(nestedGraph);
}
// if the size constraints are not empty
if (shouldCalculateMinimumGraphSize(elknode)) {
final LGraph finalNestedGraph = nestedGraph;
elknode.getPorts().stream().forEach(elkport -> ensureDefinedPortSide(finalNestedGraph, elkport));
calculateMinimumGraphSize(elknode, nestedGraph);
}
}
// Transform da node!!!
LGraph parentLGraph = lgraph;
LNode parentLNode = (LNode) nodeAndPortMap.get(elknode.getParent());
if (parentLNode != null) {
parentLGraph = parentLNode.getNestedGraph();
}
LNode lnode = transformNode(elknode, parentLGraph);
// Setup hierarchical relationships
if (nestedGraph != null) {
lnode.setNestedGraph(nestedGraph);
nestedGraph.setParentNode(lnode);
elkGraphQueue.addAll(elknode.getChildren());
}
}
}
// Model order index for edges.
index = 0;
// Transform the edges
elkGraphQueue.add(elkgraph);
while (!elkGraphQueue.isEmpty()) {
ElkNode elkGraphNode = elkGraphQueue.poll();
for (ElkEdge elkedge : elkGraphNode.getContainedEdges()) {
// We don't support hyperedges
checkEdgeValidity(elkedge);
if (elkgraph.getProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY) != OrderingStrategy.NONE || elkgraph.getProperty(LayeredOptions.CYCLE_BREAKING_STRATEGY) == CycleBreakingStrategy.MODEL_ORDER) {
// Assign a model order to the edges as they are read
elkedge.setProperty(InternalProperties.MODEL_ORDER, index++);
}
ElkNode sourceNode = ElkGraphUtil.connectableShapeToNode(elkedge.getSources().get(0));
ElkNode targetNode = ElkGraphUtil.connectableShapeToNode(elkedge.getTargets().get(0));
// Don't bother if either the edge or at least one of its end points are excluded from layout
if (elkedge.getProperty(LayeredOptions.NO_LAYOUT) || sourceNode.getProperty(LayeredOptions.NO_LAYOUT) || targetNode.getProperty(LayeredOptions.NO_LAYOUT)) {
continue;
}
// Check if this edge is an inside self-loop
boolean isInsideSelfLoop = elkedge.isSelfloop() && sourceNode.getProperty(LayeredOptions.INSIDE_SELF_LOOPS_ACTIVATE) && elkedge.getProperty(LayeredOptions.INSIDE_SELF_LOOPS_YO);
// Find the graph the edge will be placed in. Basically, if the edge is an inside
// self loop or connects one of its end points to a descendant, the edge will be
// placed in the graph that represents that end point's insides. Otherwise, it will
// be placed in the current graph.
ElkNode parentElkGraph = elkGraphNode;
if (isInsideSelfLoop || ElkGraphUtil.isDescendant(targetNode, sourceNode)) {
parentElkGraph = sourceNode;
} else if (ElkGraphUtil.isDescendant(sourceNode, targetNode)) {
parentElkGraph = targetNode;
}
LGraph parentLGraph = lgraph;
LNode parentLNode = (LNode) nodeAndPortMap.get(parentElkGraph);
if (parentLNode != null) {
parentLGraph = parentLNode.getNestedGraph();
}
// Transform the edge, finally...
LEdge ledge = transformEdge(elkedge, parentElkGraph, parentLGraph);
// Find the graph the edge's coordinates will have to be made relative to during export. This will only
// do something if the edge containment inside ELK Layered differs from the edge containment in the
// ELK graph
ledge.setProperty(InternalProperties.COORDINATE_SYSTEM_ORIGIN, findCoordinateSystemOrigin(elkedge, elkgraph, lgraph));
}
// We may need to look at edges contained in the current graph node's children as well.
// this is true unless either the current graph node does not have hierarchy handling
// enabled, or a child has another layout algorithm configured
boolean hasHierarchyHandlingEnabled = elkGraphNode.getProperty(LayeredOptions.HIERARCHY_HANDLING) == HierarchyHandling.INCLUDE_CHILDREN;
if (hasHierarchyHandlingEnabled) {
for (ElkNode elkChildGraphNode : elkGraphNode.getChildren()) {
boolean usesElkLayered = !elkChildGraphNode.hasProperty(CoreOptions.ALGORITHM) || elkChildGraphNode.getProperty(CoreOptions.ALGORITHM).equals(LayeredOptions.ALGORITHM_ID);
boolean partOfSameLayoutRun = elkChildGraphNode.getProperty(LayeredOptions.HIERARCHY_HANDLING) == HierarchyHandling.INCLUDE_CHILDREN;
if (usesElkLayered && partOfSameLayoutRun) {
elkGraphQueue.add(elkChildGraphNode);
}
}
}
}
}
use of org.eclipse.elk.core.options.Direction in project elk by eclipse.
the class CompoundGraphPreprocessor method createExternalPortDummy.
/**
* Retrieves a dummy node to be used to represent a new external port of the parent node and to
* connect a new segment of the given hierarchical edge to. A proper dummy node might already
* have been created; if so, that one is returned.
*
* @param graph
* the graph.
* @param parentNode
* the graph's parent node.
* @param portType
* the type of the new external port.
* @param edge
* the edge that will be connected to the external port.
* @return an appropriate external port dummy.
*/
private LNode createExternalPortDummy(final LGraph graph, final LNode parentNode, final PortType portType, final PortSide portSide, final LEdge edge) {
LNode dummyNode = null;
// find the port on the outside of its parent node that the edge connects to
LPort outsidePort = portType == PortType.INPUT ? edge.getSource() : edge.getTarget();
Direction layoutDirection = LGraphUtil.getDirection(graph);
// check if the edge connects to the parent node or to something way outside...
if (outsidePort.getNode() == parentNode) {
// we need to check if a dummy node has already been created for the port
dummyNode = dummyNodeMap.get(outsidePort);
if (dummyNode == null) {
// Ticket #160 explains why we need to pass on a position here. While that works fine to determine port
// orders, the exact port positions might yet need to be adjusted for some inside paddings
dummyNode = LGraphUtil.createExternalPortDummy(outsidePort, parentNode.getProperty(LayeredOptions.PORT_CONSTRAINTS), portSide, calculateNetFlow(outsidePort), null, outsidePort.getPosition(), outsidePort.getSize(), layoutDirection, graph);
dummyNode.setProperty(InternalProperties.ORIGIN, outsidePort);
dummyNodeMap.put(outsidePort, dummyNode);
}
} else {
// We create a new dummy node in any case, and since there is no port yet we have to create one as well. We
// do need to pass a position vector to keep an NPE from being thrown (#160), but it is questionable
// whether this part of the code should actually be used with anything other than fixed port constraints.
double thickness = edge.getProperty(LayeredOptions.EDGE_THICKNESS);
dummyNode = LGraphUtil.createExternalPortDummy(createExternalPortProperties(graph), parentNode.getProperty(LayeredOptions.PORT_CONSTRAINTS), portSide, calculateNetFlow(outsidePort), null, new KVector(), new KVector(thickness, thickness), layoutDirection, graph);
LPort dummyPort = createPortForDummy(dummyNode, parentNode, portType);
dummyNode.setProperty(InternalProperties.ORIGIN, dummyPort);
dummyNodeMap.put(dummyPort, dummyNode);
}
// set a few graph properties
graph.getProperty(InternalProperties.GRAPH_PROPERTIES).add(GraphProperties.EXTERNAL_PORTS);
if (graph.getProperty(LayeredOptions.PORT_CONSTRAINTS).isSideFixed()) {
graph.setProperty(LayeredOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE);
} else {
graph.setProperty(LayeredOptions.PORT_CONSTRAINTS, PortConstraints.FREE);
}
return dummyNode;
}
use of org.eclipse.elk.core.options.Direction in project elk by eclipse.
the class LGraphUtil method createPort.
// /////////////////////////////////////////////////////////////////////////////
// Handling of Ports
/**
* Create a port for an edge that is not connected to a port. This is necessary because ELK
* Layered wants all edges to have a source port and a target port. The port side is computed
* from the given absolute end point position of the edge.
*
* @param node
* the node at which the edge is incident
* @param endPoint
* the absolute point where the edge ends, or {@code null} if unknown
* @param type
* the port type
* @param layeredGraph
* the layered graph
* @return a new port
*/
public static LPort createPort(final LNode node, final KVector endPoint, final PortType type, final LGraph layeredGraph) {
LPort port;
Direction direction = getDirection(layeredGraph);
boolean mergePorts = layeredGraph.getProperty(LayeredOptions.MERGE_EDGES);
if ((mergePorts || node.getProperty(LayeredOptions.HYPERNODE)) && !node.getProperty(LayeredOptions.PORT_CONSTRAINTS).isSideFixed()) {
// Hypernodes have one output port and one input port
PortSide defaultSide = PortSide.fromDirection(direction);
port = provideCollectorPort(layeredGraph, node, type, type == PortType.OUTPUT ? defaultSide : defaultSide.opposed());
} else {
port = new LPort();
port.setNode(node);
if (endPoint != null) {
KVector pos = port.getPosition();
pos.x = endPoint.x - node.getPosition().x;
pos.y = endPoint.y - node.getPosition().y;
pos.bound(0, 0, node.getSize().x, node.getSize().y);
port.setSide(calcPortSide(port, direction));
} else {
PortSide defaultSide = PortSide.fromDirection(direction);
port.setSide(type == PortType.OUTPUT ? defaultSide : defaultSide.opposed());
}
Set<GraphProperties> graphProperties = layeredGraph.getProperty(InternalProperties.GRAPH_PROPERTIES);
PortSide portSide = port.getSide();
switch(direction) {
case LEFT:
case RIGHT:
if (portSide == PortSide.NORTH || portSide == PortSide.SOUTH) {
graphProperties.add(GraphProperties.NORTH_SOUTH_PORTS);
}
break;
case UP:
case DOWN:
if (portSide == PortSide.EAST || portSide == PortSide.WEST) {
graphProperties.add(GraphProperties.NORTH_SOUTH_PORTS);
}
break;
}
}
return port;
}
Aggregations