use of org.eclipse.elk.alg.layered.options.GraphProperties in project elk by eclipse.
the class GraphConfigurator method getPhaseIndependentLayoutProcessorConfiguration.
/**
* Returns an intermediate processing configuration with processors not tied to specific phases.
*
* @param lgraph the layered graph to be processed. The configuration may vary depending on certain
* properties of the graph.
* @return intermediate processing configuration. May be {@code null}.
*/
private LayoutProcessorConfiguration<LayeredPhases, LGraph> getPhaseIndependentLayoutProcessorConfiguration(final LGraph lgraph) {
Set<GraphProperties> graphProperties = lgraph.getProperty(InternalProperties.GRAPH_PROPERTIES);
// Basic configuration
LayoutProcessorConfiguration<LayeredPhases, LGraph> configuration = LayoutProcessorConfiguration.createFrom(BASELINE_PROCESSING_CONFIGURATION);
// Hierarchical layout.
// Note that the recursive graph layout engine made sure that at this point
// 'INCLUDE_CHILDREN' has been propagated to all parent nodes with 'INHERIT'.
// Thus, every lgraph of the hierarchical lgraph structure is configured with the following additions.
HierarchyHandling hierarchyHandling = lgraph.getProperty(LayeredOptions.HIERARCHY_HANDLING);
if (hierarchyHandling == HierarchyHandling.INCLUDE_CHILDREN) {
configuration.addAll(HIERARCHICAL_ADDITIONS);
}
// Port side processor, put to first slot only if requested and routing is orthogonal
if (lgraph.getProperty(LayeredOptions.FEEDBACK_EDGES)) {
configuration.addBefore(LayeredPhases.P1_CYCLE_BREAKING, IntermediateProcessorStrategy.PORT_SIDE_PROCESSOR);
} else {
configuration.addBefore(LayeredPhases.P3_NODE_ORDERING, IntermediateProcessorStrategy.PORT_SIDE_PROCESSOR);
}
// If the graph has a label manager, so add label management additions
if (lgraph.getProperty(LabelManagementOptions.LABEL_MANAGER) != null) {
configuration.addAll(LABEL_MANAGEMENT_ADDITIONS);
}
// If the graph should be laid out interactively, add the layers and positions to the nodes.
if (lgraph.getProperty(LayeredOptions.INTERACTIVE_LAYOUT)) {
configuration.addAfter(LayeredPhases.P5_EDGE_ROUTING, IntermediateProcessorStrategy.CONSTRAINTS_POSTPROCESSOR);
}
// graph transformations for unusual layout directions
switch(lgraph.getProperty(LayeredOptions.DIRECTION)) {
case LEFT:
case DOWN:
case UP:
configuration.addBefore(LayeredPhases.P1_CYCLE_BREAKING, IntermediateProcessorStrategy.DIRECTION_PREPROCESSOR).addAfter(LayeredPhases.P5_EDGE_ROUTING, IntermediateProcessorStrategy.DIRECTION_POSTPROCESSOR);
break;
default:
// don't need any processors here
break;
}
// Additional dependencies
if (graphProperties.contains(GraphProperties.COMMENTS)) {
configuration.addBefore(LayeredPhases.P1_CYCLE_BREAKING, IntermediateProcessorStrategy.COMMENT_PREPROCESSOR).addBefore(LayeredPhases.P4_NODE_PLACEMENT, IntermediateProcessorStrategy.COMMENT_NODE_MARGIN_CALCULATOR).addAfter(LayeredPhases.P5_EDGE_ROUTING, IntermediateProcessorStrategy.COMMENT_POSTPROCESSOR);
}
// Node-Promotion application for reduction of dummy nodes after layering
if (lgraph.getProperty(LayeredOptions.LAYERING_NODE_PROMOTION_STRATEGY) != NodePromotionStrategy.NONE) {
configuration.addBefore(LayeredPhases.P3_NODE_ORDERING, IntermediateProcessorStrategy.NODE_PROMOTION);
}
// Preserve certain partitions during layering
if (graphProperties.contains(GraphProperties.PARTITIONS)) {
configuration.addBefore(LayeredPhases.P1_CYCLE_BREAKING, IntermediateProcessorStrategy.PARTITION_PREPROCESSOR);
configuration.addBefore(LayeredPhases.P2_LAYERING, IntermediateProcessorStrategy.PARTITION_MIDPROCESSOR);
configuration.addBefore(LayeredPhases.P3_NODE_ORDERING, IntermediateProcessorStrategy.PARTITION_POSTPROCESSOR);
}
// Additional horizontal compaction depends on orthogonal edge routing
if (lgraph.getProperty(LayeredOptions.COMPACTION_POST_COMPACTION_STRATEGY) != GraphCompactionStrategy.NONE && lgraph.getProperty(LayeredOptions.EDGE_ROUTING) != EdgeRouting.POLYLINE) {
configuration.addAfter(LayeredPhases.P5_EDGE_ROUTING, IntermediateProcessorStrategy.HORIZONTAL_COMPACTOR);
}
// Move trees of high degree nodes to separate layers
if (lgraph.getProperty(LayeredOptions.HIGH_DEGREE_NODES_TREATMENT)) {
configuration.addBefore(LayeredPhases.P3_NODE_ORDERING, IntermediateProcessorStrategy.HIGH_DEGREE_NODE_LAYER_PROCESSOR);
}
// Introduce in-layer constraints to preserve the order of regular nodes
if (lgraph.getProperty(LayeredOptions.CROSSING_MINIMIZATION_SEMI_INTERACTIVE)) {
configuration.addBefore(LayeredPhases.P3_NODE_ORDERING, IntermediateProcessorStrategy.SEMI_INTERACTIVE_CROSSMIN_PROCESSOR);
}
// ElkLayered#reviewAndCorrectHierarchicalProcessors(...)
if (activateGreedySwitchFor(lgraph)) {
final GreedySwitchType greedySwitchType;
if (isHierarchicalLayout(lgraph)) {
greedySwitchType = lgraph.getProperty(LayeredOptions.CROSSING_MINIMIZATION_GREEDY_SWITCH_HIERARCHICAL_TYPE);
} else {
greedySwitchType = lgraph.getProperty(LayeredOptions.CROSSING_MINIMIZATION_GREEDY_SWITCH_TYPE);
}
IntermediateProcessorStrategy internalGreedyType = (greedySwitchType == GreedySwitchType.ONE_SIDED) ? IntermediateProcessorStrategy.ONE_SIDED_GREEDY_SWITCH : IntermediateProcessorStrategy.TWO_SIDED_GREEDY_SWITCH;
configuration.addBefore(LayeredPhases.P4_NODE_PLACEMENT, internalGreedyType);
}
// Wrapping of graphs
switch(lgraph.getProperty(LayeredOptions.WRAPPING_STRATEGY)) {
case SINGLE_EDGE:
configuration.addBefore(LayeredPhases.P4_NODE_PLACEMENT, IntermediateProcessorStrategy.SINGLE_EDGE_GRAPH_WRAPPER);
break;
case MULTI_EDGE:
configuration.addBefore(LayeredPhases.P3_NODE_ORDERING, IntermediateProcessorStrategy.BREAKING_POINT_INSERTER).addBefore(LayeredPhases.P4_NODE_PLACEMENT, IntermediateProcessorStrategy.BREAKING_POINT_PROCESSOR).addAfter(LayeredPhases.P5_EDGE_ROUTING, IntermediateProcessorStrategy.BREAKING_POINT_REMOVER);
break;
// OFF
default:
}
if (lgraph.getProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY) != OrderingStrategy.NONE) {
configuration.addBefore(LayeredPhases.P3_NODE_ORDERING, IntermediateProcessorStrategy.SORT_BY_INPUT_ORDER_OF_MODEL);
}
return configuration;
}
use of org.eclipse.elk.alg.layered.options.GraphProperties in project elk by eclipse.
the class ElkGraphImporter method importGraph.
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Import Entry Points
/**
* Imports the given graph.
*
* @param elkgraph
* the graph to import.
* @return the transformed graph.
*/
public LGraph importGraph(final ElkNode elkgraph) {
// Create the layered graph
final LGraph topLevelGraph = createLGraph(elkgraph);
// Assign defined port sides to all external ports
elkgraph.getPorts().stream().forEach(elkport -> ensureDefinedPortSide(topLevelGraph, elkport));
// Transform the external ports, if any
Set<GraphProperties> graphProperties = topLevelGraph.getProperty(InternalProperties.GRAPH_PROPERTIES);
checkExternalPorts(elkgraph, graphProperties);
if (graphProperties.contains(GraphProperties.EXTERNAL_PORTS)) {
for (ElkPort elkport : elkgraph.getPorts()) {
transformExternalPort(elkgraph, topLevelGraph, elkport);
}
}
// Calculate the graph's minimum size
if (shouldCalculateMinimumGraphSize(elkgraph)) {
calculateMinimumGraphSize(elkgraph, topLevelGraph);
}
// Remember things
if (topLevelGraph.getProperty(LayeredOptions.PARTITIONING_ACTIVATE)) {
graphProperties.add(GraphProperties.PARTITIONS);
}
// values of the first layout run would be used (explicitly set spacing values are not overwritten).
if (topLevelGraph.hasProperty(LayeredOptions.SPACING_BASE_VALUE)) {
LayeredSpacings.withBaseValue(topLevelGraph.getProperty(LayeredOptions.SPACING_BASE_VALUE)).apply(topLevelGraph);
}
// Import the graph either with or without multiple nested levels of hierarchy
if (elkgraph.getProperty(LayeredOptions.HIERARCHY_HANDLING) == HierarchyHandling.INCLUDE_CHILDREN) {
importHierarchicalGraph(elkgraph, topLevelGraph);
} else {
importFlatGraph(elkgraph, topLevelGraph);
}
return topLevelGraph;
}
use of org.eclipse.elk.alg.layered.options.GraphProperties 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.alg.layered.options.GraphProperties in project elk by eclipse.
the class ElkGraphImporter method transformEdge.
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Edge Transformation
/**
* Transforms the given edge if it's not a hyperedge. If it is a hyperedge, throws an exception.
*
* @param elkedge the edge to transform
* @param elkparent the node in the original graph which currently gets transformed into {@code lgraph}
* @param lgraph the layered graph
* @return the transformed edge, or {@code null} if it cannot be transformed
* @throws UnsupportedGraphException if the edge is a hyperedge.
*/
private LEdge transformEdge(final ElkEdge elkedge, final ElkNode elkparent, final LGraph lgraph) {
checkEdgeValidity(elkedge);
// Get a few basic information about the edge
ElkConnectableShape elkSourceShape = elkedge.getSources().get(0);
ElkConnectableShape elkTargetShape = elkedge.getTargets().get(0);
ElkNode elkSourceNode = ElkGraphUtil.connectableShapeToNode(elkSourceShape);
ElkNode elkTargetNode = ElkGraphUtil.connectableShapeToNode(elkTargetShape);
ElkEdgeSection edgeSection = elkedge.getSections().isEmpty() ? null : elkedge.getSections().get(0);
// Find the transformed source and target nodes
LNode sourceLNode = (LNode) nodeAndPortMap.get(elkSourceNode);
LNode targetLNode = (LNode) nodeAndPortMap.get(elkTargetNode);
LPort sourceLPort = null;
LPort targetLPort = null;
// Find the transformed source port, if any
if (elkSourceShape instanceof ElkPort) {
// If the ElkPort is a regular port, it will map to an LPort; if it's an external port, it
// will map to an LNode
LGraphElement sourceElem = nodeAndPortMap.get(elkSourceShape);
if (sourceElem instanceof LPort) {
sourceLPort = (LPort) sourceElem;
} else if (sourceElem instanceof LNode) {
sourceLNode = (LNode) sourceElem;
sourceLPort = sourceLNode.getPorts().get(0);
}
}
// Find the transformed target port, if any
if (elkTargetShape instanceof ElkPort) {
// If the ElkPort is a regular port, it will map to an LPort; if it's an external port, it
// will map to an LNode
LGraphElement targetElem = nodeAndPortMap.get(elkTargetShape);
if (targetElem instanceof LPort) {
targetLPort = (LPort) targetElem;
} else if (targetElem instanceof LNode) {
targetLNode = (LNode) targetElem;
targetLPort = targetLNode.getPorts().get(0);
}
}
// reason, we back out
if (sourceLNode == null || targetLNode == null) {
throw new UnsupportedGraphException("The source or the target of edge " + elkedge + " could not be found. " + "This usually happens when an edge connects a node laid out by ELK Layered to a node in " + "another level of hierarchy laid out by either another instance of ELK Layered or another " + "layout algorithm alltogether. The former can be solved by setting the hierarchyHandling " + "option to INCLUDE_CHILDREN.");
}
// Create a layered edge
LEdge ledge = new LEdge();
ledge.copyProperties(elkedge);
ledge.setProperty(InternalProperties.ORIGIN, elkedge);
// Clear junction points, since they are recomputed from scratch
ledge.setProperty(LayeredOptions.JUNCTION_POINTS, null);
// If we have a self-loop, set the appropriate graph property
Set<GraphProperties> graphProperties = lgraph.getProperty(InternalProperties.GRAPH_PROPERTIES);
if (sourceLNode == targetLNode) {
graphProperties.add(GraphProperties.SELF_LOOPS);
}
// Create source and target ports if they do not exist yet
if (sourceLPort == null) {
PortType portType = PortType.OUTPUT;
KVector sourcePoint = null;
if (edgeSection != null && sourceLNode.getProperty(LayeredOptions.PORT_CONSTRAINTS).isSideFixed()) {
sourcePoint = new KVector(edgeSection.getStartX(), edgeSection.getStartY());
// The coordinates need to be relative to us
ElkUtil.toAbsolute(sourcePoint, elkedge.getContainingNode());
ElkUtil.toRelative(sourcePoint, elkparent);
// source), we may need to adjust the coordinates
if (ElkGraphUtil.isDescendant(elkTargetNode, elkSourceNode)) {
// External source port: put it on the west side
portType = PortType.INPUT;
sourcePoint.add(sourceLNode.getPosition());
}
}
sourceLPort = LGraphUtil.createPort(sourceLNode, sourcePoint, portType, lgraph);
}
if (targetLPort == null) {
PortType portType = PortType.INPUT;
KVector targetPoint = null;
if (edgeSection != null && targetLNode.getProperty(LayeredOptions.PORT_CONSTRAINTS).isSideFixed()) {
targetPoint = new KVector(edgeSection.getEndX(), edgeSection.getEndY());
// Adjust the coordinates
// MIGRATE Not sure yet if this really does what we want it to do
ElkUtil.toAbsolute(targetPoint, elkedge.getContainingNode());
ElkUtil.toRelative(targetPoint, elkparent);
}
targetLPort = LGraphUtil.createPort(targetLNode, targetPoint, portType, targetLNode.getGraph());
}
// Finally set the source and target of the edge
ledge.setSource(sourceLPort);
ledge.setTarget(targetLPort);
// If the ports have multiple incoming or outgoing edges, the HYPEREDGE property needs to be set
if (sourceLPort.getIncomingEdges().size() > 1 || sourceLPort.getOutgoingEdges().size() > 1 || targetLPort.getIncomingEdges().size() > 1 || targetLPort.getOutgoingEdges().size() > 1) {
graphProperties.add(GraphProperties.HYPEREDGES);
}
// Transform the edge's labels
for (ElkLabel elklabel : elkedge.getLabels()) {
if (!elklabel.getProperty(LayeredOptions.NO_LAYOUT) && !Strings.isNullOrEmpty(elklabel.getText())) {
LLabel llabel = transformLabel(elklabel);
ledge.getLabels().add(llabel);
// edge label placement is actually properly defined
switch(llabel.getProperty(LayeredOptions.EDGE_LABELS_PLACEMENT)) {
case HEAD:
case TAIL:
graphProperties.add(GraphProperties.END_LABELS);
break;
case CENTER:
graphProperties.add(GraphProperties.CENTER_LABELS);
llabel.setProperty(LayeredOptions.EDGE_LABELS_PLACEMENT, EdgeLabelPlacement.CENTER);
}
}
}
// Copy the original bend points of the edge in case they are required
CrossingMinimizationStrategy crossMinStrat = lgraph.getProperty(LayeredOptions.CROSSING_MINIMIZATION_STRATEGY);
NodePlacementStrategy nodePlaceStrat = lgraph.getProperty(LayeredOptions.NODE_PLACEMENT_STRATEGY);
boolean bendPointsRequired = crossMinStrat == CrossingMinimizationStrategy.INTERACTIVE || nodePlaceStrat == NodePlacementStrategy.INTERACTIVE;
if (edgeSection != null && !edgeSection.getBendPoints().isEmpty() && bendPointsRequired) {
KVectorChain originalBendpoints = ElkUtil.createVectorChain(edgeSection);
KVectorChain importedBendpoints = new KVectorChain();
// MIGRATE We may have to do some coordinate conversion here
for (KVector point : originalBendpoints) {
importedBendpoints.add(new KVector(point));
}
ledge.setProperty(InternalProperties.ORIGINAL_BENDPOINTS, importedBendpoints);
}
return ledge;
}
use of org.eclipse.elk.alg.layered.options.GraphProperties 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