use of org.eclipse.elk.core.service.LayoutMapping 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.service.LayoutMapping in project osate2 by osate.
the class ElkGraphBuilder method buildLayoutGraph.
private LayoutMapping buildLayoutGraph(final DiagramNode rootDiagramNode) {
// Create the graph
final LayoutMapping mapping = new LayoutMapping(null);
final ElkNode rootNode = ElkGraphUtil.createGraph();
rootNode.setProperty(CoreOptions.DIRECTION, Direction.RIGHT);
mapping.setLayoutGraph(rootNode);
// As of 2020-04-06, INCLUDE_CHILDREN causes layout issues. In particular, labels can overlap with children
// https://github.com/eclipse/elk/issues/316
// https://github.com/eclipse/elk/issues/412
rootNode.setProperty(CoreOptions.HIERARCHY_HANDLING, HierarchyHandling.SEPARATE_CHILDREN);
if (rootDiagramNode instanceof AgeDiagram) {
final ElkNode diagramElkNode = ElkGraphUtil.createNode(rootNode);
mapping.getGraphMap().put(diagramElkNode, rootDiagramNode);
createElkGraphElementsForNonLabelChildShapes(rootDiagramNode, diagramElkNode, mapping);
} else if (rootDiagramNode instanceof DiagramElement) {
createElkGraphElementsForElements(Collections.singleton((DiagramElement) rootDiagramNode), rootNode, mapping);
}
createElkGraphElementsForConnections(rootDiagramNode, mapping);
return mapping;
}
use of org.eclipse.elk.core.service.LayoutMapping in project osate2 by osate.
the class ElkGraphBuilder method createElkLabels.
private void createElkLabels(final DiagramElement parentElement, final ElkGraphElement parentLayoutElement, final LayoutMapping mapping) {
// Don't create labels for ElkPort. The bounds of the port contain their labels.
if (parentLayoutElement instanceof ElkPort) {
return;
}
final boolean isConnection = parentElement.getGraphic() instanceof AgeConnection;
final Style style = styleProvider.getStyle(parentElement);
if (style.getPrimaryLabelVisible()) {
// Create Primary Label
if (parentElement.getLabelName() != null) {
final ElkLabel elkLabel = createElkLabel(parentLayoutElement, parentElement.getLabelName(), layoutInfoProvider.getPrimaryLabelSize(parentElement));
if (isConnection) {
if (!layoutConnectionLabels) {
elkLabel.setProperty(CoreOptions.NO_LAYOUT, true);
}
mapping.getGraphMap().put(elkLabel, new PrimaryConnectionLabelReference(parentElement));
}
}
}
// Create label for annotations which are part of the graphic configuration. These are only supported by non-connections.
if (!isConnection && parentElement.getGraphicalConfiguration().getAnnotation() != null) {
createElkLabel(parentLayoutElement, parentElement.getGraphicalConfiguration().getAnnotation(), layoutInfoProvider.getAnnotationLabelSize(parentElement));
}
// Create Secondary Labels
parentElement.getChildren().stream().filter(c -> c.getGraphic() instanceof Label).forEachOrdered(labelElement -> {
final ElkLabel elkLabel = createElkLabel(parentLayoutElement, labelElement.getLabelName(), layoutInfoProvider.getPrimaryLabelSize(labelElement));
if (isConnection) {
if (!layoutConnectionLabels) {
elkLabel.setProperty(CoreOptions.NO_LAYOUT, true);
}
mapping.getGraphMap().put(elkLabel, new SecondaryConnectionLabelReference(labelElement));
}
});
if (parentLayoutElement instanceof ElkNode) {
parentLayoutElement.setProperty(CoreOptions.NODE_LABELS_PLACEMENT, getNodeLabelPlacement(style));
}
}
use of org.eclipse.elk.core.service.LayoutMapping 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.service.LayoutMapping in project elk by eclipse.
the class TextLayoutConnector method buildLayoutGraph.
protected LayoutMapping buildLayoutGraph(XtextEditor xtextEditor, ElkNode rootNode) {
LayoutMapping mapping = new LayoutMapping(xtextEditor);
mapping.setParentElement(rootNode);
mapping.setLayoutGraph(EcoreUtil.copy(rootNode));
return mapping;
}
Aggregations