in GraphLayout/MSAGL/Layout/Layered/Routing.cs [68:259]
void CreateRegularSplines() {
#if PPC // Parallel -- susanlwo
var options = new ParallelOptions();
if (CancelToken != null)
options.CancellationToken = CancelToken.CancellationToken;
Parallel.ForEach<List<IntEdge>>(this.Database.RegularMultiedges, options, (intEdgeList) =>
{
#else
foreach (var intEdgeList in Database.RegularMultiedges) {
#endif
//Here we try to optimize multi-edge routing
int m = intEdgeList.Count;
bool optimizeShortEdges = m == 1 && !FanAtSourceOrTarget(intEdgeList[0]);
for (int i = m / 2; i < m; i++) CreateSplineForNonSelfEdge(intEdgeList[i], optimizeShortEdges);
#if SHARPKIT // https://github.com/SharpKit/SharpKit/issues/4
for (int i = (m / 2) - 1; i >= 0; i--) CreateSplineForNonSelfEdge(intEdgeList[i], optimizeShortEdges);
#else
for (int i = m / 2 - 1; i >= 0; i--) CreateSplineForNonSelfEdge(intEdgeList[i], optimizeShortEdges);
#endif
}
#if PPC
);
#endif
}
private bool FanAtSourceOrTarget(PolyIntEdge intEdge) {
return ProperLayeredGraph.OutDegreeIsMoreThanOne(intEdge.Source) || ProperLayeredGraph.InDegreeIsMoreThanOne(intEdge.Target);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.Msagl.Core.Geometry.Site")]
void CreateSelfSplines()
{
foreach (var kv in Database.Multiedges)
{
this.ProgressStep();
IntPair ip = kv.Key;
if (ip.x == ip.y) {
Anchor anchor = Database.Anchors[ip.x];
double offset = anchor.LeftAnchor;
foreach (PolyIntEdge intEdge in kv.Value) {
ProgressStep();
double dx = settings.NodeSeparation + settings.MinNodeWidth + offset;
double dy = anchor.BottomAnchor / 2;
Point p0 = anchor.Origin;
Point p1 = p0 + new Point(0, dy);
Point p2 = p0 + new Point(dx, dy);
Point p3 = p0 + new Point(dx, -dy);
Point p4 = p0 + new Point(0, -dy);
var s = new Site(p0);
var polyline = new SmoothedPolyline(s);
s = new Site(s, p1);
s = new Site(s, p2);
s = new Site(s, p3);
s = new Site(s, p4);
new Site(s, p0);
Curve c;
intEdge.Curve = c = polyline.CreateCurve();
intEdge.Edge.UnderlyingPolyline = polyline;
offset = dx;
if (intEdge.Edge.Label != null) {
offset += intEdge.Edge.Label.Width;
Point center =
intEdge.Edge.Label.Center =
new Point(c[(c.ParStart + c.ParEnd) / 2].X + intEdge.LabelWidth / 2, anchor.Y);
var del = new Point(intEdge.Edge.Label.Width / 2, intEdge.Edge.Label.Height / 2);
var box = new Rectangle(center + del, center - del);
intEdge.Edge.Label.BoundingBox = box;
}
Arrowheads.TrimSplineAndCalculateArrowheads(intEdge.Edge.EdgeGeometry,
intEdge.Edge.Source.BoundaryCurve,
intEdge.Edge.Target.BoundaryCurve, c, false);
}
}
}
}
void CreateSplineForNonSelfEdge(PolyIntEdge es, bool optimizeShortEdges) {
this.ProgressStep();
if (es.LayerEdges != null) {
DrawSplineBySmothingThePolyline(es, optimizeShortEdges);
if (!es.IsVirtualEdge)
{
es.UpdateEdgeLabelPosition(Database.Anchors);
Arrowheads.TrimSplineAndCalculateArrowheads(es.Edge.EdgeGeometry, es.Edge.Source.BoundaryCurve,
es.Edge.Target.BoundaryCurve, es.Curve, true);
}
}
}
void DrawSplineBySmothingThePolyline(PolyIntEdge edgePath, bool optimizeShortEdges) {
var smoothedPolyline = new SmoothedPolylineCalculator(edgePath, Database.Anchors, OriginalGraph, settings,
LayerArrays,
ProperLayeredGraph, Database);
ICurve spline = smoothedPolyline.GetSpline(optimizeShortEdges);
if (edgePath.Reversed) {
edgePath.Curve = spline.Reverse();
edgePath.UnderlyingPolyline = smoothedPolyline.Reverse().GetPolyline;
}
else {
edgePath.Curve = spline;
edgePath.UnderlyingPolyline = smoothedPolyline.GetPolyline;
}
}
//void UpdateEdgeLabelPosition(LayerEdge[][] list, int i) {
// IntEdge e;
// int labelNodeIndex;
// if (Engine.GetLabelEdgeAndVirtualNode(list, i, out e, out labelNodeIndex)) {
// UpdateLabel(e, labelNodeIndex, db.Anchors);
// }
//}
internal static void UpdateLabel(Edge e, Anchor anchor){
LineSegment labelSide = null;
if (anchor.LabelToTheRightOfAnchorCenter){
e.Label.Center = new Point(anchor.X + anchor.RightAnchor/2, anchor.Y);
labelSide = new LineSegment(e.LabelBBox.LeftTop, e.LabelBBox.LeftBottom);
}
else if (anchor.LabelToTheLeftOfAnchorCenter){
e.Label.Center = new Point(anchor.X - anchor.LeftAnchor/2, anchor.Y);
labelSide = new LineSegment(e.LabelBBox.RightTop, e.LabelBBox.RightBottom);
}
ICurve segmentInFrontOfLabel = GetSegmentInFrontOfLabel(e.Curve, e.Label.Center.Y);
if (segmentInFrontOfLabel == null)
return;
if (Curve.GetAllIntersections(e.Curve, Curve.PolyFromBox(e.LabelBBox), false).Count == 0){
Point curveClosestPoint;
Point labelSideClosest;
if (FindClosestPoints(out curveClosestPoint, out labelSideClosest, segmentInFrontOfLabel, labelSide)){
//shift the label if needed
ShiftLabel(e, ref curveClosestPoint, ref labelSideClosest);
}
else{
//assume that the distance is reached at the ends of labelSideClosest
double u = segmentInFrontOfLabel.ClosestParameter(labelSide.Start);
double v = segmentInFrontOfLabel.ClosestParameter(labelSide.End);
if ((segmentInFrontOfLabel[u] - labelSide.Start).Length <
(segmentInFrontOfLabel[v] - labelSide.End).Length){
curveClosestPoint = segmentInFrontOfLabel[u];
labelSideClosest = labelSide.Start;
}
else{
curveClosestPoint = segmentInFrontOfLabel[v];
labelSideClosest = labelSide.End;
}
ShiftLabel(e, ref curveClosestPoint, ref labelSideClosest);
}
}
}
static void ShiftLabel(Edge e, ref Point curveClosestPoint, ref Point labelSideClosest) {
double w = e.LineWidth/2;
Point shift = curveClosestPoint - labelSideClosest;
double shiftLength = shift.Length;
// SugiyamaLayoutSettings.Show(e.Curve, shiftLength > 0 ? new LineSegment(curveClosestPoint, labelSideClosest) : null, PolyFromBox(e.LabelBBox));
if (shiftLength > w)
e.Label.Center += shift/shiftLength*(shiftLength - w);
}
static bool FindClosestPoints(out Point curveClosestPoint, out Point labelSideClosest,
ICurve segmentInFrontOfLabel, LineSegment labelSide) {
double u, v;
return Curve.MinDistWithinIntervals(segmentInFrontOfLabel, labelSide, segmentInFrontOfLabel.ParStart,
segmentInFrontOfLabel.ParEnd, labelSide.ParStart, labelSide.ParEnd,
(segmentInFrontOfLabel.ParStart + segmentInFrontOfLabel.ParEnd)/2,
(labelSide.ParStart + labelSide.ParEnd)/2,
out u, out v, out curveClosestPoint, out labelSideClosest);
}
static ICurve GetSegmentInFrontOfLabel(ICurve edgeCurve, double labelY) {
var curve = edgeCurve as Curve;
if (curve != null) {
foreach (ICurve seg in curve.Segments)
if ((seg.Start.Y - labelY)*(seg.End.Y - labelY) <= 0)
return seg;
}
else Debug.Assert(false); //not implemented
return null;
}
internal static NodeKind GetNodeKind(int vertexOffset, PolyIntEdge edgePath) {
return vertexOffset == 0
? NodeKind.Top
: (vertexOffset < edgePath.Count ? NodeKind.Internal : NodeKind.Bottom);
}
}