DrawBezierLabel
(
DrawingContext oDrawingContext,
GraphDrawingContext oGraphDrawingContext,
FormattedText oFormattedText,
Point oEdgeEndpoint1,
Point oEdgeEndpoint2,
PathGeometry oBezierCurve,
Double dLabelOriginAsFractionOfEdgeLength,
Double dEdgeLength,
Double dBufferWidth,
Color oLabelTextColor,
Color oTranslucentRectangleColor,
Double dFontSize
)
{
Debug.Assert(oDrawingContext != null);
Debug.Assert(oGraphDrawingContext != null);
Debug.Assert(oFormattedText != null);
Debug.Assert(oBezierCurve != null);
Debug.Assert(dLabelOriginAsFractionOfEdgeLength >= 0);
Debug.Assert(dEdgeLength >= 0);
Debug.Assert(dBufferWidth >= 0);
Debug.Assert(dFontSize > 0);
AssertValid();
// This method uses a modified version of the technique described in
// "Render Text On A Path With WPF," by Charles Petzold, at
// http://msdn.microsoft.com/en-us/magazine/dd263097.aspx.
if (oEdgeEndpoint2.X < oEdgeEndpoint1.X)
{
// Don't let text be drawn upside-down.
WpfGraphicsUtil.SwapPoints(ref oEdgeEndpoint1, ref oEdgeEndpoint2);
oBezierCurve = WpfPathGeometryUtil.ReverseQuadraticBezierCurve(
oBezierCurve);
}
Double dTextWidth = oFormattedText.Width;
Double dTextWidthToEdgeLengthRatio = dTextWidth / dEdgeLength;
Double dLineOffset = 0;
Point oOrigin = new Point(0, 0);
String [] asLines =
oFormattedText.Text.Split( new char[] {'\r', '\n'} );
foreach (String sLine in asLines)
{
// The label characters will be drawn one by one using positions
// computed by PathGeometry.GetPointAtFractionLength(). The first
// character will be at dLabelOriginAsFractionOfEdgeLength.
Double dFractionOfEdgeLength = dLabelOriginAsFractionOfEdgeLength;
Boolean bUsedEllipses = false;
foreach (Char c in sLine)
{
if (bUsedEllipses)
{
break;
}
Point oPointAtFractionLength;
Point oPointTangent;
oBezierCurve.GetPointAtFractionLength(dFractionOfEdgeLength,
out oPointAtFractionLength, out oPointTangent);
Double dCharacterAngleDegrees = MathUtil.RadiansToDegrees(
Math.Atan2(oPointTangent.Y, oPointTangent.X) );
String sChar = c.ToString();
if ( (oPointAtFractionLength - oEdgeEndpoint2).Length <=
6.0 * dBufferWidth)
{
// There is probably not enough room for the rest of the
// string. Terminate it with ellipses. (The buffer width
// multiple was determined experimentally.)
bUsedEllipses = true;
sChar = "...";
}
FormattedText oCharacterFormattedText =
m_oFormattedTextManager.CreateFormattedText(
sChar, oLabelTextColor, dFontSize, m_dGraphScale);
Double dCharacterWidth =
oCharacterFormattedText.WidthIncludingTrailingWhitespace;
Double dCharacterHeight = oCharacterFormattedText.Height;
// Apply a RotateTransform to make the character's base
// approximately parallel to the Bezier curve's tangent, and a
// TranslateTransform to vertically position the character.
oDrawingContext.PushTransform( new TranslateTransform(
oPointAtFractionLength.X,
oPointAtFractionLength.Y - (oFormattedText.Height / 2.0)
+ dLineOffset
) );
oDrawingContext.PushTransform( new RotateTransform(
dCharacterAngleDegrees, 0,
(oFormattedText.Height / 2.0) - dLineOffset
) );
// A translucent rectangle is drawn for each character.
// Rounding errors cause the underlying edge to show through
// faintly between the rectangles, which is a bug. How to fix
// this?
Rect oTranslucentRectangle = new Rect( oOrigin,
new Size(dCharacterWidth, dCharacterHeight) );
DrawTranslucentRectangle(oDrawingContext,
oTranslucentRectangle, oTranslucentRectangleColor);
oDrawingContext.DrawText(oCharacterFormattedText, oOrigin);
oDrawingContext.Pop();
oDrawingContext.Pop();
dFractionOfEdgeLength += dCharacterWidth / dEdgeLength;
}
dLineOffset += oFormattedText.Height / asLines.Length;
}
}