Search in sources :

Example 16 with PortSide

use of org.eclipse.elk.core.options.PortSide in project elk by eclipse.

the class ComponentsCompactor method edgeToSegments.

private Segments edgeToSegments(final LEdge edge, final InternalExternalExtension externalExtension) {
    LPort externalPort = externalExtension.externalPort;
    PortSide externalPortSide = externalExtension.externalPortSide;
    // extract the correct segment that is to be used for the external extension
    KVector p1 = edge.getSource().getAbsoluteAnchor();
    KVector p2 = edge.getTarget().getAbsoluteAnchor();
    // of the diagram yet, hence we adjust their position here
    if (externalPort == edge.getSource()) {
        p1 = getExternalPortPosition(p1, externalPortSide);
        p2 = getPortPositionOnMargin(edge.getTarget());
    } else {
        p1 = getPortPositionOnMargin(edge.getSource());
        p2 = getExternalPortPosition(p2, externalPortSide);
    }
    KVectorChain points = new KVectorChain(edge.getBendPoints());
    points.addFirst(p1);
    points.addLast(p2);
    boolean outerSegmentIsFirst = edge.getSource() == externalPort;
    // for easy processing, make it a list of segments
    Segments segments = new Segments();
    for (int i = 0; i < points.size() - 1; ++i) {
        Pair<KVector, KVector> segment = Pair.of(points.get(i), points.get(i + 1));
        if ((outerSegmentIsFirst && i == 0) || (!outerSegmentIsFirst && i == points.size() - 2)) {
            segments.outerSegment = segment;
        } else {
            segments.innerSegments.add(segment);
        }
    }
    return segments;
}
Also used : KVectorChain(org.eclipse.elk.core.math.KVectorChain) LPort(org.eclipse.elk.alg.layered.graph.LPort) PortSide(org.eclipse.elk.core.options.PortSide) KVector(org.eclipse.elk.core.math.KVector) Point(org.eclipse.elk.alg.layered.compaction.recthull.Point)

Example 17 with PortSide

use of org.eclipse.elk.core.options.PortSide in project elk by eclipse.

the class CompoundGraphPreprocessor method introduceHierarchicalEdgeSegment.

// //////////////////////////////////////////////////////////////////////////////////////////
// General Hierarchical Edge Segment Processing
/**
 * Does the actual work of creating a new hierarchical edge segment between an external port and
 * a given opposite port. The external port used for the segment is returned. This method does
 * not put any created edges into the cross hierarchy map!
 *
 * <p>
 * The method first decides on an external port to use for the segment. If the default external
 * port passed to the method is not {@code null} and if external ports are to be merged in the
 * current graph, the default external port is reused. An exception are segments that start or
 * end in the parent node; each such segments gets its own external port.
 * </p>
 *
 * <p>
 * If a new external port is created, the method also creates a dummy node for it as well as an
 * actual port on the parent node, if no such port already exists, as well as a dummy edge for
 * the connection. Thus, the newly created external port has everything it needs to be properly
 * represented and initialized.
 * </p>
 *
 * <p>
 * The original edge is added to the list of original edges in the external port used for the
 * segment. The dummy edge is associated with the original hierarchy-crossing edge in the cross
 * hierarchy map.
 * </p>
 *
 * @param graph
 *            the layered graph.
 * @param parentNode
 *            the graph's parent node, or {@code null} if the graph is at the top level.
 * @param origEdge
 *            the hierarchy-crossing edge that is being broken.
 * @param oppositePort
 *            the port that will be one of the two end points of the new segment.
 * @param portType
 *            the type of the port to create as one of the segment's edge points.
 * @param defaultExternalPort
 *            a default external port we can reuse if external ports should be merged. If this
 *            is {@code null}, a new external port is always created. If this port is reused, it
 *            is returned by this method.
 * @return the (created or reused) external port used as one endpoint of the edge segment.
 */
