public static Point GetEdgeEndpointOnTriangle(Point oVertexLocation, double mDHalfWidth, Point otherEndpoint)
        {
            // Instead of doing geometry calculations similar to what is done in
            // VertexDrawingHistory.GetEdgePointOnRectangle(), make use of that
            // method by making the triangle look like a rectangle.  First, figure
            // out how to rotate the triangle about the vertex location so that the
            // side containing the endpoint is vertical and to the right of the
            // vertex location.

            var dEdgeAngle = MathHelper.GetAngleBetweenPointsRadians(
                oVertexLocation, otherEndpoint);

            var dEdgeAngleDegrees = MathHelper.RadiansToDegrees(dEdgeAngle);

            double dAngleToRotateDegrees;

            if (dEdgeAngleDegrees >= -30.0 && dEdgeAngleDegrees < 90.0)
            {
                dAngleToRotateDegrees = 30.0;
            }
            else if (dEdgeAngleDegrees >= -150.0 && dEdgeAngleDegrees < -30.0)
            {
                dAngleToRotateDegrees = 270.0;
            }
            else
            {
                dAngleToRotateDegrees = 150.0;
            }

            // Now create a rotated rectangle that is centered on the vertex
            // location and that has the vertical, endpoint-containing triangle
            // side as the rectangle's right edge.

            var dWidth = 2.0 * mDHalfWidth;

            var oRotatedRectangle = new Rect(
                oVertexLocation.X,
                oVertexLocation.Y - mDHalfWidth,
                dWidth * MathHelper.Tangent30Degrees,
                dWidth
                );

            var oMatrix = GetRotatedMatrix(oVertexLocation,
                                           dAngleToRotateDegrees);

            // Rotate the other vertex location.
            var oRotatedOtherVertexLocation = oMatrix.Transform(otherEndpoint);

            // GetEdgeEndpointOnRectangle will compute an endpoint on the
            // rectangle's right edge.
            var oRotatedEdgeEndpoint = GetEdgeEndpointOnRectangle(oVertexLocation, oRotatedRectangle,
                                                                  oRotatedOtherVertexLocation);

            // Now rotate the edge endpoint in the other direction.
            oMatrix = GetRotatedMatrix(oVertexLocation,
                                       -dAngleToRotateDegrees);

            return(oMatrix.Transform(oRotatedEdgeEndpoint));
        }
        public static PathFigure GenerateArrow(Point oArrowTipLocation, Point start, Point end, double customAngle = 0.1)
        {
            //Debug.Assert(dEdgeWidth > 0);

            // Compute the arrow's dimensions.  The width factor is arbitrary and
            // was determined experimentally.

            //const Double WidthFactor = 1.5;
            var          dArrowAngle      = customAngle == 0.1 ? MathHelper.GetAngleBetweenPointsRadians(start, end) : customAngle;
            var          dArrowTipX       = oArrowTipLocation.X;
            var          dArrowTipY       = oArrowTipLocation.Y;
            const double dArrowWidth      = 3.0; //TODO dynamic width
            const double dArrowHalfHeight = dArrowWidth / 2.0;
            var          dX = dArrowTipX - dArrowWidth;

            // Compute the arrow's three points as if the arrow were at an angle of
            // zero degrees, then use a rotated transform to adjust for the actual
            // specified angle.

            var aoPoints = new[] {
                // Index 0: Arrow tip.

                oArrowTipLocation,

                // Index 1: Arrow bottom.

                new Point(dX, dArrowTipY - dArrowHalfHeight),

                // Index 2: Arrow top.

                new Point(dX, dArrowTipY + dArrowHalfHeight),

                // Index 3: Center of the flat end of the arrow.
                //
                // Note: The 0.2 is to avoid a gap between the edge endcap and the
                // flat end of the arrow, but it sometimes causes the two to
                // overlap slightly, and that can show if the edge isn't opaque.
                // What is the correct way to get the endcap to merge invisibly
                // with the arrow?

                new Point(dX + 0.2, dArrowTipY)
            };

            var oMatrix = GetRotatedMatrix(oArrowTipLocation,
                                           -MathHelper.RadiansToDegrees(dArrowAngle));

#if WPF
            oMatrix.Transform(aoPoints);
#elif METRO
            foreach (var item in aoPoints)
            {
                oMatrix.Transform(item);
            }
#endif

            return(GetPathFigureFromPoints(aoPoints[0], aoPoints[1], aoPoints[2]));
        }