Search in sources :

Example 6 with KVector

use of org.eclipse.elk.core.math.KVector in project elk by eclipse.

the class LabelPlacer method placeHorizontalOuterNodeLabelContainer.

/**
 * Places a horizontal outer node label container on the given side.
 */
private static void placeHorizontalOuterNodeLabelContainer(final NodeContext nodeContext, final boolean outerNodeLabelsOverhang, final PortSide portSide) {
    KVector nodeSize = nodeContext.nodeSize;
    StripContainerCell nodeLabelContainer = nodeContext.outsideNodeLabelContainers.get(portSide);
    ElkRectangle nodeLabelContainerRect = nodeLabelContainer.getCellRectangle();
    // Set the container's width and height to its minimum width and height
    nodeLabelContainerRect.width = nodeLabelContainer.getMinimumWidth();
    nodeLabelContainerRect.height = nodeLabelContainer.getMinimumHeight();
    // The container must be at least as wide as the node is
    nodeLabelContainerRect.width = Math.max(nodeLabelContainerRect.width, nodeSize.x);
    // If node labels are not allowed to overhang and if they would do so right now, make the container smaller
    if (nodeLabelContainerRect.width > nodeSize.x && !outerNodeLabelsOverhang) {
        nodeLabelContainerRect.width = nodeSize.x;
    }
    // Container's x coordinate
    nodeLabelContainerRect.x = -(nodeLabelContainerRect.width - nodeSize.x) / 2;
    // Container's y coordinate depends on whether we place the thing on the northern or southern side
    switch(portSide) {
        case NORTH:
            nodeLabelContainerRect.y = -nodeLabelContainerRect.height;
            break;
        case SOUTH:
            nodeLabelContainerRect.y = nodeSize.y;
            break;
    }
    // Layout the container's children
    nodeLabelContainer.layoutChildrenHorizontally();
    nodeLabelContainer.layoutChildrenVertically();
}
Also used : KVector(org.eclipse.elk.core.math.KVector) ElkRectangle(org.eclipse.elk.core.math.ElkRectangle) StripContainerCell(org.eclipse.elk.alg.common.nodespacing.cellsystem.StripContainerCell)

Example 7 with KVector

use of org.eclipse.elk.core.math.KVector in project elk by eclipse.

the class NodeSizeCalculator method setNodeHeight.

// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node Height
/**
 * Sets the node's height according to the active node size constraints. Also sets that height on the cell system
 * and tells it to compute a horizontal layout.
 */
public static void setNodeHeight(final NodeContext nodeContext) {
    KVector nodeSize = nodeContext.nodeSize;
    double height;
    if (NodeLabelAndSizeUtilities.areSizeConstraintsFixed(nodeContext)) {
        // Simply use the node's current height
        height = nodeSize.y;
    } else {
        // Ask the cell system how heigh it would like to be
        height = nodeContext.nodeContainer.getMinimumHeight();
        // If we include node labels and outside node labels are not to overhang, we need to include those as well
        if (nodeContext.sizeConstraints.contains(SizeConstraint.NODE_LABELS) && !nodeContext.sizeOptions.contains(SizeOptions.OUTSIDE_NODE_LABELS_OVERHANG)) {
            height = Math.max(height, nodeContext.outsideNodeLabelContainers.get(PortSide.EAST).getMinimumHeight());
            height = Math.max(height, nodeContext.outsideNodeLabelContainers.get(PortSide.WEST).getMinimumHeight());
        }
        // The node might have a minimum size set...
        KVector minNodeSize = NodeLabelAndSizeUtilities.getMinimumNodeSize(nodeContext);
        if (minNodeSize != null) {
            height = Math.max(height, minNodeSize.y);
        }
        // come out of the cell system
        if (nodeContext.sizeConstraints.contains(SizeConstraint.PORTS)) {
            if (nodeContext.portConstraints == PortConstraints.FIXED_RATIO || nodeContext.portConstraints == PortConstraints.FIXED_POS) {
                height = Math.max(height, nodeContext.insidePortLabelCells.get(PortSide.EAST).getMinimumHeight());
                height = Math.max(height, nodeContext.insidePortLabelCells.get(PortSide.WEST).getMinimumHeight());
            }
        }
    }
    // Set the node's height
    nodeSize.y = height;
    // Set the cell system's height and tell it to compute vertical coordinates and heights
    ElkRectangle nodeCellRectangle = nodeContext.nodeContainer.getCellRectangle();
    nodeCellRectangle.y = 0;
    nodeCellRectangle.height = height;
    nodeContext.nodeContainer.layoutChildrenVertically();
}
Also used : KVector(org.eclipse.elk.core.math.KVector) ElkRectangle(org.eclipse.elk.core.math.ElkRectangle)