private ExternalPort introduceHierarchicalEdgeSegment(final LGraph graph, final LNode parentNode, final LEdge origEdge, final LPort oppositePort, final PortType portType, final ExternalPort defaultExternalPort) {
    // check if external ports are to be merged
    boolean mergeExternalPorts = graph.getProperty(LayeredOptions.MERGE_HIERARCHY_EDGES);
    // check if the edge connects to the parent node instead of to the outside world; if so, the
    // parentEndPort will be non-null
    LPort parentEndPort = null;
    if (portType == PortType.INPUT && origEdge.getSource().getNode() == parentNode) {
        parentEndPort = origEdge.getSource();
    } else if (portType == PortType.OUTPUT && origEdge.getTarget().getNode() == parentNode) {
        parentEndPort = origEdge.getTarget();
    }
    // only create a new external port if the current one is null or if ports are not to be merged
    // or if the connection actually ends at the parent node
    ExternalPort externalPort = defaultExternalPort;
    if (externalPort == null || !mergeExternalPorts || parentEndPort != null) {
        // create a dummy node that will represent the external port
        PortSide externalPortSide = PortSide.UNDEFINED;
        if (parentEndPort != null) {
            externalPortSide = parentEndPort.getSide();
        } else {
            // for people to set compound node port constraints to FREE
            if (parentNode.getProperty(LayeredOptions.PORT_CONSTRAINTS).isSideFixed()) {
                externalPortSide = portType == PortType.INPUT ? PortSide.WEST : PortSide.EAST;
            }
        }
        LNode dummyNode = createExternalPortDummy(graph, parentNode, portType, externalPortSide, origEdge);
        // create a dummy edge to be connected to the port
        LEdge dummyEdge = createDummyEdge(parentNode.getGraph(), origEdge);
        if (portType == PortType.INPUT) {
            // if the external port is an input port, the source of the edge must be connected to
            // the new dummy node
            dummyEdge.setSource(dummyNode.getPorts().get(0));
            dummyEdge.setTarget(oppositePort);
        } else {
            // if the external port is an output port, the target of the edge must be connected to
            // the new dummy node
            dummyEdge.setSource(oppositePort);
            dummyEdge.setTarget(dummyNode.getPorts().get(0));
        }
        // create the external port (the port is to be exported if the connection is not just to the
        // parent node)
        externalPort = new ExternalPort(origEdge, dummyEdge, dummyNode, (LPort) dummyNode.getProperty(InternalProperties.ORIGIN), portType, parentEndPort == null);
    } else {
        // we use an existing external port, so simply add the original edge to its list of
        // original edges
        externalPort.origEdges.add(origEdge);
        // merge the properties of the original edges
        double thickness = Math.max(externalPort.newEdge.getProperty(LayeredOptions.EDGE_THICKNESS), origEdge.getProperty(LayeredOptions.EDGE_THICKNESS));
        externalPort.newEdge.setProperty(LayeredOptions.EDGE_THICKNESS, thickness);
    }
    crossHierarchyMap.put(origEdge, new CrossHierarchyEdge(externalPort.newEdge, graph, portType));
    return externalPort;
}
Also used : LEdge(org.eclipse.elk.alg.layered.graph.LEdge) LPort(org.eclipse.elk.alg.layered.graph.LPort) LNode(org.eclipse.elk.alg.layered.graph.LNode) PortSide(org.eclipse.elk.core.options.PortSide)

Example 18 with PortSide

use of org.eclipse.elk.core.options.PortSide in project elk by eclipse.

the class LGraphUtil method createPort.

// /////////////////////////////////////////////////////////////////////////////
// Handling of Ports
/**
 * Create a port for an edge that is not connected to a port. This is necessary because ELK
 * Layered wants all edges to have a source port and a target port. The port side is computed
 * from the given absolute end point position of the edge.
 *
 * @param node
 *            the node at which the edge is incident
 * @param endPoint
 *            the absolute point where the edge ends, or {@code null} if unknown
 * @param type
 *            the port type
 * @param layeredGraph
 *            the layered graph
 * @return a new port
 */
