private void PaintBackConnection(Pen pen,
Point srcPos,
Point tgtPos,
ConnectionPointInfo srcInfo,
ConnectionPointInfo tgtInfo,
PaintState state)
{
const float SlopeInit = 0.25f;
const float SlopeIncr = 0.23f;
// This is the maximum slope value we get before exceeding the slope threshold of 1.
float slopeMax = SlopeInit + (SlopeIncr * (float)Math.Floor((1f-SlopeInit) / SlopeIncr));
// Back connection is described by the line ABCDEF. A = srcPos and F = tgtPos.
int srcConIdx, tgtConIdx;
int srcSide, tgtSide;
// If the source and target nodes are close on the X-axis then connect to the same side on both
// nodes. Otherwise connect nodes on their facing sides.
if(Math.Abs(tgtPos.X - srcPos.X) <= NodeDiameterModel) {
srcConIdx = srcInfo._lowerLeft++;
tgtConIdx = tgtInfo._upperLeft++;
srcSide = -1;
tgtSide = -1;
}
else if(tgtPos.X > srcPos.X) {
srcConIdx = srcInfo._lowerRight++;
tgtConIdx = tgtInfo._upperLeft++;
srcSide = 1;
tgtSide = -1;
} else {
srcConIdx = srcInfo._lowerLeft++;
tgtConIdx = tgtInfo._upperRight++;
srcSide = -1;
tgtSide = 1;
}
//--- Point B.
// The line AB is a connection leg emerging from the base of a node. To visually separate multiple legs
// the first leg has a gentle gradient (almost horizontal) and each successive leg has a steeper gradient.
// Once a vertical gradient has been reached each successive leg is made longer.
// Calculate leg slope: 0=horizontal, 1=vertical. Hence this is value is not a gradient.
// Slope pre-trimming back to maximum of 1.0.
float slopePre = SlopeInit + (SlopeIncr * srcConIdx);
// Leg length.
float lenAB = state._backConnectionLegLength;
float slope = slopePre;
if(slope > slopeMax)
{ // Increase length in fractions of _backConnectionLegLength.
lenAB += (slopePre-slopeMax) * state._backConnectionLegLength;
slope = 1f;
}
// Calculate position of B as relative to A.
// Note. Length is taken to be L1 length (Manhattan distance). This means that the successive B positions
// describe a straight line (rather than the circle you get with L2/Euclidean distance) which in turn
// ensures that the BC segments of successive connections are evenly spaced out.
int xDelta = (int)(lenAB * (1f - slope)) * srcSide;
int yDelta = (int)(lenAB * slope);
Point b = new Point(srcPos.X + xDelta, srcPos.Y + yDelta);
//--- Point C.
// Line BC is a horizontal line from the end of the leg AB.
int lenBC = (int)(2f * slopePre * state._backConnectionLegLength);
xDelta = lenBC * srcSide;
Point c = new Point(b.X + xDelta, b.Y);
//--- Point E. Equivalent to point B but emerging from the target node.
slopePre = SlopeInit + (SlopeIncr * tgtConIdx);
// Leg length.
float lenEF = state._backConnectionLegLength;
slope = slopePre;
if(slope > slopeMax)
{ // Increase length in fractions of _backConnectionLegLength.
lenEF += (slopePre-slopeMax) * state._backConnectionLegLength;
slope = 1f;
}
xDelta = (int)(lenEF * (1f - slope)) * tgtSide;
yDelta = -(int)(lenEF * slope);
Point e = new Point(tgtPos.X + xDelta, tgtPos.Y + yDelta);
//--- Point D. Equivalent to point C but on the target end of the connection.
int lenDE = (int)(2f * slopePre * state._backConnectionLegLength);
xDelta = lenDE * tgtSide;
Point d = new Point(e.X + xDelta, e.Y);
state._g.DrawLines(pen, new Point[]{srcPos,b,c,d,e,tgtPos});
}