Example 8 with KVector

use of org.eclipse.elk.core.math.KVector in project elk by eclipse.

the class PortLabelPlacementCalculator method simpleOutsidePortLabelPlacement.

// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Simple Outside Port Labels
/**
 * Place the port label cells outside of the node.
 */
private static void simpleOutsidePortLabelPlacement(final NodeContext nodeContext, final PortSide portSide) {
    Collection<PortContext> portContexts = nodeContext.portContexts.get(portSide);
    // If there are only two ports on a side, we place the first port's label on its other side to make it
    // especially clear which port it belongs to. The same applies if the user requested space-efficient mode
    boolean placeFirstPortDifferently = NodeLabelAndSizeUtilities.isFirstOutsidePortLabelPlacedDifferently(nodeContext, portSide);
    for (PortContext portContext : portContexts) {
        // If the port doesn't have labels, skip
        if (portContext.portLabelCell == null || !portContext.portLabelCell.hasLabels()) {
            continue;
        }
        // Retrieve information about the port itself
        KVector portSize = portContext.port.getSize();
        // Retrieve the label cell and its rectangle and set the rectangle's size (we will use the rectangle to
        // place the cell relative to the port below)
        LabelCell portLabelCell = portContext.portLabelCell;
        ElkRectangle portLabelCellRect = portLabelCell.getCellRectangle();
        portLabelCellRect.width = portLabelCell.getMinimumWidth();
        portLabelCellRect.height = portLabelCell.getMinimumHeight();
        // Calculate the position of the port's label space
        switch(portSide) {
            case NORTH:
                if (portContext.labelsNextToPort) {
                    portLabelCellRect.x = (portSize.x - portLabelCellRect.width) / 2;
                    portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.CENTER);
                } else if (placeFirstPortDifferently) {
                    portLabelCellRect.x = -portLabelCellRect.width - nodeContext.portLabelSpacing;
                    portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.RIGHT);
                } else {
                    portLabelCellRect.x = portSize.x + nodeContext.portLabelSpacing;
                    portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.LEFT);
                }
                portLabelCellRect.y = -portLabelCellRect.height - nodeContext.portLabelSpacing;
                portLabelCell.setVerticalAlignment(VerticalLabelAlignment.BOTTOM);
                break;
            case SOUTH:
                if (portContext.labelsNextToPort) {
                    portLabelCellRect.x = (portSize.x - portLabelCellRect.width) / 2;
                    portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.CENTER);
                } else if (placeFirstPortDifferently) {
                    portLabelCellRect.x = -portLabelCellRect.width - nodeContext.portLabelSpacing;
                    portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.RIGHT);
                } else {
                    portLabelCellRect.x = portSize.x + nodeContext.portLabelSpacing;
                    portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.LEFT);
                }
                portLabelCellRect.y = portSize.y + nodeContext.portLabelSpacing;
                portLabelCell.setVerticalAlignment(VerticalLabelAlignment.TOP);
                break;
            case EAST:
                if (portContext.labelsNextToPort) {
                    double labelHeight = nodeContext.portLabelsTreatAsGroup ? portLabelCellRect.height : portLabelCell.getLabels().get(0).getSize().y;
                    portLabelCellRect.y = (portSize.y - labelHeight) / 2;
                    portLabelCell.setVerticalAlignment(VerticalLabelAlignment.CENTER);
                } else if (placeFirstPortDifferently) {
                    portLabelCellRect.y = -portLabelCellRect.height - nodeContext.portLabelSpacing;
                    portLabelCell.setVerticalAlignment(VerticalLabelAlignment.BOTTOM);
                } else {
                    portLabelCellRect.y = portSize.y + nodeContext.portLabelSpacing;
                    portLabelCell.setVerticalAlignment(VerticalLabelAlignment.TOP);
                }
                portLabelCellRect.x = portSize.x + nodeContext.portLabelSpacing;
                portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.LEFT);
                break;
            case WEST:
                if (portContext.labelsNextToPort) {
                    double labelHeight = nodeContext.portLabelsTreatAsGroup ? portLabelCellRect.height : portLabelCell.getLabels().get(0).getSize().y;
                    portLabelCellRect.y = (portSize.y - labelHeight) / 2;
                    portLabelCell.setVerticalAlignment(VerticalLabelAlignment.CENTER);
                } else if (placeFirstPortDifferently) {
                    portLabelCellRect.y = -portLabelCellRect.height - nodeContext.portLabelSpacing;
                    portLabelCell.setVerticalAlignment(VerticalLabelAlignment.BOTTOM);
                } else {
                    portLabelCellRect.y = portSize.y + nodeContext.portLabelSpacing;
                    portLabelCell.setVerticalAlignment(VerticalLabelAlignment.TOP);
                }
                portLabelCellRect.x = -portLabelCellRect.width - nodeContext.portLabelSpacing;
                portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.RIGHT);
                break;
        }
        // The next port definitely doesn't have special needs anymore
        placeFirstPortDifferently = false;
    }
}
Also used : LabelCell(org.eclipse.elk.alg.common.nodespacing.cellsystem.LabelCell) KVector(org.eclipse.elk.core.math.KVector) ElkRectangle(org.eclipse.elk.core.math.ElkRectangle) PortContext(org.eclipse.elk.alg.common.nodespacing.internal.PortContext)

