Search in sources :

Example 1 with EdgeInformation

use of org.eclipse.elk.alg.layered.p5edges.splines.SplineSegment.EdgeInformation in project elk by eclipse.

the class FinalSplineBendpointsCalculator method calculateControlPointsConservative.

/**
 * For a regular upward pointing edge and the conservative routing style, control points ({@code +}) are computed
 * as follows:
 * <pre>
 *               ___
 *              |   |
 *          +--+|   |
 *   ___    |   |___|
 *  |   |   |
 *  |   |+--+
 *  |___|
 *  </pre>
 * An downward pointing edge is handled analog.
 */
private void calculateControlPointsConservative(final LEdge edge, final SplineSegment containingSegment) {
    final double startXPos = containingSegment.boundingBox.x;
    final double endXPos = containingSegment.boundingBox.x + containingSegment.boundingBox.width;
    final EdgeInformation ei = containingSegment.edgeInformation.get(edge);
    final double ySourceAnchor = ei.startY;
    final double yTargetAnchor = ei.endY;
    // Calculate bend points to draw inner layer segments straight
    // to prevent intersections with big nodes
    final KVector sourceStraightCP = new KVector(startXPos, ySourceAnchor);
    final KVector targetStraightCP = new KVector(endXPos, yTargetAnchor);
    double centerXPos = startXPos;
    if (!containingSegment.isWestOfInitialLayer) {
        centerXPos += edgeNodeSpacing;
    }
    centerXPos += containingSegment.xDelta + (containingSegment.rank + 0) * edgeEdgeSpacing;
    final KVector sourceVerticalCP = new KVector(centerXPos, ySourceAnchor);
    final KVector targetVerticalCP = new KVector(centerXPos, yTargetAnchor);
    // Traditional four control points (plus an extra center control point for hyperedges)
    edge.getBendPoints().addAll(sourceStraightCP, sourceVerticalCP);
    boolean isHyperedge = containingSegment.edges.size() > 1;
    if (isHyperedge) {
        // add an additional center control point to assert that the hyperedge segments share a part of their route
        final KVector center = new KVector(centerXPos, containingSegment.centerControlPointY);
        edge.getBendPoints().add(center);
    }
    edge.getBendPoints().addAll(targetVerticalCP, targetStraightCP);
}
Also used : EdgeInformation(org.eclipse.elk.alg.layered.p5edges.splines.SplineSegment.EdgeInformation) KVector(org.eclipse.elk.core.math.KVector)

Example 2 with EdgeInformation

use of org.eclipse.elk.alg.layered.p5edges.splines.SplineSegment.EdgeInformation in project elk by eclipse.

the class FinalSplineBendpointsCalculator method calculateControlPoints.

/**
 * The method dispatches the calculation of NUB control points for the passed {@code segment} to the
 * {@code calculateControlPoints*(..)} methods based on the following criteria.
 * <ul>
 * <li>the desired {@link SplineRoutingMode}</li>
 * <li>if the segment is straight</li>
 * <li>if the segment represents an in-layer edge due to inverted ports</li>
 * <li>if the segment represents a hyperedge</li>
 * </ul>
 */
private void calculateControlPoints(final SplineSegment segment) {
    // with hyperedges it can happen that this method is called multiple times for the same segment
    if (segment.handled) {
        return;
    }
    segment.handled = true;
    for (final LEdge edge : segment.edges) {
        if (segment.isStraight && !segment.isHyperEdge()) {
            calculateControlPointsStraight(segment);
            continue;
        }
        // Remember that the edge itself is not necessarily valid at this point
        // (it may have been remove by the long edge joiner, for instance)
        EdgeInformation ei = segment.edgeInformation.get(edge);
        // inverted ports are handled in the same way for
        if (ei.invertedLeft || ei.invertedRight) {
            calculateControlPointsInvertedEdge(edge, segment);
            continue;
        }
        // to compute sloppy control points at least one of the
        // two nodes connected by the segment must be a 'normal' node,
        // and in case horizontal compaction is active the predicate implemented
        // by the 'segmentAllowsSloppyRouting(..)' method must be true
        // for hyperedges sloppy routing is not possible
        boolean sloppy = splineRoutingMode == SplineRoutingMode.SLOPPY && (ei.normalSourceNode || ei.normalTargetNode) && segmentAllowsSloppyRouting(segment) && !segment.isHyperEdge();
        if (sloppy) {
            calculateControlPointsSloppy(edge, segment);
        } else {
            calculateControlPointsConservative(edge, segment);
        }
    }
    if (segment.inverseOrder) {
        segment.edges.forEach(e -> Collections.reverse(e.getBendPoints()));
    }
}
Also used : LEdge(org.eclipse.elk.alg.layered.graph.LEdge) EdgeInformation(org.eclipse.elk.alg.layered.p5edges.splines.SplineSegment.EdgeInformation)

