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);
}
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()));
}
}
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);
}
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);
}
}
Aggregations