Question

I want to draw a custom line cap - a equilateral triangle with the radius r. Apparently I can't:

  Dim triangleSide As Single = CSng(3 * r / Math.Sqrt(3))
  Dim triangleHeight As Single = CSng(3 * r / 2)
  path = New GraphicsPath()
  Dim points() As PointF = New PointF() { _ 
      New PointF(-triangleSide / 2, 0), _ 
      New PointF(triangleSide / 2, 0), _
      New PointF(0, triangleHeight) }
  path.AddLines(points)

  ' Not Implemented Exception, Was is Das? '
  _HlpCap = New CustomLineCap(path, Nothing) 

Do I something wrong or it's just a framework bug?

EDIT:

After Mark Cidade remark, I tried using (Nothing, path) and it helped, but I need to fill in the triangle, not only to stroke it out...

Était-ce utile?

La solution

The exception comes from the GDI+ library returning a NotImplemented status from its GdipCreateCustomLineCap() function. Try passing a stroke path instead of Nothing:

  Dim path2 As GraphicsPath = New GraphicsPath()
  path2.AddLines(points);
  _HlpCap = New CustomLineCap(path, path2) 

Autres conseils

Apparently, the path cannot cross the x-axis. I used this code to create an arrow cap:

  GraphicsPath capPath = new GraphicsPath();
  float arrowSize = 2.0f;
  capPath.AddLines(new PointF[] {
    new PointF(arrowSize, -(float)Math.Sqrt(3.0) * arrowSize),
    new PointF(0.0f, -0.01f),
    new PointF(-arrowSize, -(float)Math.Sqrt(3.0) * arrowSize)
  });

  CustomLineCap arrowCap = new CustomLineCap(capPath, null, LineCap.NoAnchor, (float)Math.Sqrt(3.0) * arrowSize);

Bad

Here's an example of a GraphicsPath that tries to draw an arrow head beyond the end of the line and causes a System.NotImplementedException.

GraphicsPath capPath = new GraphicsPath();
capPath.AddLine(0, 8, -5, 0);
capPath.AddLine(-5, 0, 5, 0);
arrowPen.CustomEndCap = new CustomLineCap(capPath, null); // System.NotImplementedException

This fails because the path must intercept the negative Y-axis. But the path above only passes through the origin and doesn't actually hit the negative Y-axis.

The remarks in the docs are crucially important:

The fillPath and strokePath parameters cannot be used at the same time. One parameter must be passed a null value. If neither parameter is passed a null value, fillPath will be ignored. If strokePath is null, fillPath should intercept the negative y-axis.

This is one of those cases where the wording is poor. The docs say "should intercept" but I think it's the case that it "must intercept" or else you're going to get a System.NotImplementedException. This intercept issue applies only to a fillPath, and not a strokePath. (The documentation could probably use some pictures too.)

Good

Here's an example of a GraphicsPath that draws an arrow head at the end of the line and works correctly. And it's likely the kind of arrow head that most people want to draw anyway.

enter image description here

GraphicsPath capPath = new GraphicsPath();
capPath.AddLine(0, 0, -5, -8);
capPath.AddLine(-5, -8, 5, -8);
arrowPen.CustomEndCap = new CustomLineCap(capPath, null); // OK

Fixing Your Example

A fix is to just subtract the triangleHeight from y. This places the tip of the arrow at 0,0 (which are the coordinates of the end of the line) which is likely what you intended anyway, and puts the base of the triangle at -triangleHeight.

float radius = 5.0f;
float triangleSide = 3.0f * radius / (float)Math.Sqrt(3.0f);
float triangleHeight = 3.0f * radius / 2.0f;
GraphicsPath capPath = new GraphicsPath();
capPath.AddLines(new PointF[] {
    new PointF(-triangleSide / 2.0f, -triangleHeight),
    new PointF(triangleSide / 2.0f, -triangleHeight),
    new PointF(0, 0) }
);
arrowPen.CustomEndCap = new CustomLineCap(capPath, null);

The exact fix for you (in Visual Basic):

  Dim points() As PointF = New PointF() { _ 
      New PointF(-triangleSide / 2, -triangleHeight), _ 
      New PointF(triangleSide / 2, -triangleHeight), _
      New PointF(0, 0) }
  path.AddLines(points)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top