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])); }