Example 3 with EdgeInformation

use of org.eclipse.elk.alg.layered.p5edges.splines.SplineSegment.EdgeInformation in project elk by eclipse.

the class FinalSplineBendpointsCalculator method calculateControlPointsInvertedEdge.

/**
 * If {@code edge} is an inverted edge on the western side, control points ({@code +}) are computed as follows:
 * <pre>
 *        ___
 *       |   |
 *   +--+|   |
 *   |   |   |
 *   |   |   |
 *   +--+|   |
 *       |___|
 * </pre>
 * An inverted edge on the right side works analog.
 */
private void calculateControlPointsInvertedEdge(final LEdge edge, final SplineSegment containingSegment) {
    final double startXPos = containingSegment.boundingBox.x;
    final double endXPos = containingSegment.boundingBox.x + containingSegment.boundingBox.width;
    final EdgeInformation ei = containingSegment.edgeInformation.get(edge);
    final double ySourceAnchor = ei.startY;
    final double yTargetAnchor = ei.endY;
    // compute the desired control points
    final KVector sourceStraightCP;
    final KVector targetStraightCP;
    if (ei.invertedLeft) {
        sourceStraightCP = new KVector(endXPos, ySourceAnchor);
    } else {
        sourceStraightCP = new KVector(startXPos, ySourceAnchor);
    }
    if (ei.invertedRight) {
        targetStraightCP = new KVector(startXPos, yTargetAnchor);
    } else {
        targetStraightCP = new KVector(endXPos, yTargetAnchor);
    }
    // the center position is the same for all edges but depends on sloppiness of the routing
    double centerXPos = startXPos;
    if (!containingSegment.isWestOfInitialLayer) {
        centerXPos += edgeNodeSpacing;
    }
    centerXPos += containingSegment.xDelta + (containingSegment.rank + 0) * edgeEdgeSpacing;
    final KVector sourceVerticalCP = new KVector(centerXPos, ySourceAnchor);
    final KVector targetVerticalCP = new KVector(centerXPos, yTargetAnchor);
    // add control points to the edge's bendpoints
    edge.getBendPoints().addAll(sourceStraightCP, sourceVerticalCP);
    boolean isHyperedge = containingSegment.edges.size() > 1;
    if (isHyperedge) {
        // add an additional center control point to assert that the hyperedge segments
        // share a part of their route
        final KVector center = new KVector(centerXPos, containingSegment.centerControlPointY);
        edge.getBendPoints().add(center);
    }
    edge.getBendPoints().addAll(targetVerticalCP, targetStraightCP);
}
Also used : EdgeInformation(org.eclipse.elk.alg.layered.p5edges.splines.SplineSegment.EdgeInformation) KVector(org.eclipse.elk.core.math.KVector)

Example 4 with EdgeInformation

use of org.eclipse.elk.alg.layered.p5edges.splines.SplineSegment.EdgeInformation in project elk by eclipse.

the class FinalSplineBendpointsCalculator method calculateControlPointsSloppy.

/**
 * As opposed to the conservative strategy to insert control points (see
 * {@link #calculateControlPointsConservative(LEdge, SplineSegment)}), the sloppy version tries to use less control
 * points to make the splines curvier. The method handles only the case in which one of the two involved nodes is a
 * {@link SplineEdgeRouter#isNormalNode(LNode) normal} node.
 *
 * Let {@code e = (u,v)} be the edge for which control points are to be determined. The conservative strategy adds
 * control points {@code a,b,c,d} as depicted in the figure below. For a normal node {@code u}, it is checked if
 * {@code a,b} can be omitted. This is the case if the path of the resulting spline doesn't intersect a node that
 * lies above or below {@code u}. The same is checked for a regular node {@code v} and the control points
 * {@code c,d}. In case all control points could be omitted, a single control point is added halfway in between the
 * two nodes, where the y-coordinate is computed as implemented in
 * {@link #computeSloppyCenterY(LEdge, double, double)}. Note that if no control points can be omitted,
 * the same control points are used as with the conservative strategy.
 *
 * <pre>
 *                v
 *               ___
 *              |   |
 *    u     c--d|   |
 *   ___    |   |___|
 *  |   |   |
 *  |   |a--b
 *  |___|
 * </pre>
 *
 * To illustrate the three possible scenarios in which control points are omitted.
 * <pre>
 *   shortcut src              shortcut tgt            shortcut both
 * </pre>
 *
 * <pre>
 *               ___                      ___                     ___
 *              |   |                    |   |                   |   |
 *          +--+|   |                .---|   |               .---|   |
 *   ___    |   |___|         ___    |   |___|        ___    +   |___|
 *  |   |   |                |   |   |               |   |   |
 *  |   |---`                |   |+--+               |   |---`
 *  |___|                    |___|                   |___|
 * </pre>
 */