Example 9 with KVector

use of org.eclipse.elk.core.math.KVector in project elk by eclipse.

the class NodeMarginCalculator method processNode.

/**
 * Calculates the margin of the given node.
 *
 * @param node the node whose margin to calculate.
 * @param labelSpacing label spacing set on the layered graph.
 */
private void processNode(final NodeAdapter<?> node, final double labelSpacing) {
    // This will be our bounding box. We'll start with one that's the same size
    // as our node, and at the same position.
    ElkRectangle boundingBox = new ElkRectangle(node.getPosition().x, node.getPosition().y, node.getSize().x, node.getSize().y);
    // We'll reuse this rectangle as our box for elements to add to the bounding box
    ElkRectangle elementBox = new ElkRectangle();
    // Put the node's labels into the bounding box
    if (includeLabels) {
        for (LabelAdapter<?> label : node.getLabels()) {
            elementBox.x = label.getPosition().x + node.getPosition().x;
            elementBox.y = label.getPosition().y + node.getPosition().y;
            elementBox.width = label.getSize().x;
            elementBox.height = label.getSize().y;
            boundingBox.union(elementBox);
        }
    }
    // Do the same for ports and their labels
    for (PortAdapter<?> port : node.getPorts()) {
        // Calculate the port's upper left corner's x and y coordinate
        double portX = port.getPosition().x + node.getPosition().x;
        double portY = port.getPosition().y + node.getPosition().y;
        // The port itself
        if (includePorts) {
            elementBox.x = portX;
            elementBox.y = portY;
            elementBox.width = port.getSize().x;
            elementBox.height = port.getSize().y;
            boundingBox.union(elementBox);
        }
        // The port's labels
        if (includePortLabels) {
            for (LabelAdapter<?> label : port.getLabels()) {
                elementBox.x = label.getPosition().x + portX;
                elementBox.y = label.getPosition().y + portY;
                elementBox.width = label.getSize().x;
                elementBox.height = label.getSize().y;
                boundingBox.union(elementBox);
            }
        }
        // End labels of edges connected to the port
        if (includeEdgeHeadTailLabels) {
            KVector requiredPortLabelSpace = new KVector(-labelSpacing, -labelSpacing);
            // TODO: maybe leave space for manually placed ports
            if (node.getProperty(CoreOptions.PORT_LABELS_PLACEMENT).contains(PortLabelPlacement.OUTSIDE)) {
                for (LabelAdapter<?> label : port.getLabels()) {
                    requiredPortLabelSpace.x += label.getSize().x + labelSpacing;
                    requiredPortLabelSpace.y += label.getSize().y + labelSpacing;
                }
            }
            requiredPortLabelSpace.x = Math.max(requiredPortLabelSpace.x, 0.0);
            requiredPortLabelSpace.y = Math.max(requiredPortLabelSpace.y, 0.0);
            processEdgeHeadTailLabels(boundingBox, port.getOutgoingEdges(), port.getIncomingEdges(), node, port, requiredPortLabelSpace, labelSpacing);
        }
    }
    // Process end labels of edges directly connected to the node
    if (includeEdgeHeadTailLabels) {
        processEdgeHeadTailLabels(boundingBox, node.getOutgoingEdges(), node.getIncomingEdges(), node, null, null, labelSpacing);
    }
    // Reset the margin (guard against very small double precision errors which can cause the results to be small
    // negative values, which doesn't make sense -- see #616)
    ElkMargin margin = new ElkMargin(node.getMargin());
    margin.top = Math.max(0, node.getPosition().y - boundingBox.y);
    margin.bottom = Math.max(0, boundingBox.y + boundingBox.height - (node.getPosition().y + node.getSize().y));
    margin.left = Math.max(0, node.getPosition().x - boundingBox.x);
    margin.right = Math.max(0, boundingBox.x + boundingBox.width - (node.getPosition().x + node.getSize().x));
    node.setMargin(margin);
}
Also used : ElkRectangle(org.eclipse.elk.core.math.ElkRectangle) KVector(org.eclipse.elk.core.math.KVector) ElkMargin(org.eclipse.elk.core.math.ElkMargin)

