GetBezierControlPoint
(
GraphDrawingContext oGraphDrawingContext,
Point oEndpoint1,
Point oEndpoint2,
Double dBezierDisplacementFactor
)
{
Debug.Assert(oGraphDrawingContext != null);
Debug.Assert(dBezierDisplacementFactor >= 0);
AssertValid();
// This method finds the midpoint of the straight line between the two
// endpoints, then calculates a point that is displaced from the
// midpoint at a right angle. This is analagous to pulling a taut
// string at its midpoint.
//
// The calculations are based on the anonymous post "How Can I
// Calculate The Cartesian Coordinates Of A The Third Corner Of A
// Triangle If I Have The Lengths Of All Three Sides And The
// Coordinates Of The First Two Corners?" at
// http://www.blurtit.com/q9044151.html.
// Point a, the first endpoint, is one vertex of a right triangle.
Double dPointAX = oEndpoint1.X;
Double dPointAY = oEndpoint1.Y;
// Point b, the midpoint of the line between the two endpoints, is
// another vertex of the right triangle. The angle at b is 90 degrees.
Double dPointBX = dPointAX + (oEndpoint2.X - dPointAX) / 2.0;
Double dPointBY = dPointAY + (oEndpoint2.Y - dPointAY) / 2.0;
// Side C connects points a and b.
Double dSideCLength = WpfGraphicsUtil.GetDistanceBetweenPoints(
new Point(dPointBX, dPointBY), oEndpoint1);
// Side A connects points b and c, where c is the point we need to
// calculate. Make the length of A, which is the displacement
// mentioned above, proportional to the length of the line between the
// two endpoints, so that a longer line gets displaced more than a
// shorter line.
Double dSideALength = dSideCLength * dBezierDisplacementFactor;
// Calculate the angle of the line between the two endpoints.
Double dAbsAtan2 = Math.Abs( Math.Atan2(
Math.Max(oEndpoint2.Y, dPointAY) - Math.Min(oEndpoint2.Y, dPointAY),
Math.Max(oEndpoint2.X, dPointAX) - Math.Min(oEndpoint2.X, dPointAX)
) );
Rect oGraphRectangle = oGraphDrawingContext.GraphRectangle;
if (dAbsAtan2 >= Math.PI / 4.0 && dAbsAtan2 <= 3.0 * Math.PI / 4.0)
{
// The line between the two endpoints is closer to vertical than
// horizontal.
//
// As explained in the post mentioned above, the length of side A
// can be negative or positive, depending on which direction point
// c should be displaced. The following adjustments to
// dSideALength were determined experimentally.
if (oEndpoint2.Y > dPointAY)
{
dSideALength *= -1.0;
}
if (dPointBX - oGraphRectangle.Left <
oGraphRectangle.Right - dPointBX)
{
dSideALength *= -1.0;
}
}
else
{
// The line between the two endpoints is closer to horizontal than
// vertical.
if (oEndpoint2.X < dPointAX)
{
dSideALength *= -1.0;
}
if (dPointBY - oGraphRectangle.Top <
oGraphRectangle.Bottom - dPointBY)
{
dSideALength *= -1.0;
}
}
// Calculate point c.
Double dPointCX = dPointBX +
( dSideALength * (dPointAY - dPointBY) ) / dSideCLength;
Double dPointCY = dPointBY +
( dSideALength * (dPointBX - dPointAX) ) / dSideCLength;
// Don't let point c fall outside the graph's margins.
return ( WpfGraphicsUtil.MovePointWithinBounds(
new Point(dPointCX, dPointCY),
oGraphDrawingContext.GraphRectangleMinusMargin) );
}