use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class GmfDiagramLayoutConnector method processConnections.
/**
* Creates new edges and takes care of the labels for each connection identified in the
* {@code buildLayoutGraphRecursively} method.
*
* @param mapping
* the layout mapping
*/
protected void processConnections(final LayoutMapping mapping) {
Map<EReference, ElkEdge> reference2EdgeMap = new HashMap<>();
for (ConnectionEditPart connection : mapping.getProperty(CONNECTIONS)) {
boolean isOppositeEdge = false;
Optional<EdgeLabelPlacement> edgeLabelPlacement = Optional.empty();
ElkEdge edge;
// Check whether the edge belongs to an Ecore reference, which may have opposites.
// This is required for the layout of Ecore diagrams, since the bend points of
// opposite references are kept synchronized by the editor.
EObject modelObject = connection.getNotationView().getElement();
if (modelObject instanceof EReference) {
EReference reference = (EReference) modelObject;
edge = reference2EdgeMap.get(reference.getEOpposite());
if (edge != null) {
edgeLabelPlacement = Optional.of(EdgeLabelPlacement.TAIL);
isOppositeEdge = true;
} else {
edge = ElkGraphUtil.createEdge(null);
reference2EdgeMap.put(reference, edge);
}
} else {
edge = ElkGraphUtil.createEdge(null);
}
BiMap<Object, ElkGraphElement> inverseGraphMap = mapping.getGraphMap().inverse();
// find a proper source node and source port
ElkGraphElement sourceElem;
EditPart sourceObj = connection.getSource();
if (sourceObj instanceof ConnectionEditPart) {
sourceElem = inverseGraphMap.get(((ConnectionEditPart) sourceObj).getSource());
if (sourceElem == null) {
sourceElem = inverseGraphMap.get(((ConnectionEditPart) sourceObj).getTarget());
}
} else {
sourceElem = inverseGraphMap.get(sourceObj);
}
ElkConnectableShape sourceShape = null;
ElkPort sourcePort = null;
ElkNode sourceNode = null;
if (sourceElem instanceof ElkNode) {
sourceNode = (ElkNode) sourceElem;
sourceShape = sourceNode;
} else if (sourceElem instanceof ElkPort) {
sourcePort = (ElkPort) sourceElem;
sourceNode = sourcePort.getParent();
sourceShape = sourcePort;
} else {
continue;
}
// find a proper target node and target port
ElkGraphElement targetElem;
EditPart targetObj = connection.getTarget();
if (targetObj instanceof ConnectionEditPart) {
targetElem = inverseGraphMap.get(((ConnectionEditPart) targetObj).getTarget());
if (targetElem == null) {
targetElem = inverseGraphMap.get(((ConnectionEditPart) targetObj).getSource());
}
} else {
targetElem = inverseGraphMap.get(targetObj);
}
ElkConnectableShape targetShape = null;
ElkNode targetNode = null;
ElkPort targetPort = null;
if (targetElem instanceof ElkNode) {
targetNode = (ElkNode) targetElem;
targetShape = targetNode;
} else if (targetElem instanceof ElkPort) {
targetPort = (ElkPort) targetElem;
targetNode = targetPort.getParent();
targetShape = targetPort;
} else {
continue;
}
// calculate offset for edge and label coordinates
ElkNode edgeContainment = ElkGraphUtil.findLowestCommonAncestor(sourceNode, targetNode);
KVector offset = new KVector();
ElkUtil.toAbsolute(offset, edgeContainment);
if (!isOppositeEdge) {
// set source and target
edge.getSources().add(sourceShape);
edge.getTargets().add(targetShape);
// now that source and target are set, put the edge into the graph
edgeContainment.getContainedEdges().add(edge);
mapping.getGraphMap().put(edge, connection);
// store the current coordinates of the edge
setEdgeLayout(edge, connection, offset);
}
// process edge labels
processEdgeLabels(mapping, connection, edge, edgeLabelPlacement, offset);
}
}
use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class StraightLineEdgeRouter method routeEdges.
/**
* Route edges from node center to node center. Then clip it, to not cross the node.
*/
public void routeEdges(final ElkNode node) {
for (ElkEdge edge : ElkGraphUtil.allOutgoingEdges(node)) {
if (!(edge.getSources().get(0) instanceof ElkPort)) {
ElkNode target = ElkGraphUtil.connectableShapeToNode(edge.getTargets().get(0));
if (!edge.isHierarchical()) {
double sourceX = node.getX() + node.getWidth() / 2;
double sourceY = node.getY() + node.getHeight() / 2;
double targetX = target.getX() + target.getWidth() / 2;
double targetY = target.getY() + target.getHeight() / 2;
// Clipping
KVector vector = new KVector();
vector.x = targetX - sourceX;
vector.y = targetY - sourceY;
KVector sourceClip = new KVector(vector.x, vector.y);
ElkMath.clipVector(sourceClip, node.getWidth(), node.getHeight());
vector.x -= sourceClip.x;
vector.y -= sourceClip.y;
sourceX = targetX - vector.x;
sourceY = targetY - vector.y;
KVector targetClip = new KVector(vector.x, vector.y);
ElkMath.clipVector(targetClip, target.getWidth(), target.getHeight());
vector.x -= targetClip.x;
vector.y -= targetClip.y;
targetX = sourceX + vector.x;
targetY = sourceY + vector.y;
ElkEdgeSection section = ElkGraphUtil.firstEdgeSection(edge, true, true);
section.setStartLocation(sourceX, sourceY);
section.setEndLocation(targetX, targetY);
routeEdges(target);
}
}
}
}
use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class RectPackingLayoutProvider method layout.
/**
* Calculating and applying layout to the model.
*/
@Override
public void layout(final ElkNode layoutGraph, final IElkProgressMonitor progressMonitor) {
progressMonitor.begin("Rectangle Packing", 1);
if (progressMonitor.isLoggingEnabled()) {
progressMonitor.logGraph(layoutGraph, "Input");
}
// The desired aspect ratio.
double aspectRatio = layoutGraph.getProperty(RectPackingOptions.ASPECT_RATIO);
// The strategy for the initial width approximation.
OptimizationGoal goal = layoutGraph.getProperty(RectPackingOptions.OPTIMIZATION_GOAL);
// Option for better width approximation.
boolean lastPlaceShift = layoutGraph.getProperty(RectPackingOptions.LAST_PLACE_SHIFT);
// Option to only do the initial width approximation.
boolean onlyFirstIteration = layoutGraph.getProperty(RectPackingOptions.ONLY_FIRST_ITERATION);
// Option whether the nodes should be expanded to fill the bounding rectangle.
boolean expandNodes = layoutGraph.getProperty(RectPackingOptions.EXPAND_NODES);
// The padding surrounding the drawing.
ElkPadding padding = layoutGraph.getProperty(RectPackingOptions.PADDING);
// The spacing between two nodes.
double nodeNodeSpacing = layoutGraph.getProperty(RectPackingOptions.SPACING_NODE_NODE);
// Whether the nodes are compacted after the initial placement.
boolean compaction = layoutGraph.getProperty(RectPackingOptions.ROW_COMPACTION);
// Whether the nodes should be expanded to fit the aspect ratio during node expansion.
// Only effective if nodes are expanded.
boolean expandToAspectRatio = layoutGraph.getProperty(RectPackingOptions.EXPAND_TO_ASPECT_RATIO);
// Whether interactive layout is activ.
boolean interactive = layoutGraph.getProperty(RectPackingOptions.INTERACTIVE);
// A target width for the algorithm. If this is set the width approximation step is skipped.
double targetWidth = layoutGraph.getProperty(RectPackingOptions.TARGET_WIDTH);
List<ElkNode> rectangles = layoutGraph.getChildren();
DrawingUtil.resetCoordinates(rectangles);
DrawingData drawing;
if (interactive) {
List<ElkNode> fixedNodes = new ArrayList<>();
for (ElkNode elkNode : rectangles) {
if (elkNode.hasProperty(RectPackingOptions.DESIRED_POSITION)) {
fixedNodes.add(elkNode);
}
}
for (ElkNode elkNode : fixedNodes) {
rectangles.remove(elkNode);
}
Collections.sort(fixedNodes, (a, b) -> {
int positionA = a.getProperty(RectPackingOptions.DESIRED_POSITION);
int positionB = b.getProperty(RectPackingOptions.DESIRED_POSITION);
if (positionA == positionB) {
return -1;
} else {
return Integer.compare(positionA, positionB);
}
});
for (ElkNode elkNode : fixedNodes) {
int position = elkNode.getProperty(RectPackingOptions.DESIRED_POSITION);
position = Math.min(position, rectangles.size());
rectangles.add(position, elkNode);
}
int index = 0;
for (ElkNode elkNode : rectangles) {
elkNode.setProperty(RectPackingOptions.CURRENT_POSITION, index);
index++;
}
}
// Get minimum size of parent.
KVector minSize = ElkUtil.effectiveMinSizeConstraintFor(layoutGraph);
// Remove padding to get the space the algorithm can use.
minSize.x -= padding.getHorizontal();
minSize.y -= padding.getVertical();
double maxWidth = minSize.x;
if (targetWidth < 0 || targetWidth < minSize.x) {
// Initial width approximation.
AreaApproximation firstIt = new AreaApproximation(aspectRatio, goal, lastPlaceShift);
drawing = firstIt.approxBoundingBox(rectangles, nodeNodeSpacing, padding);
if (progressMonitor.isLoggingEnabled()) {
progressMonitor.logGraph(layoutGraph, "After approximation");
}
} else {
drawing = new DrawingData(aspectRatio, targetWidth, 0, DrawingDataDescriptor.WHOLE_DRAWING);
}
// Readd padding for next steps.
minSize.x += padding.getHorizontal();
minSize.y += padding.getVertical();
// Placement according to approximated width.
if (!onlyFirstIteration) {
DrawingUtil.resetCoordinates(rectangles);
RowFillingAndCompaction secondIt = new RowFillingAndCompaction(aspectRatio, expandNodes, expandToAspectRatio, compaction, nodeNodeSpacing);
// Modify the initial approximation if necessary.
maxWidth = Math.max(minSize.x, drawing.getDrawingWidth());
// Run placement, compaction, and expansion (if enabled).
drawing = secondIt.start(rectangles, maxWidth, minSize, progressMonitor, layoutGraph);
}
// Final touch.
applyPadding(rectangles, padding);
ElkUtil.resizeNode(layoutGraph, drawing.getDrawingWidth() + padding.getHorizontal(), drawing.getDrawingHeight() + padding.getVertical(), false, true);
// if requested, compute nodes's dimensions, place node labels, ports, port labels, etc.
if (!layoutGraph.getProperty(RectPackingOptions.OMIT_NODE_MICRO_LAYOUT)) {
NodeMicroLayout.forGraph(layoutGraph).execute();
}
if (progressMonitor.isLoggingEnabled()) {
progressMonitor.logGraph(layoutGraph, "Output");
}
progressMonitor.done();
}
use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class ElkGraphImporter method updateGraph.
@Override
public void updateGraph(final Graph g) {
Map<KVector, Pair<Node, ElkNode>> updatedNodeMap = Maps.newHashMap();
// reset graph
g.tEdges = null;
g.tree = null;
// update nodes
for (Node n : g.vertices) {
Pair<Node, ElkNode> original = nodeMap.get(n.originalVertex);
n.originalVertex = n.rect.getCenter();
updatedNodeMap.put(n.originalVertex, original);
}
nodeMap = updatedNodeMap;
}
use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class ElkGraphImporter method importGraph.
@Override
public Graph importGraph(final ElkNode inputGraph) {
elkGraph = inputGraph;
nodeMap = Maps.newHashMap();
// calculate margins
ElkGraphAdapter adapter = ElkGraphAdapters.adapt(elkGraph);
NodeDimensionCalculation.calculateNodeMargins(adapter);
// retrieve layout options
String preferredRootID = elkGraph.getProperty(SporeCompactionOptions.PROCESSING_ORDER_PREFERRED_ROOT);
SpanningTreeCostFunction costFunctionID = elkGraph.getProperty(SporeCompactionOptions.PROCESSING_ORDER_SPANNING_TREE_COST_FUNCTION);
TreeConstructionStrategy treeConstructionStrategy = elkGraph.getProperty(SporeCompactionOptions.PROCESSING_ORDER_TREE_CONSTRUCTION);
CompactionStrategy compactionStrategy = elkGraph.getProperty(SporeCompactionOptions.COMPACTION_COMPACTION_STRATEGY);
RootSelection rootSelection = elkGraph.getProperty(SporeCompactionOptions.PROCESSING_ORDER_ROOT_SELECTION);
spacingNodeNode = elkGraph.getProperty(SporeCompactionOptions.SPACING_NODE_NODE);
ICostFunction costFunction = centerDistance;
switch(costFunctionID) {
case CENTER_DISTANCE:
costFunction = centerDistance;
break;
case CIRCLE_UNDERLAP:
costFunction = circleUnderlap;
break;
case RECTANGLE_UNDERLAP:
costFunction = rectangleUnderlap;
break;
case INVERTED_OVERLAP:
costFunction = invertedOverlap;
break;
case MINIMUM_ROOT_DISTANCE:
costFunction = minimumRootDistance;
break;
default:
throw new IllegalArgumentException("No implementation available for " + costFunctionID.toString());
}
// instantiate Graph
graph = new Graph(costFunction, treeConstructionStrategy, compactionStrategy);
graph.setProperty(InternalProperties.DEBUG_SVG, elkGraph.getProperty(SporeCompactionOptions.DEBUG_MODE));
graph.orthogonalCompaction = elkGraph.getProperty(SporeCompactionOptions.COMPACTION_ORTHOGONAL);
if (elkGraph.getChildren().isEmpty()) {
// don't bother
return graph;
}
// create Nodes representing the ElkNodes
for (ElkNode elkNode : elkGraph.getChildren()) {
double halfWidth = elkNode.getWidth() / 2;
double halfHeight = elkNode.getHeight() / 2;
KVector vertex = new KVector(elkNode.getX() + halfWidth, elkNode.getY() + halfHeight);
// randomly shift identical points a tiny bit to make them unique
while (nodeMap.containsKey(vertex)) {
// SUPPRESS CHECKSTYLE NEXT 1 MagicNumber
vertex.add((Math.random() - 0.5) * 0.001, (Math.random() - 0.5) * 0.001);
// If two positions were identical, their corresponding edge in the spanning tree would be
// of zero length, had no direction, and couldn't be scaled by anything.
}
ElkMargin margin = elkNode.getProperty(CoreOptions.MARGINS);
Node node = new Node(vertex, new ElkRectangle(vertex.x - halfWidth - spacingNodeNode / 2 - margin.left, vertex.y - halfHeight - spacingNodeNode / 2 - margin.top, elkNode.getWidth() + spacingNodeNode + margin.getHorizontal(), elkNode.getHeight() + spacingNodeNode + margin.getVertical()));
graph.vertices.add(node);
nodeMap.put(vertex, Pair.of(node, elkNode));
}
// spanning tree root selection method
switch(rootSelection) {
case FIXED:
if (preferredRootID == null) {
// get first Node in list if no ID specified
graph.preferredRoot = graph.vertices.get(0);
} else {
// find Node associated with the ElkNode containing the ID
for (Node node : graph.vertices) {
String id = nodeMap.get(node.originalVertex).getSecond().getIdentifier();
if (id != null && id.equals(preferredRootID)) {
graph.preferredRoot = node;
}
}
}
break;
case CENTER_NODE:
// find node that is most central in the drawing
KVector center = new KVector(elkGraph.getWidth(), elkGraph.getHeight());
// CHECKSTYLEOFF MagicNumber
center.scale(0.5);
center.add(elkGraph.getX(), elkGraph.getY());
double closest = Double.POSITIVE_INFINITY;
for (Node node : graph.vertices) {
double distance = node.originalVertex.distance(center);
if (distance < closest) {
closest = distance;
graph.preferredRoot = node;
}
}
break;
default:
throw new IllegalArgumentException("No implementation available for " + rootSelection.toString());
}
return graph;
}
Aggregations