Example 10 with KVector

use of org.eclipse.elk.core.math.KVector in project elk by eclipse.

the class BowyerWatsonTriangulation method triangulate.

/**
 * Triangulates a list of points.
 *
 * @param vertices the input points
 * @param debugOutputFile file name for debug SVG. Debug output will be deactivated if this is null.
 * @return the edges of the triangulation
 */
public static Set<TEdge> triangulate(final List<KVector> vertices, final String debugOutputFile) {
    /*d*/
    SVGImage svg = new SVGImage(debugOutputFile);
    /*d*/
    svg.addGroups("invalid", "tri", "bndry", "done", "new");
    /* preliminaries */
    // determine the bounding box of the given points
    KVector topleft = new KVector(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
    KVector bottomright = new KVector(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
    for (KVector v : vertices) {
        topleft.x = Math.min(topleft.x, v.x);
        topleft.y = Math.min(topleft.y, v.y);
        bottomright.x = Math.max(bottomright.x, v.x);
        bottomright.y = Math.max(bottomright.y, v.y);
        // CHECKSTYLEOFF MagicNumber
        /*d*/
        svg.g("bb").addCircle(v.x, v.y, 18, "stroke=\"black\" stroke-width=\"1\" fill=\"lightgray\"");
    }
    KVector size = new KVector(bottomright.x - topleft.x, bottomright.y - topleft.y);
    /*d*/
    svg.g("bb").addRect(topleft.x, topleft.y, size.x, size.y, "stroke=\"blue\" stroke-width=\"4\" fill=\"none\"");
    // find a super-triangle spanning over all vertices
    final double wiggleroom = 50;
    // This ensures that no vertices are too close to the edges of the super-triangle
    // because that would result in circumcircles that are so large that a double would not
    // be precise enough to compute the circumcircle criterion.
    KVector sa = new KVector(topleft.x - wiggleroom, topleft.y - size.x - wiggleroom);
    KVector sb = new KVector(topleft.x - wiggleroom, bottomright.y + size.x + wiggleroom);
    KVector sc = new KVector(bottomright.x + size.y / 2 + wiggleroom, topleft.y + size.y / 2);
    /*d*/
    svg.g("bb").addPoly("stroke=\"gray\" stroke-width=\"4\" fill=\"none\" stroke-dasharray=\"20,20\"", sa, sb, sc, sa);
    TTriangle superTriangle = new TTriangle(sa, sb, sc);
    /*d*/
    // circumcircles will make it gigantic
    svg.setViewBox(sa.x, sa.y, sc.x - sa.x, sb.y - sa.y);
    /*d*/
    svg.isave();
    /*d*/
    svg.removeGroup("bb");
    /* Bowyer Watson algorithm*/
    Set<TTriangle> triangulation = Sets.newHashSet();
    List<TTriangle> invalidTriangles = Lists.newArrayList();
    List<TEdge> boundary = Lists.newArrayList();
    triangulation.add(superTriangle);
    // incrementally adding vertices
    for (KVector vertex : vertices) {
        /*d*/
        svg.g("done").addCircle(vertex.x, vertex.y, 18, "stroke=\"black\" stroke-width=\"1\" fill=\"lightgray\"");
        /*d*/
        svg.g("new").addCircle(vertex.x, vertex.y, 18, "stroke=\"black\" stroke-width=\"1\" fill=\"black\"");
        // gather invalid triangles where the new vertex lies inside the circumcircle
        invalidTriangles.clear();
        for (TTriangle triangle : triangulation) {
            /*d*/
            svg.g("tri").addPoly("stroke=\"black\" fill=\"none\" stroke-width=\"4\"", triangle.a, triangle.b, triangle.c, triangle.a);
            /*d*/
            KVector c = triangle.getCircumcenter();
            /*d*/
            svg.g("invalid").addCircle(c.x, c.y, c.distance(triangle.a), "stroke=\"orange\" stroke-width=\"4\" fill=\"none\"");
            if (triangle.inCircumcircle(vertex)) {
                invalidTriangles.add(triangle);
                /*d*/
                svg.g("invalid").addPoly("stroke=\"none\" fill=\"red\" opacity=\"0.18\"", triangle.a, triangle.b, triangle.c, triangle.a);
            }
        }
        /*d*/
        svg.isave();
        /*d*/
        svg.clearGroup("invalid");
        // calculate boundary of invalid triangles
        boundary.clear();
        for (TTriangle triangle : invalidTriangles) {
            for (TEdge tEdge : triangle.tEdges) {
                boolean onBoundary = true;
                // edges that are not shared with other invalid triangles are on the boundary
                for (TTriangle other : invalidTriangles) {
                    if (other != triangle && other.contains(tEdge)) {
                        onBoundary = false;
                    }
                }
                if (onBoundary) {
                    boundary.add(tEdge);
                    /*d*/
                    svg.g("bndry").addLine(tEdge.u.x, tEdge.u.y, tEdge.v.x, tEdge.v.y, "stroke=\"purple\" stroke-width=\"18\" stroke-dasharray=\"20,20\"");
                }
            }
        }
        /*d*/
        svg.isave();
        // remove invalid triangles
        triangulation.removeAll(invalidTriangles);
        /*d*/
        svg.clearGroup("tri");
        /*d*/
        triangulation.forEach(triangle -> svg.g("tri").addPoly("stroke=\"black\" fill=\"none\" stroke-width=\"4\"", triangle.a, triangle.b, triangle.c, triangle.a));
        /*d*/
        svg.isave();
        // triangulate boundary
        for (TEdge tEdge : boundary) {
            triangulation.add(new TTriangle(vertex, tEdge.u, tEdge.v));
            /*d*/
            svg.g("tri").addPoly("stroke=\"black\" fill=\"none\" stroke-width=\"4\"", vertex, tEdge.u, tEdge.v, vertex);
        }
        /*d*/
        svg.isave();
        /*d*/
        svg.clearGroup("new");
        /*d*/
        svg.clearGroup("bndry");
        /*d*/
        svg.clearGroup("tri");
    }
    // convert triangulation to set of edges
    // Because it's a set, edges that were redundant in the triangulation exist only once in the set.
    Set<TEdge> tEdges = Sets.newHashSet();
    triangulation.forEach(triangle -> tEdges.addAll(triangle.tEdges));
    // remove edges connected to super triangle
    Iterator<TEdge> i = tEdges.iterator();
    while (i.hasNext()) {
        TEdge tEdge = i.next();
        if (superTriangle.contains(tEdge.u) || superTriangle.contains(tEdge.v)) {
            i.remove();
        }
    }
    /*d*/
    tEdges.forEach(tEdge -> svg.addLine(tEdge.u.x, tEdge.u.y, tEdge.v.x, tEdge.v.y, "stroke=\"black\" stroke-width=\"4\""));
    /*d*/
    svg.isave();
    return tEdges;
}
Also used : KVector(org.eclipse.elk.core.math.KVector) SVGImage(org.eclipse.elk.alg.common.utils.SVGImage)

Aggregations

KVector (org.eclipse.elk.core.math.KVector)292 KVectorChain (org.eclipse.elk.core.math.KVectorChain)52 ElkNode (org.eclipse.elk.graph.ElkNode)49 LNode (org.eclipse.elk.alg.layered.graph.LNode)39 LPort (org.eclipse.elk.alg.layered.graph.LPort)37 ElkRectangle (org.eclipse.elk.core.math.ElkRectangle)36 LEdge (org.eclipse.elk.alg.layered.graph.LEdge)35 Test (org.junit.Test)30 ElkEdge (org.eclipse.elk.graph.ElkEdge)28 ElkLabel (org.eclipse.elk.graph.ElkLabel)27 ElkEdgeSection (org.eclipse.elk.graph.ElkEdgeSection)26 ElkPadding (org.eclipse.elk.core.math.ElkPadding)25 LLabel (org.eclipse.elk.alg.layered.graph.LLabel)20 PortSide (org.eclipse.elk.core.options.PortSide)19 ElkPort (org.eclipse.elk.graph.ElkPort)18 SizeConstraint (org.eclipse.elk.core.options.SizeConstraint)17 LGraph (org.eclipse.elk.alg.layered.graph.LGraph)15 ArrayList (java.util.ArrayList)13 Layer (org.eclipse.elk.alg.layered.graph.Layer)12 LMargin (org.eclipse.elk.alg.layered.graph.LMargin)11