public static LPort createPort(final LNode node, final KVector endPoint, final PortType type, final LGraph layeredGraph) {
    LPort port;
    Direction direction = getDirection(layeredGraph);
    boolean mergePorts = layeredGraph.getProperty(LayeredOptions.MERGE_EDGES);
    if ((mergePorts || node.getProperty(LayeredOptions.HYPERNODE)) && !node.getProperty(LayeredOptions.PORT_CONSTRAINTS).isSideFixed()) {
        // Hypernodes have one output port and one input port
        PortSide defaultSide = PortSide.fromDirection(direction);
        port = provideCollectorPort(layeredGraph, node, type, type == PortType.OUTPUT ? defaultSide : defaultSide.opposed());
    } else {
        port = new LPort();
        port.setNode(node);
        if (endPoint != null) {
            KVector pos = port.getPosition();
            pos.x = endPoint.x - node.getPosition().x;
            pos.y = endPoint.y - node.getPosition().y;
            pos.bound(0, 0, node.getSize().x, node.getSize().y);
            port.setSide(calcPortSide(port, direction));
        } else {
            PortSide defaultSide = PortSide.fromDirection(direction);
            port.setSide(type == PortType.OUTPUT ? defaultSide : defaultSide.opposed());
        }
        Set<GraphProperties> graphProperties = layeredGraph.getProperty(InternalProperties.GRAPH_PROPERTIES);
        PortSide portSide = port.getSide();
        switch(direction) {
            case LEFT:
            case RIGHT:
                if (portSide == PortSide.NORTH || portSide == PortSide.SOUTH) {
                    graphProperties.add(GraphProperties.NORTH_SOUTH_PORTS);
                }
                break;
            case UP:
            case DOWN:
                if (portSide == PortSide.EAST || portSide == PortSide.WEST) {
                    graphProperties.add(GraphProperties.NORTH_SOUTH_PORTS);
                }
                break;
        }
    }
    return port;
}
Also used : GraphProperties(org.eclipse.elk.alg.layered.options.GraphProperties) PortSide(org.eclipse.elk.core.options.PortSide) KVector(org.eclipse.elk.core.math.KVector) Direction(org.eclipse.elk.core.options.Direction)

Example 19 with PortSide

use of org.eclipse.elk.core.options.PortSide in project elk by eclipse.

the class LGraphUtil method initializePort.

/**
 * Initialize the side, offset, and anchor point of the given port. The port is assumed to
 * be also present in the original graph structure. The port's current position and the size
 * of the corresponding node are used to determine missing values.
 *
 * @param port a port
 * @param portConstraints the port constraints of the containing node
 * @param direction the overall layout direction
 * @param anchorPos the anchor position, or {@code null} if it shall be determined automatically
 */
public static void initializePort(final LPort port, final PortConstraints portConstraints, final Direction direction, final KVector anchorPos) {
    PortSide portSide = port.getSide();
    if (portSide == PortSide.UNDEFINED && portConstraints.isSideFixed()) {
        // calculate the port side and offset from the port's current position
        portSide = calcPortSide(port, direction);
        port.setSide(portSide);
        // frustration-free
        if (!port.getAllProperties().containsKey(LayeredOptions.PORT_BORDER_OFFSET) && portSide != PortSide.UNDEFINED && (port.getPosition().x != 0 || port.getPosition().y != 0)) {
            port.setProperty(LayeredOptions.PORT_BORDER_OFFSET, calcPortOffset(port, portSide));
        }
    }
    // if the port constraints are set to fixed ratio, remember the current ratio
    if (portConstraints.isRatioFixed()) {
        double ratio = 0.0;
        switch(portSide) {
            case NORTH:
            case SOUTH:
                double nodeWidth = port.getNode().getSize().x;
                if (nodeWidth > 0) {
                    ratio = port.getPosition().x / nodeWidth;
                }
                break;
            case EAST:
            case WEST:
                double nodeHeight = port.getNode().getSize().y;
                if (nodeHeight > 0) {
                    ratio = port.getPosition().y / nodeHeight;
                }
                break;
        }
        port.setProperty(InternalProperties.PORT_RATIO_OR_POSITION, ratio);
    }
    KVector portSize = port.getSize();
    KVector portAnchor = port.getAnchor();
    // if the port anchor property is set, use it as anchor point
    if (anchorPos != null) {
        portAnchor.x = anchorPos.x;
        portAnchor.y = anchorPos.y;
        // Since we have applied an explicit anchor, assume the user knows what they are doing and fix it
        port.setExplicitlySuppliedPortAnchor(true);
    } else if (portConstraints.isSideFixed() && portSide != PortSide.UNDEFINED) {
        // set the anchor point according to the port side
        switch(portSide) {
            case NORTH:
                portAnchor.x = portSize.x / 2;
                break;
            case EAST:
                portAnchor.x = portSize.x;
                portAnchor.y = portSize.y / 2;
                break;
            case SOUTH:
                portAnchor.x = portSize.x / 2;
                portAnchor.y = portSize.y;
                break;
            case WEST:
                portAnchor.y = portSize.y / 2;
                break;
        }
    } else {
        // the port side will be decided later, so set the anchor to the center point
        portAnchor.x = portSize.x / 2;
        portAnchor.y = portSize.y / 2;
    }
}
Also used : PortSide(org.eclipse.elk.core.options.PortSide) KVector(org.eclipse.elk.core.math.KVector)

