Search in sources :

Example 1 with LayoutMapping

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);
    }
}
Also used : IStatus(org.eclipse.core.runtime.IStatus) Status(org.eclipse.core.runtime.Status) DiagramNode(org.osate.ge.internal.diagram.runtime.DiagramNode) ElkNode(org.eclipse.elk.graph.ElkNode) ElkPort(org.eclipse.elk.graph.ElkPort) RecursiveGraphLayoutEngine(org.eclipse.elk.core.RecursiveGraphLayoutEngine) DiagramElement(org.osate.ge.internal.diagram.runtime.DiagramElement) BasicProgressMonitor(org.eclipse.elk.core.util.BasicProgressMonitor) PortSide(org.eclipse.elk.core.options.PortSide) LayoutMapping(org.eclipse.elk.core.service.LayoutMapping) ElkGraphElement(org.eclipse.elk.graph.ElkGraphElement)

Example 2 with LayoutMapping

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;
}
Also used : DiagramElement(org.osate.ge.internal.diagram.runtime.DiagramElement) ElkNode(org.eclipse.elk.graph.ElkNode) AgeDiagram(org.osate.ge.internal.diagram.runtime.AgeDiagram) LayoutMapping(org.eclipse.elk.core.service.LayoutMapping)

Example 3 with LayoutMapping

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));
    }
}
Also used : CoreOptions(org.eclipse.elk.core.options.CoreOptions) DiagramElement(org.osate.ge.internal.diagram.runtime.DiagramElement) PortSide(org.eclipse.elk.core.options.PortSide) Dimension(org.osate.ge.graphics.Dimension) HashMap(java.util.HashMap) LayoutMapping(org.eclipse.elk.core.service.LayoutMapping) ElkNode(org.eclipse.elk.graph.ElkNode) ElkPort(org.eclipse.elk.graph.ElkPort) ArrayList(java.util.ArrayList) Style(org.osate.ge.graphics.Style) DockArea(org.osate.ge.internal.diagram.runtime.DockArea) ElkPadding(org.eclipse.elk.core.math.ElkPadding) Map(java.util.Map) AgeShape(org.osate.ge.graphics.internal.AgeShape) DiagramElementPredicates(org.osate.ge.internal.diagram.runtime.DiagramElementPredicates) HierarchyHandling(org.eclipse.elk.core.options.HierarchyHandling) EnumSet(java.util.EnumSet) PortConstraints(org.eclipse.elk.core.options.PortConstraints) ElkLabel(org.eclipse.elk.graph.ElkLabel) Predicate(java.util.function.Predicate) KVector(org.eclipse.elk.core.math.KVector) Collection(java.util.Collection) Label(org.osate.ge.graphics.internal.Label) SizeOptions(org.eclipse.elk.core.options.SizeOptions) EcoreUtil(org.eclipse.emf.ecore.util.EcoreUtil) SizeConstraint(org.eclipse.elk.core.options.SizeConstraint) Collectors(java.util.stream.Collectors) ElkConnectableShape(org.eclipse.elk.graph.ElkConnectableShape) DiagramElementUtil(org.osate.ge.internal.util.DiagramElementUtil) DockingPosition(org.osate.ge.DockingPosition) Objects(java.util.Objects) AgeDiagram(org.osate.ge.internal.diagram.runtime.AgeDiagram) List(java.util.List) ElkGraphElement(org.eclipse.elk.graph.ElkGraphElement) NodeLabelPlacement(org.eclipse.elk.core.options.NodeLabelPlacement) Direction(org.eclipse.elk.core.options.Direction) Entry(java.util.Map.Entry) Optional(java.util.Optional) ElkEdge(org.eclipse.elk.graph.ElkEdge) AgeConnection(org.osate.ge.graphics.internal.AgeConnection) ElkGraphUtil(org.eclipse.elk.graph.util.ElkGraphUtil) StyleProvider(org.osate.ge.internal.diagram.runtime.styling.StyleProvider) Collections(java.util.Collections) DiagramNode(org.osate.ge.internal.diagram.runtime.DiagramNode) AgeConnection(org.osate.ge.graphics.internal.AgeConnection) ElkNode(org.eclipse.elk.graph.ElkNode) ElkLabel(org.eclipse.elk.graph.ElkLabel) ElkPort(org.eclipse.elk.graph.ElkPort) ElkLabel(org.eclipse.elk.graph.ElkLabel) Label(org.osate.ge.graphics.internal.Label) Style(org.osate.ge.graphics.Style)