private void calculateControlPointsSloppy(final LEdge edge, final SplineSegment containingSegment) {
    EdgeInformation ei = containingSegment.edgeInformation.get(edge);
    assert ei.normalSourceNode || ei.normalTargetNode;
    double startXPos = containingSegment.boundingBox.x;
    double endXPos = containingSegment.boundingBox.x + containingSegment.boundingBox.width;
    final double ySourceAnchor = ei.startY;
    final double yTargetAnchor = ei.endY;
    final boolean edgePointsDownwards = ySourceAnchor < yTargetAnchor;
    // pre-compute a number of coordinates that we might use as control points
    final KVector sourceStraightCP = new KVector(startXPos, ySourceAnchor);
    final KVector targetStraightCP = new KVector(endXPos, yTargetAnchor);
    final double centerXPos = (startXPos + endXPos) / 2;
    final KVector sourceVerticalCP = new KVector(centerXPos, ySourceAnchor);
    final KVector targetVerticalCP = new KVector(centerXPos, yTargetAnchor);
    // evaluate if a rather direct curve is possible
    double centerYPos = computeSloppyCenterY(edge, ySourceAnchor, yTargetAnchor);
    KVector v1 = containingSegment.sourcePort.getAbsoluteAnchor();
    KVector v2 = new KVector(centerXPos, centerYPos);
    KVector v3 = containingSegment.targetPort.getAbsoluteAnchor();
    // approx will be an array holding two values, the zeroth of which represents the
    // center of the curve
    KVector[] approx = ElkMath.approximateBezierSegment(2, v1, v2, v3);
    boolean shortCutSource = false;
    final LNode src = containingSegment.sourcePort.getNode();
    // the node's layer)
    if (src != null && src.getLayer() != null && ei.normalSourceNode) {
        // possible intersections must only be checked if there are nodes
        // with which the edge could potentially intersect
        final boolean needToCheckSrc = edgePointsDownwards && src.id < src.getLayer().getNodes().size() - 1 || !edgePointsDownwards && src.id > 0;
        if (!needToCheckSrc) {
            shortCutSource = true;
        } else {
            // check within src's layer
            if (needToCheckSrc) {
                int neighborIndex = src.id;
                if (edgePointsDownwards) {
                    neighborIndex++;
                } else {
                    neighborIndex--;
                }
                LNode neighbor = src.getLayer().getNodes().get(neighborIndex);
                ElkRectangle box = nodeToBoundingBox(neighbor);
                shortCutSource = !(ElkMath.intersects(box, v1, approx[0]) || ElkMath.contains(box, v1, approx[0]));
            }
        }
    }
    boolean shortCutTarget = false;
    final LNode tgt = containingSegment.targetPort.getNode();
    // see comment above
    if (tgt != null && tgt.getLayer() != null && ei.normalTargetNode) {
        final boolean needToCheckTgt = edgePointsDownwards && tgt.id > 0 || !edgePointsDownwards && tgt.id < tgt.getLayer().getNodes().size() - 1;
        // tgt's layer
        if (!needToCheckTgt) {
            shortCutTarget = true;
        } else {
            int neighborIndex = tgt.id;
            if (edgePointsDownwards) {
                neighborIndex--;
            } else {
                neighborIndex++;
            }
            LNode neighbor = tgt.getLayer().getNodes().get(neighborIndex);
            ElkRectangle box = nodeToBoundingBox(neighbor);
            shortCutTarget = !(ElkMath.intersects(box, approx[0], v3) || ElkMath.contains(box, approx[0], v3));
        }
    }
    // now add the control points
    if (shortCutSource && shortCutTarget) {
        edge.getBendPoints().add(v2);
    }
    if (!shortCutSource) {
        edge.getBendPoints().addAll(sourceStraightCP, sourceVerticalCP);
    }
    if (!shortCutTarget) {
        edge.getBendPoints().addAll(targetVerticalCP, targetStraightCP);
    }
}
Also used : LNode(org.eclipse.elk.alg.layered.graph.LNode) EdgeInformation(org.eclipse.elk.alg.layered.p5edges.splines.SplineSegment.EdgeInformation) KVector(org.eclipse.elk.core.math.KVector) ElkRectangle(org.eclipse.elk.core.math.ElkRectangle)

Aggregations

EdgeInformation (org.eclipse.elk.alg.layered.p5edges.splines.SplineSegment.EdgeInformation)4 KVector (org.eclipse.elk.core.math.KVector)3 LEdge (org.eclipse.elk.alg.layered.graph.LEdge)1 LNode (org.eclipse.elk.alg.layered.graph.LNode)1 ElkRectangle (org.eclipse.elk.core.math.ElkRectangle)1