GetBounds() { AssertValid(); if (m_oEdge.IsSelfLoop) { Debug.Assert(false); throw new InvalidOperationException("Edge is self-loop."); } // Start with a rectangle that has the same dimensions as the edge, but // that starts at the origin and has an angle of zero. Point oVertex1Location = WpfGraphicsUtil.PointFToWpfPoint( m_oEdge.Vertex1.Location); Point oVertex2Location = WpfGraphicsUtil.PointFToWpfPoint( m_oEdge.Vertex2.Location); Double dLength = WpfGraphicsUtil.GetDistanceBetweenPoints( oVertex1Location, oVertex2Location); Double dAngleDegrees = MathUtil.RadiansToDegrees( WpfGraphicsUtil.GetAngleBetweenPointsRadians( oVertex1Location, oVertex2Location)); Double dHalfWidth = m_dWidth / 2.0; Point[] ao4BoundingPoints = new Point[4] { new Point(0, -dHalfWidth), new Point(dLength, -dHalfWidth), new Point(dLength, dHalfWidth), new Point(0, dHalfWidth), }; // Rotate the rectangle so it is at the same angle as the edge. TransformPoints(new RotateTransform(dAngleDegrees), ao4BoundingPoints); // Translate the rotated rectangle to the location of the edge's first // endpoint. TransformPoints( new TranslateTransform(oVertex1Location.X, oVertex1Location.Y), ao4BoundingPoints); // Create a PathGeometry from the bounding points. return(WpfPathGeometryUtil.GetPathGeometryFromPoints( ao4BoundingPoints[0], ao4BoundingPoints[1], ao4BoundingPoints[2], ao4BoundingPoints[3] )); }
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)); }
DrawLabel ( DrawingContext oDrawingContext, GraphDrawingContext oGraphDrawingContext, Point oEdgeEndpoint1, Point oEdgeEndpoint2, String sLabel, Color oColor ) { Debug.Assert(oDrawingContext != null); Debug.Assert(oGraphDrawingContext != null); Debug.Assert(sLabel != null); AssertValid(); if (sLabel.Length == 0) { return; } sLabel = TruncateLabel(sLabel); if (oEdgeEndpoint2.X < oEdgeEndpoint1.X) { // Don't let text be drawn upside-down. Point oTemp = oEdgeEndpoint2; oEdgeEndpoint2 = oEdgeEndpoint1; oEdgeEndpoint1 = oTemp; } Double dEdgeAngleDegrees = MathUtil.RadiansToDegrees( WpfGraphicsUtil.GetAngleBetweenPoints( oEdgeEndpoint1, oEdgeEndpoint2)); Double dEdgeLength = WpfGraphicsUtil.GetDistanceBetweenPoints( oEdgeEndpoint1, oEdgeEndpoint2); // To avoid trigonometric calculations, use a RotateTransform to make // the edge look as if it is horizontal, with oEdgeEndpoint2 to the // right of oEdgeEndpoint1. RotateTransform oRotateTransform = new RotateTransform( dEdgeAngleDegrees, oEdgeEndpoint1.X, oEdgeEndpoint1.Y); oEdgeEndpoint2 = oRotateTransform.Transform(oEdgeEndpoint2); oRotateTransform.Angle = -dEdgeAngleDegrees; oDrawingContext.PushTransform(oRotateTransform); FormattedText oFormattedText = CreateFormattedText(sLabel, oColor); oFormattedText.Trimming = TextTrimming.CharacterEllipsis; if (sLabel.IndexOf('\n') == -1) { // Unless the label includes line breaks, don't allow the // FormattedText class to break the text into multiple lines. oFormattedText.MaxLineCount = 1; } Double dTextWidth = oFormattedText.Width; // The ends of the label text are between one and two "buffer units" // from the ends of the edge. The buffer unit is the width of an // arbitrary character. Double dBufferUnit = CreateFormattedText("i", oColor).Width; Double dEdgeLengthMinusBuffers = dEdgeLength - 2 * dBufferUnit; if (dEdgeLengthMinusBuffers <= 0) { return; } // Determine where to draw the label text, and where to draw a // translucent rectangle behind the text. The translucent rectangle // serves to obscure, but not completely hide, the underlying edge. Point oLabelOrigin = oEdgeEndpoint1; Rect oTranslucentRectangle; if (dTextWidth > dEdgeLengthMinusBuffers) { // The label text should start one buffer unit from the first // endpoint, and terminate with ellipses approximately one buffer // length from the second endpoint. oLabelOrigin.Offset(dBufferUnit, 0); oFormattedText.MaxTextWidth = dEdgeLengthMinusBuffers; // The translucent rectangle should be the same width as the text. oTranslucentRectangle = new Rect(oLabelOrigin, new Size(dEdgeLengthMinusBuffers, oFormattedText.Height)); } else { // The label should be centered along the edge's length. oLabelOrigin.Offset(dEdgeLength / 2.0, 0); oFormattedText.TextAlignment = TextAlignment.Center; // The translucent rectangle should extend between zero and one // buffer units beyond the ends of the text. This provides a // margin between the text and the unobscured edge, if there is // enough space. oTranslucentRectangle = new Rect(oLabelOrigin, new Size( Math.Min(dTextWidth + 2 * dBufferUnit, dEdgeLengthMinusBuffers), oFormattedText.Height) ); oTranslucentRectangle.Offset( -oTranslucentRectangle.Width / 2.0, 0); } // The text and rectangle should be vertically centered on the edge. oDrawingContext.PushTransform( new TranslateTransform(0, -oFormattedText.Height / 2.0)); // Draw the translucent rectangle, then the text. // // Note: Don't make the rectangle any more opaque than the edge, which // might be translucent itself. oDrawingContext.DrawRectangle(GetBrush( WpfGraphicsUtil.SetWpfColorAlpha(oGraphDrawingContext.BackColor, Math.Min(LabelBackgroundAlpha, oColor.A)) ), null, oTranslucentRectangle); oDrawingContext.DrawText(oFormattedText, oLabelOrigin); oDrawingContext.Pop(); oDrawingContext.Pop(); }