Example 20 with PortSide

use of org.eclipse.elk.core.options.PortSide in project elk by eclipse.

the class ElkUtil method resizeNode.

/**
 * Resize a node to the given width and height, adjusting port and label positions if needed.
 *
 * @param node a node
 * @param newWidth the new width to set
 * @param newHeight the new height to set
 * @param movePorts whether port positions should be adjusted
 * @param moveLabels whether label positions should be adjusted
 * @return a vector holding the width and height resizing ratio
 */
public static KVector resizeNode(final ElkNode node, final double newWidth, final double newHeight, final boolean movePorts, final boolean moveLabels) {
    KVector oldSize = new KVector(node.getWidth(), node.getHeight());
    KVector newSize = effectiveMinSizeConstraintFor(node);
    newSize.x = Math.max(newSize.x, newWidth);
    newSize.y = Math.max(newSize.y, newHeight);
    double widthRatio = newSize.x / oldSize.x;
    double heightRatio = newSize.y / oldSize.y;
    double widthDiff = newSize.x - oldSize.x;
    double heightDiff = newSize.y - oldSize.y;
    // update port positions
    if (movePorts) {
        Direction direction = node.getParent() == null ? node.getProperty(CoreOptions.DIRECTION) : node.getParent().getProperty(CoreOptions.DIRECTION);
        boolean fixedPorts = node.getProperty(CoreOptions.PORT_CONSTRAINTS) == PortConstraints.FIXED_POS;
        for (ElkPort port : node.getPorts()) {
            PortSide portSide = port.getProperty(CoreOptions.PORT_SIDE);
            if (portSide == PortSide.UNDEFINED) {
                portSide = calcPortSide(port, direction);
                port.setProperty(CoreOptions.PORT_SIDE, portSide);
            }
            switch(portSide) {
                case NORTH:
                    if (!fixedPorts) {
                        port.setX(port.getX() * widthRatio);
                    }
                    break;
                case EAST:
                    port.setX(port.getX() + widthDiff);
                    if (!fixedPorts) {
                        port.setY(port.getY() * heightRatio);
                    }
                    break;
                case SOUTH:
                    if (!fixedPorts) {
                        port.setX(port.getX() * widthRatio);
                    }
                    port.setY(port.getY() + heightDiff);
                    break;
                case WEST:
                    if (!fixedPorts) {
                        port.setY(port.getY() * heightRatio);
                    }
                    break;
            }
        }
    }
    // resize the node AFTER ports have been placed, since calcPortSide needs the old size
    node.setDimensions(newSize.x, newSize.y);
    // update label positions
    if (moveLabels) {
        for (ElkLabel label : node.getLabels()) {
            double midx = label.getX() + label.getWidth() / 2;
            double midy = label.getY() + label.getHeight() / 2;
            double widthPercent = midx / oldSize.x;
            double heightPercent = midy / oldSize.y;
            if (widthPercent + heightPercent >= 1) {
                if (widthPercent - heightPercent > 0 && midy >= 0) {
                    // label is on the right
                    label.setX(label.getX() + widthDiff);
                    label.setY(label.getY() + heightDiff * heightPercent);
                } else if (widthPercent - heightPercent < 0 && midx >= 0) {
                    // label is on the bottom
                    label.setX(label.getX() + widthDiff * widthPercent);
                    label.setY(label.getY() + heightDiff);
                }
            }
        }
    }
    // set fixed size option for the node: now the size is assumed to stay as determined here
    node.setProperty(CoreOptions.NODE_SIZE_CONSTRAINTS, SizeConstraint.fixed());
    return new KVector(widthRatio, heightRatio);
}
Also used : ElkLabel(org.eclipse.elk.graph.ElkLabel) ElkPort(org.eclipse.elk.graph.ElkPort) KVector(org.eclipse.elk.core.math.KVector) PortSide(org.eclipse.elk.core.options.PortSide) Direction(org.eclipse.elk.core.options.Direction)

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