Example 4 with LayoutMapping

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);
}
Also used : CoreOptions(org.eclipse.elk.core.options.CoreOptions) DiagramElement(org.osate.ge.internal.diagram.runtime.DiagramElement) PortSide(org.eclipse.elk.core.options.PortSide) Dimension(org.osate.ge.graphics.Dimension) HashMap(java.util.HashMap) LayoutMapping(org.eclipse.elk.core.service.LayoutMapping) ElkNode(org.eclipse.elk.graph.ElkNode) ElkPort(org.eclipse.elk.graph.ElkPort) ArrayList(java.util.ArrayList) Style(org.osate.ge.graphics.Style) DockArea(org.osate.ge.internal.diagram.runtime.DockArea) ElkPadding(org.eclipse.elk.core.math.ElkPadding) Map(java.util.Map) AgeShape(org.osate.ge.graphics.internal.AgeShape) DiagramElementPredicates(org.osate.ge.internal.diagram.runtime.DiagramElementPredicates) HierarchyHandling(org.eclipse.elk.core.options.HierarchyHandling) EnumSet(java.util.EnumSet) PortConstraints(org.eclipse.elk.core.options.PortConstraints) ElkLabel(org.eclipse.elk.graph.ElkLabel) Predicate(java.util.function.Predicate) KVector(org.eclipse.elk.core.math.KVector) Collection(java.util.Collection) Label(org.osate.ge.graphics.internal.Label) SizeOptions(org.eclipse.elk.core.options.SizeOptions) EcoreUtil(org.eclipse.emf.ecore.util.EcoreUtil) SizeConstraint(org.eclipse.elk.core.options.SizeConstraint) Collectors(java.util.stream.Collectors) ElkConnectableShape(org.eclipse.elk.graph.ElkConnectableShape) DiagramElementUtil(org.osate.ge.internal.util.DiagramElementUtil) DockingPosition(org.osate.ge.DockingPosition) Objects(java.util.Objects) AgeDiagram(org.osate.ge.internal.diagram.runtime.AgeDiagram) List(java.util.List) ElkGraphElement(org.eclipse.elk.graph.ElkGraphElement) NodeLabelPlacement(org.eclipse.elk.core.options.NodeLabelPlacement) Direction(org.eclipse.elk.core.options.Direction) Entry(java.util.Map.Entry) Optional(java.util.Optional) ElkEdge(org.eclipse.elk.graph.ElkEdge) AgeConnection(org.osate.ge.graphics.internal.AgeConnection) ElkGraphUtil(org.eclipse.elk.graph.util.ElkGraphUtil) StyleProvider(org.osate.ge.internal.diagram.runtime.styling.StyleProvider) Collections(java.util.Collections) DiagramNode(org.osate.ge.internal.diagram.runtime.DiagramNode) ArrayList(java.util.ArrayList) PortConstraints(org.eclipse.elk.core.options.PortConstraints) DiagramElement(org.osate.ge.internal.diagram.runtime.DiagramElement) NodeLabelPlacement(org.eclipse.elk.core.options.NodeLabelPlacement) ArrayList(java.util.ArrayList) List(java.util.List) PortSide(org.eclipse.elk.core.options.PortSide) ElkPadding(org.eclipse.elk.core.math.ElkPadding)

Example 5 with LayoutMapping

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;
}
Also used : LayoutMapping(org.eclipse.elk.core.service.LayoutMapping)

Aggregations

LayoutMapping (org.eclipse.elk.core.service.LayoutMapping)11 ElkNode (org.eclipse.elk.graph.ElkNode)9 DiagramElement (org.osate.ge.internal.diagram.runtime.DiagramElement)8 Collection (java.util.Collection)7 KVector (org.eclipse.elk.core.math.KVector)7 PortSide (org.eclipse.elk.core.options.PortSide)7 ElkGraphElement (org.eclipse.elk.graph.ElkGraphElement)7 ElkLabel (org.eclipse.elk.graph.ElkLabel)7 ElkPort (org.eclipse.elk.graph.ElkPort)7 AgeDiagram (org.osate.ge.internal.diagram.runtime.AgeDiagram)7 DiagramNode (org.osate.ge.internal.diagram.runtime.DiagramNode)7 Collections (java.util.Collections)6 EnumSet (java.util.EnumSet)6 List (java.util.List)6 Entry (java.util.Map.Entry)6 Objects (java.util.Objects)6 Optional (java.util.Optional)6 Collectors (java.util.stream.Collectors)6 CoreOptions (org.eclipse.elk.core.options.CoreOptions)6 NodeLabelPlacement (org.eclipse.elk.core.options.NodeLabelPlacement)6