use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class ElkGraphLayoutTransferrer method applyLayout.
/**
* Applies the layout information contained in the given LGraph to the ElkGraph elements it was
* created from. All source ElkGraph elements are expected to be accessible through their LGraph
* counterparts through the {@link InternalProperties#ORIGIN} property.
*
* @param lgraph the LGraph whose layout information to apply.
*/
public void applyLayout(final LGraph lgraph) {
Object graphOrigin = lgraph.getProperty(InternalProperties.ORIGIN);
if (!(graphOrigin instanceof ElkNode)) {
return;
}
// The ElkNode that represents this graph in the original ElkGraph
ElkNode parentElkNode = (ElkNode) graphOrigin;
// The LNode that represents this graph in the upper hierarchy level, if any
LNode parentLNode = (LNode) lgraph.getParentNode();
// Get the offset to be added to all coordinates
KVector offset = new KVector(lgraph.getOffset());
// Adjust offset (and with it the positions) by the requested padding
LPadding lPadding = lgraph.getPadding();
offset.x += lPadding.left;
offset.y += lPadding.top;
// Set node padding, if it was computed during layout
final EnumSet<SizeOptions> sizeOptions = parentElkNode.getProperty(LayeredOptions.NODE_SIZE_OPTIONS);
if (sizeOptions.contains(SizeOptions.COMPUTE_PADDING)) {
ElkPadding padding = parentElkNode.getProperty(LayeredOptions.PADDING);
padding.setBottom(lPadding.bottom);
padding.setTop(lPadding.top);
padding.setLeft(lPadding.left);
padding.setRight(lPadding.right);
}
// Along the way, we collect the list of edges to be processed later
List<LEdge> edgeList = Lists.newArrayList();
// Process the nodes
for (LNode lnode : lgraph.getLayerlessNodes()) {
if (representsNode(lnode)) {
applyNodeLayout(lnode, offset);
} else if (representsExternalPort(lnode) && parentLNode == null) {
// We have an external port here on the top-most hierarchy level of the current (possibly
// hierarchical) layout run; set its position
ElkPort elkport = (ElkPort) lnode.getProperty(InternalProperties.ORIGIN);
KVector portPosition = LGraphUtil.getExternalPortPosition(lgraph, lnode, elkport.getWidth(), elkport.getHeight());
elkport.setLocation(portPosition.x, portPosition.y);
}
// correctly)
for (LPort port : lnode.getPorts()) {
port.getOutgoingEdges().stream().filter(edge -> !LGraphUtil.isDescendant(edge.getTarget().getNode(), lnode)).forEach(edge -> edgeList.add(edge));
}
}
// Collect edges that go from the current graph's representing LNode down into its descendants
if (parentLNode != null) {
for (LPort port : parentLNode.getPorts()) {
port.getOutgoingEdges().stream().filter(edge -> LGraphUtil.isDescendant(edge.getTarget().getNode(), parentLNode)).forEach(edge -> edgeList.add(edge));
}
}
// Iterate through all edges
EdgeRouting routing = parentElkNode.getProperty(LayeredOptions.EDGE_ROUTING);
for (LEdge ledge : edgeList) {
applyEdgeLayout(ledge, routing, offset, lPadding);
}
// Setup the parent node
applyParentNodeLayout(lgraph);
// Process nested subgraphs
for (LNode lnode : lgraph.getLayerlessNodes()) {
LGraph nestedGraph = lnode.getNestedGraph();
if (nestedGraph != null) {
applyLayout(nestedGraph);
}
}
}
use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class ElkGraphLayoutTransferrer method calculateHierarchicalOffset.
/**
* If the coordinates of an edge must be relative to a different node than they are in the algorithm, this
* method returns the correct offset to translate from the algorithm's coordinate system to the necessary
* target coordinate system.
*
* @return the offset vector, which may simply be zero (but not {@code null}). Must not be modified.
*/
private KVector calculateHierarchicalOffset(final LEdge ledge) {
LGraph targetCoordinateSystem = ledge.getProperty(InternalProperties.COORDINATE_SYSTEM_ORIGIN);
if (targetCoordinateSystem != null) {
KVector result = new KVector();
// Edges this method is called on are always in the coordinate system of their source
LGraph currentGraph = ledge.getSource().getNode().getGraph();
while (currentGraph != targetCoordinateSystem) {
// The current graph should always have an upper level if we have not reached the target graph yet;
LNode representingNode = currentGraph.getParentNode();
currentGraph = representingNode.getGraph();
result.add(representingNode.getPosition()).add(currentGraph.getOffset()).add(currentGraph.getPadding().left, currentGraph.getPadding().top);
}
return result;
}
// No coordinate system conversion is required
return ZERO_OFFSET;
}
use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class CommentPostprocessor method process.
/**
* Process a node with its connected comment boxes.
*
* @param node a normal node
* @param topBoxes a list of boxes to be placed on top, or {@code null}
* @param bottomBoxes a list of boxes to be placed in the bottom, or {@code null}
*/
private void process(final LNode node, final List<LNode> topBoxes, final List<LNode> bottomBoxes) {
KVector nodePos = node.getPosition();
KVector nodeSize = node.getSize();
LMargin margin = node.getMargin();
double commentCommentSpacing = Spacings.getIndividualOrDefault(node, LayeredOptions.SPACING_COMMENT_COMMENT);
if (topBoxes != null) {
// determine the total width and maximal height of the top boxes
double boxesWidth = commentCommentSpacing * (topBoxes.size() - 1);
double maxHeight = 0;
for (LNode box : topBoxes) {
boxesWidth += box.getSize().x;
maxHeight = Math.max(maxHeight, box.getSize().y);
}
// place the boxes on top of the node, horizontally centered around the node itself
double x = nodePos.x - (boxesWidth - nodeSize.x) / 2;
double baseLine = nodePos.y - margin.top + maxHeight;
double anchorInc = nodeSize.x / (topBoxes.size() + 1);
double anchorX = anchorInc;
for (LNode box : topBoxes) {
box.getPosition().x = x;
box.getPosition().y = baseLine - box.getSize().y;
x += box.getSize().x + commentCommentSpacing;
// set source and target point for the connecting edge
LPort boxPort = getBoxPort(box);
boxPort.getPosition().x = box.getSize().x / 2 - boxPort.getAnchor().x;
boxPort.getPosition().y = box.getSize().y;
LPort nodePort = box.getProperty(InternalProperties.COMMENT_CONN_PORT);
if (nodePort.getDegree() == 1) {
nodePort.getPosition().x = anchorX - nodePort.getAnchor().x;
nodePort.getPosition().y = 0;
nodePort.setNode(node);
}
anchorX += anchorInc;
}
}
if (bottomBoxes != null) {
// determine the total width and maximal height of the bottom boxes
double boxesWidth = commentCommentSpacing * (bottomBoxes.size() - 1);
double maxHeight = 0;
for (LNode box : bottomBoxes) {
boxesWidth += box.getSize().x;
maxHeight = Math.max(maxHeight, box.getSize().y);
}
// place the boxes in the bottom of the node, horizontally centered around the node itself
double x = nodePos.x - (boxesWidth - nodeSize.x) / 2;
double baseLine = nodePos.y + nodeSize.y + margin.bottom - maxHeight;
double anchorInc = nodeSize.x / (bottomBoxes.size() + 1);
double anchorX = anchorInc;
for (LNode box : bottomBoxes) {
box.getPosition().x = x;
box.getPosition().y = baseLine;
x += box.getSize().x + commentCommentSpacing;
// set source and target point for the connecting edge
LPort boxPort = getBoxPort(box);
boxPort.getPosition().x = box.getSize().x / 2 - boxPort.getAnchor().x;
boxPort.getPosition().y = 0;
LPort nodePort = box.getProperty(InternalProperties.COMMENT_CONN_PORT);
if (nodePort.getDegree() == 1) {
nodePort.getPosition().x = anchorX - nodePort.getAnchor().x;
nodePort.getPosition().y = nodeSize.y;
nodePort.setNode(node);
}
anchorX += anchorInc;
}
}
}
use of org.eclipse.elk.core.math.KVector 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.math.KVector in project elk by eclipse.
the class EndLabelPreprocessor method updateNodeMargins.
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node Margins
/**
* Updates the node's margins to account for its end labels.
*/
private void updateNodeMargins(final LNode node, final LabelCell[] labelCells) {
LMargin nodeMargin = node.getMargin();
KVector nodeSize = node.getSize();
// Calculate the rectangle that describes the node's current margin
ElkRectangle nodeMarginRectangle = new ElkRectangle(-nodeMargin.left, -nodeMargin.top, nodeMargin.left + nodeSize.x + nodeMargin.right, nodeMargin.top + nodeSize.y + nodeMargin.bottom);
// Union the rectangle with each rectangle that describes a label cell
for (LabelCell labelCell : labelCells) {
if (labelCell != null) {
nodeMarginRectangle.union(labelCell.getCellRectangle());
}
}
// Reapply the new rectangle to the margin
nodeMargin.left = -nodeMarginRectangle.x;
nodeMargin.top = -nodeMarginRectangle.y;
nodeMargin.right = nodeMarginRectangle.width - nodeMargin.left - nodeSize.x;
nodeMargin.bottom = nodeMarginRectangle.height - nodeMargin.top - nodeSize.y;
}
Aggregations