use of org.eclipse.elk.core.options.PortSide in project osate2 by osate.
the class DiagramElementLayoutUtil method layout.
private static void layout(final DiagramModification m, final Collection<? extends DiagramNode> nodesToLayout, final StyleProvider styleProvider, final LayoutInfoProvider layoutInfoProvider, final LayoutOptions options) {
Objects.requireNonNull(nodesToLayout, "nodesToLayout must not be null");
try {
// Layout the nodes
final RecursiveGraphLayoutEngine layoutEngine = new RecursiveGraphLayoutEngine();
for (final DiagramNode dn : nodesToLayout) {
LayoutMapping mapping;
ElkNode layoutGraph;
// Perform the first layout. This layout will not include nested ports. This will allow ELK additional flexibility when determining port
// placement.
mapping = ElkGraphBuilder.buildLayoutGraph(dn, styleProvider, layoutInfoProvider, options, !options.layoutPortsOnDefaultSides, ElkGraphBuilder.FixedPortPositionProvider.NO_OP);
layoutGraph = mapping.getLayoutGraph();
layoutGraph.setProperty(CoreOptions.ALGORITHM, LAYOUT_ALGORITHM);
applyProperties(dn, mapping);
LayoutDebugUtil.saveElkGraphToDebugProject(layoutGraph, "pass1");
layoutEngine.layout(layoutGraph, new BasicProgressMonitor());
// nested ports and performing edge routing.
if (layoutGraph.getProperty(AgeLayoutOptions.NESTED_PORTS_WERE_OMITTED)) {
final LayoutMapping initialLayoutMapping = mapping;
mapping = ElkGraphBuilder.buildLayoutGraph(dn, styleProvider, layoutInfoProvider, options, false, new ElkGraphBuilder.FixedPortPositionProvider() {
@Override
public PortSide getPortSide(final DiagramElement de) {
final ElkGraphElement ge = initialLayoutMapping.getGraphMap().inverse().get(de);
if (ge instanceof ElkPort) {
return ge.getProperty(CoreOptions.PORT_SIDE);
}
return null;
}
@Override
public Double getPortPosition(final DiagramElement de) {
final ElkGraphElement ge = initialLayoutMapping.getGraphMap().inverse().get(de);
if (ge instanceof ElkPort) {
final ElkPort port = (ElkPort) ge;
final PortSide ps = port.getProperty(CoreOptions.PORT_SIDE);
if (PortSide.SIDES_EAST_WEST.contains(ps)) {
return port.getY();
} else {
return port.getX();
}
}
return null;
}
});
layoutGraph = mapping.getLayoutGraph();
layoutGraph.setProperty(CoreOptions.ALGORITHM, LAYOUT_ALGORITHM);
applyProperties(dn, mapping);
LayoutDebugUtil.saveElkGraphToDebugProject(layoutGraph, "pass2");
layoutEngine.layout(layoutGraph, new BasicProgressMonitor());
}
LayoutDebugUtil.saveElkGraphToDebugProject(layoutGraph, "final");
applyShapeLayout(mapping, m);
applyConnectionLayout(mapping, m);
// Layout feature self loop connections. These are omitted from the ELK based layout.
dn.getAllDiagramNodes().filter(DiagramElementLayoutUtil::isFeatureSelfLoopConnection).map(DiagramElement.class::cast).forEachOrdered(de -> layoutFeatureSelfLoopConnection(de, m, layoutInfoProvider));
}
} catch (final RuntimeException ex) {
// If a layout error occurs, display the exception but do not rethrow. This is so that the operation that attempted to do the layout will continue.
// This is important because otherwise simple operations such a adding elements to the diagram will completely fail. Suppressing the error will
// degrade performance but allow the user to keep working and should ensure things stay in a valid state.
// It would be best for other parts of the code to handle exceptions properly to avoid entering into an invalid state but this is the best
// workaround.
final Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, "A layout error occured.", ex);
StatusManager.getManager().handle(status, StatusManager.SHOW | StatusManager.LOG);
}
}
use of org.eclipse.elk.core.options.PortSide in project osate2 by osate.
the class ElkGraphBuilder method createElkPortsForElements.
/**
* Before calling this method, all labels for the parent node should have already been created and the node labels placement property must be set for the parent.
* @param elements
* @param parent
* @param mapping
*/
private void createElkPortsForElements(final Collection<DiagramElement> elements, final ElkNode parent, final LayoutMapping mapping) {
final EnumSet<NodeLabelPlacement> nodeLabelPlacement = parent.getProperty(CoreOptions.NODE_LABELS_PLACEMENT);
final boolean labelsAtTop = nodeLabelPlacement != null && nodeLabelPlacement.contains(NodeLabelPlacement.V_TOP);
final double topPadding = labelsAtTop ? parent.getLabels().stream().mapToDouble(l -> l.getY() + l.getHeight()).sum() : 0.0;
// Group children by the port side to which they should be assigned.
final List<DiagramElement> dockedShapes = elements.stream().filter(dockedShapeFilter).collect(Collectors.toList());
final boolean diagramElementIncludesNestedPorts = dockedShapes.stream().flatMap(de -> de.getChildren().stream()).anyMatch(dockedShapeFilter);
// Set the flag to indicate that there are nested ports which will not be included in the final layout graph
if (omitNestedPorts && diagramElementIncludesNestedPorts) {
mapping.getLayoutGraph().setProperty(AgeLayoutOptions.NESTED_PORTS_WERE_OMITTED, true);
}
// Set port constraints and graph hierarchy handling of the parent based on whether the diagram element actually has nested ports.
final boolean hasNestedPorts = !omitNestedPorts && diagramElementIncludesNestedPorts;
PortConstraints portConstraints;
if (dockedShapes.size() == 0) {
// Don't constrain ports if there aren't any. As of 2017-10-11, some other values can affect the layout even if the node does not contain ports.
portConstraints = PortConstraints.FREE;
} else {
if (hasNestedPorts || options.layoutPortsOnDefaultSides) {
portConstraints = PortConstraints.FIXED_POS;
} else {
portConstraints = PortConstraints.FREE;
}
}
parent.setProperty(CoreOptions.PORT_CONSTRAINTS, portConstraints);
final Map<PortSide, List<DiagramElement>> groupedDockedElements = dockedShapes.stream().collect(Collectors.groupingBy(de -> getPortSide(de, hasNestedPorts), HashMap::new, Collectors.toCollection(ArrayList::new)));
// Determine padding
// Need to pad both left and right sides equally if ELK is determining the side of ports. Otherwise, the space for the
// port may overlap with shapes. This is likely caused by adjusting the border offset of ports
// to lay out ports within the bounds of the containing shape
final boolean padOppositeSides = !portConstraints.isSideFixed();
final ElkPadding parentPadding = new ElkPadding(parent.getParent() == null || parent.getParent().getParent() == null ? 0.0 : portAndContentsPadding);
for (final Entry<PortSide, List<DiagramElement>> entry : groupedDockedElements.entrySet()) {
final PortSide side = entry.getKey();
double maxSize = 0;
for (final DiagramElement de : entry.getValue()) {
maxSize = Math.max(maxSize, getOrthogonalSize(de, side));
}
// Update padding for the side
final double sidePadding = maxSize + portAndContentsPadding;
switch(side) {
case NORTH:
parentPadding.top = Math.max(parentPadding.top, sidePadding);
break;
case SOUTH:
parentPadding.bottom = Math.max(parentPadding.bottom, sidePadding);
break;
case EAST:
parentPadding.right = Math.max(parentPadding.right, sidePadding);
if (padOppositeSides) {
parentPadding.left = Math.max(parentPadding.left, sidePadding);
}
break;
case WEST:
parentPadding.left = Math.max(parentPadding.left, sidePadding);
if (padOppositeSides) {
parentPadding.right = Math.max(parentPadding.right, sidePadding);
}
break;
default:
// Ignore
break;
}
}
// Create and position the ports
for (final Entry<PortSide, List<DiagramElement>> portSideToElementsEntry : groupedDockedElements.entrySet()) {
final PortSide side = portSideToElementsEntry.getKey();
final double additionalPadding;
if (PortSide.SIDES_NORTH_SOUTH.contains(side)) {
additionalPadding = Math.max(parentPadding.left, parentPadding.right);
} else {
additionalPadding = topPadding;
}
createAndPositionPorts(parent, portSideToElementsEntry.getValue(), portSideToElementsEntry.getKey(), additionalPadding, mapping, hasNestedPorts);
}
// Set the padding
parent.setProperty(CoreOptions.PADDING, parentPadding);
}
use of org.eclipse.elk.core.options.PortSide in project elk by eclipse.
the class HierarchicalPortOrthogonalEdgeRouter method connectNodeToDummy.
/**
* Adds a port to the given node and connects that to the given dummy node.
*
* @param node the node to connect to the dummy.
* @param dummy the external port dummy to connect the node to.
*/
private void connectNodeToDummy(final LGraph layeredGraph, final LNode node, final LNode dummy) {
// First, add a port to the node. The port side depends on the node's hierarchical port side
LPort outPort = new LPort();
outPort.setNode(node);
PortSide extPortSide = node.getProperty(InternalProperties.EXT_PORT_SIDE);
outPort.setSide(extPortSide);
// Find the dummy node's port
LPort inPort = dummy.getPorts().get(0);
// Connect the two nodes
LEdge edge = new LEdge();
edge.setSource(outPort);
edge.setTarget(inPort);
}
use of org.eclipse.elk.core.options.PortSide in project elk by eclipse.
the class HierarchicalPortOrthogonalEdgeRouter method correctSlantedEdgeSegments.
/**
* Goes over the eastern and western hierarchical dummy nodes in the given layer and checks
* whether their incident edges have slanted segments. Note that only the first and last
* segment needs to be checked.
*
* @param layer the layer.
*/
private void correctSlantedEdgeSegments(final Layer layer) {
for (LNode node : layer) {
if (node.getType() != NodeType.EXTERNAL_PORT) {
// We're only looking for hierarchical port dummies
continue;
}
PortSide extPortSide = node.getProperty(InternalProperties.EXT_PORT_SIDE);
if (extPortSide == PortSide.EAST || extPortSide == PortSide.WEST) {
for (LEdge edge : node.getConnectedEdges()) {
KVectorChain bendPoints = edge.getBendPoints();
if (bendPoints.isEmpty()) {
// TODO: The edge has no bend points yet, but may still be slanted. Handle that!
continue;
}
// Correct a slanted segment connected to the source port if it belongs to our node
LPort sourcePort = edge.getSource();
if (sourcePort.getNode() == node) {
KVector firstBendPoint = bendPoints.getFirst();
firstBendPoint.y = sourcePort.getAbsoluteAnchor().y;
}
// Correct a slanted segment connected to the target port if it belongs to our node
LPort targetPort = edge.getTarget();
if (targetPort.getNode() == node) {
KVector lastBendPoint = bendPoints.getLast();
lastBendPoint.y = targetPort.getAbsoluteAnchor().y;
}
}
}
}
}
use of org.eclipse.elk.core.options.PortSide in project elk by eclipse.
the class HierarchicalPortOrthogonalEdgeRouter method routeEdges.
// /////////////////////////////////////////////////////////////////////////////
// STEP 3: EDGE ROUTING
/**
* Routes nothern and southern hierarchical port edges and ajusts the graph's height and
* offsets accordingly.
*
* @param monitor the progress monitor we're using.
* @param layeredGraph the layered graph.
* @param northSouthDummies the collection of restored northern and southern port dummies.
*/
private void routeEdges(final IElkProgressMonitor monitor, final LGraph layeredGraph, final Iterable<LNode> northSouthDummies) {
// Prepare south and target layers for northern and southern routing
Set<LNode> northernSourceLayer = Sets.newLinkedHashSet();
Set<LNode> northernTargetLayer = Sets.newLinkedHashSet();
Set<LNode> southernSourceLayer = Sets.newLinkedHashSet();
Set<LNode> southernTargetLayer = Sets.newLinkedHashSet();
// Find some routing parameters
double nodeSpacing = layeredGraph.getProperty(LayeredOptions.SPACING_NODE_NODE).doubleValue();
double edgeSpacing = layeredGraph.getProperty(LayeredOptions.SPACING_EDGE_EDGE).doubleValue();
// connected to
for (LNode hierarchicalPortDummy : northSouthDummies) {
PortSide portSide = hierarchicalPortDummy.getProperty(InternalProperties.EXT_PORT_SIDE);
if (portSide == PortSide.NORTH) {
northernTargetLayer.add(hierarchicalPortDummy);
for (LEdge edge : hierarchicalPortDummy.getIncomingEdges()) {
northernSourceLayer.add(edge.getSource().getNode());
}
} else if (portSide == PortSide.SOUTH) {
southernTargetLayer.add(hierarchicalPortDummy);
for (LEdge edge : hierarchicalPortDummy.getIncomingEdges()) {
southernSourceLayer.add(edge.getSource().getNode());
}
}
}
// Northern routing
if (!northernSourceLayer.isEmpty()) {
// Route the edges using a south-to-north orthogonal edge router
OrthogonalRoutingGenerator routingGenerator = new OrthogonalRoutingGenerator(RoutingDirection.SOUTH_TO_NORTH, edgeSpacing, "extnorth");
int slots = routingGenerator.routeEdges(monitor, layeredGraph, northernSourceLayer, 0, northernTargetLayer, -nodeSpacing - layeredGraph.getOffset().y);
// If anything was routed, adjust the graph's offset and height
if (slots > 0) {
northernExtPortEdgeRoutingHeight = nodeSpacing + (slots - 1) * edgeSpacing;
layeredGraph.getOffset().y += northernExtPortEdgeRoutingHeight;
layeredGraph.getSize().y += northernExtPortEdgeRoutingHeight;
}
}
// Southern routing
if (!southernSourceLayer.isEmpty()) {
// Route the edges using a north-to-south orthogonal edge router
OrthogonalRoutingGenerator routingGenerator = new OrthogonalRoutingGenerator(RoutingDirection.NORTH_TO_SOUTH, edgeSpacing, "extsouth");
int slots = routingGenerator.routeEdges(monitor, layeredGraph, southernSourceLayer, 0, southernTargetLayer, layeredGraph.getSize().y + nodeSpacing - layeredGraph.getOffset().y);
// Adjust graph height.
if (slots > 0) {
layeredGraph.getSize().y += nodeSpacing + (slots - 1) * edgeSpacing;
}
}
}
Aggregations