Search in sources :

Example 1 with PortSide

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);
    }
}
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 PortSide

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);
}
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 3 with PortSide

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);
}
Also used : LEdge(org.eclipse.elk.alg.layered.graph.LEdge) LPort(org.eclipse.elk.alg.layered.graph.LPort) PortSide(org.eclipse.elk.core.options.PortSide)

Example 4 with PortSide

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;
                }
            }
        }
    }
}
Also used : LEdge(org.eclipse.elk.alg.layered.graph.LEdge) KVectorChain(org.eclipse.elk.core.math.KVectorChain) LPort(org.eclipse.elk.alg.layered.graph.LPort) LNode(org.eclipse.elk.alg.layered.graph.LNode) PortSide(org.eclipse.elk.core.options.PortSide) KVector(org.eclipse.elk.core.math.KVector)

Example 5 with PortSide

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;
        }
    }
}
Also used : LEdge(org.eclipse.elk.alg.layered.graph.LEdge) LNode(org.eclipse.elk.alg.layered.graph.LNode) PortSide(org.eclipse.elk.core.options.PortSide) OrthogonalRoutingGenerator(org.eclipse.elk.alg.layered.p5edges.orthogonal.OrthogonalRoutingGenerator) SizeConstraint(org.eclipse.elk.core.options.SizeConstraint)

Aggregations

PortSide (org.eclipse.elk.core.options.PortSide)71 LPort (org.eclipse.elk.alg.layered.graph.LPort)24 LNode (org.eclipse.elk.alg.layered.graph.LNode)23 KVector (org.eclipse.elk.core.math.KVector)16 SelfHyperLoop (org.eclipse.elk.alg.layered.intermediate.loops.SelfHyperLoop)10 Direction (org.eclipse.elk.core.options.Direction)9 PortConstraints (org.eclipse.elk.core.options.PortConstraints)9 ElkPort (org.eclipse.elk.graph.ElkPort)8 List (java.util.List)7 SizeConstraint (org.eclipse.elk.core.options.SizeConstraint)7 ElkLabel (org.eclipse.elk.graph.ElkLabel)7 ArrayList (java.util.ArrayList)6 LEdge (org.eclipse.elk.alg.layered.graph.LEdge)6 Collectors (java.util.stream.Collectors)5 ElkGraphElement (org.eclipse.elk.graph.ElkGraphElement)5 DiagramElement (org.osate.ge.internal.diagram.runtime.DiagramElement)5 EnumSet (java.util.EnumSet)4 HashMap (java.util.HashMap)4 Map (java.util.Map)4 LLabel (org.eclipse.elk.alg.layered.graph.LLabel)4