use of org.eclipse.elk.alg.layered.graph.LGraph in project elk by eclipse.
the class LayeredLayoutProvider method startLayoutTest.
// /////////////////////////////////////////////////////////////////////////////
// Layout Testing
/**
* Import the given KGraph and return a test execution state prepared for a test run with the
* resulting {@link LGraph}. The layout test run methods can immediately be called on the
* returned object.
*
* <p><strong>Note:</strong> This method does not apply the layout back to the original KGraph!</p>
*
* @param elkgraph the KGraph to be used for the layout test run.
* @return an initialized test execution state
*/
public ElkLayered.TestExecutionState startLayoutTest(final ElkNode elkgraph) {
// Import the graph (layeredGraph won't be null since the KGraphImporter always returns an
// LGraph instance, even though the IGraphImporter interface would allow null as a return
// value)
IGraphTransformer<ElkNode> graphImporter = new ElkGraphTransformer();
LGraph layeredGraph = graphImporter.importGraph(elkgraph);
// Prepare a layout test and return the test execution state
return elkLayered.prepareLayoutTest(layeredGraph);
}
use of org.eclipse.elk.alg.layered.graph.LGraph in project elk by eclipse.
the class LayerSweepCrossingMinimizer method initialize.
/**
* Traverses inclusion breadth-first and initializes each Graph.
*/
private List<GraphInfoHolder> initialize(final LGraph rootGraph) {
graphInfoHolders = Lists.newArrayList();
random = rootGraph.getProperty(InternalProperties.RANDOM);
randomSeed = random.nextLong();
List<GraphInfoHolder> graphsToSweepOn = Lists.newLinkedList();
List<LGraph> graphs = Lists.<LGraph>newArrayList(rootGraph);
int i = 0;
while (i < graphs.size()) {
LGraph graph = graphs.get(i);
graph.id = i++;
GraphInfoHolder gData = new GraphInfoHolder(graph, crossMinType, graphInfoHolders);
graphs.addAll(gData.childGraphs());
graphInfoHolders.add(gData);
if (gData.dontSweepInto()) {
graphsToSweepOn.add(0, gData);
}
}
graphsWhoseNodeOrderChanged = Sets.newHashSet();
return graphsToSweepOn;
}
use of org.eclipse.elk.alg.layered.graph.LGraph 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.graph.LGraph 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.alg.layered.graph.LGraph in project elk by eclipse.
the class ComponentsCompactor method compact.
// ------------------------------------------------------------------------------------------------
// public API
// ------------------------------------------------------------------------------------------------
/**
* @param graphs
* the components to be compacted
* @param originalGraphsSize
* the size of the overall graph as it is currently
* @param spacing
* the desired spacing to be preserved between any pair of components
*/
public void compact(final List<LGraph> graphs, final KVector originalGraphsSize, final double spacing) {
// determine the extreme points of the current diagram,
// we will reuse this 'frame' to cut external extensions at appropriate lengths
graphTopLeft = new KVector(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
graphBottomRight = new KVector(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
for (LGraph graph : graphs) {
for (LNode node : graph.getLayerlessNodes()) {
graphTopLeft.x = Math.min(graphTopLeft.x, node.getPosition().x - node.getMargin().left);
graphTopLeft.y = Math.min(graphTopLeft.y, node.getPosition().y - node.getMargin().top);
graphBottomRight.x = Math.max(graphBottomRight.x, node.getPosition().x + node.getSize().x + node.getMargin().right);
graphBottomRight.y = Math.max(graphBottomRight.y, node.getPosition().y + node.getSize().y + node.getMargin().bottom);
}
}
// from the lgraphs, create connected components
IConnectedComponents<LNode, Set<LEdge>> ccs = new InternalConnectedComponents();
for (LGraph graph : graphs) {
IComponent<LNode, Set<LEdge>> c = transformLGraph(graph);
ccs.getComponents().add(c);
((InternalComponent) c).containsRegularNodes |= !c.getExternalExtensionSides().isEmpty();
}
// for every component we create an element in the compactor
compactor = OneDimensionalComponentsCompaction.init(ccs, spacing);
// execute compaction
compactor.compact(new BasicProgressMonitor());
yetAnotherOffset = new KVector();
compactedGraphSize = compactor.getGraphSize();
// apply the positions
for (IComponent<LNode, Set<LEdge>> cc : ccs.getComponents()) {
// retrieve the common offset for the currently handled connected component
KVector offset = compactor.getOffset(cc);
// move it
LGraphUtil.offsetGraph(((InternalComponent) cc).graph, offset.x, offset.y);
// adjust positions of external ports
for (LNode n : ((InternalComponent) cc).getNodes()) {
if (n.getType() == NodeType.EXTERNAL_PORT) {
KVector newPos = getExternalPortPosition(n.getPosition(), n.getProperty(InternalProperties.EXT_PORT_SIDE));
n.getPosition().reset().add(newPos);
}
}
}
// external edges contribute to the graph's size ... however, only certain segments do.
for (IComponent<LNode, Set<LEdge>> cc : ccs.getComponents()) {
for (LEdge e : ((InternalComponent) cc).getExternalEdges()) {
KVectorChain vc = new KVectorChain(e.getBendPoints());
vc.add(0, e.getSource().getAbsoluteAnchor());
vc.add(e.getTarget().getAbsoluteAnchor());
KVector last = null;
for (KVector v : vc) {
if (last == null) {
last = v;
continue;
}
if (DoubleMath.fuzzyEquals(last.x, v.x, EPSILON)) {
yetAnotherOffset.x = Math.min(yetAnotherOffset.x, last.x);
compactedGraphSize.x = Math.max(compactedGraphSize.x, last.x);
} else if (DoubleMath.fuzzyEquals(last.y, v.y, EPSILON)) {
yetAnotherOffset.y = Math.min(yetAnotherOffset.y, last.y);
compactedGraphSize.y = Math.max(compactedGraphSize.y, last.y);
}
last = v;
}
}
}
yetAnotherOffset.negate();
compactedGraphSize.add(yetAnotherOffset);
}
Aggregations