Search code examples
.netgdi+drawingrounded-cornersgraphicspath

.NET GDI+: Drawing lines with rounded corners


Given an array of points, it is easy to draw a line based on these, e.g. using the GraphicsPath class.

For instance, the following array of points...

[0]: (0,0)
[1]: (100,0)
[2]: (0,100)
[3]: (100,100)

...describes a line that resembles a Z.

But here comes the challenge; I need to draw rounded corners with a radius of e.g. 10 pixels. By corners I mean the points in the line that aren't start or end points. In this case there are two corners at (0,100) and (100,0).

I've played around with beziers, curves and arcs, some of which might hold the solution - I just haven't been able to find it myself yet, since I have to be able to handle lines drawn in all angles, not just horizontal or vertical lines.

Setting the LineJoin of the Pen object to Round isn't sufficient, since this only shows with wider pens.


Edit: To clarify, I'm well aware of the bezier, curve and arc capabilities of the GraphicsPath class. I am looking for some more specific advice in regard to building the algorithm that can take any number of points, and string them together with rounded corners.


Solution

I put together the following function which returns a path representing the line with rounded corners. The function makes use of a LengthenLine function, which can be found here.

protected GraphicsPath GetRoundedLine(PointF[] points, float cornerRadius)
{
  GraphicsPath path = new GraphicsPath();
  PointF previousEndPoint = PointF.Empty;
  for (int i = 1; i < points.Length; i++)
  {
    PointF startPoint = points[i - 1];
    PointF endPoint = points[i];

    if (i > 1)
    {
      // shorten start point and add bezier curve for all but the first line segment:
      PointF cornerPoint = startPoint;
      LengthenLine(endPoint, ref startPoint, -cornerRadius);
      PointF controlPoint1 = cornerPoint;
      PointF controlPoint2 = cornerPoint;
      LengthenLine(previousEndPoint, ref controlPoint1, -cornerRadius / 2);
      LengthenLine(startPoint, ref controlPoint2, -cornerRadius / 2);
      path.AddBezier(previousEndPoint, controlPoint1, controlPoint2, startPoint);
    }
    if (i + 1 < points.Length) // shorten end point of all but the last line segment.
      LengthenLine(startPoint, ref endPoint, -cornerRadius);

    path.AddLine(startPoint, endPoint);
    previousEndPoint = endPoint;
  }
  return path;
}

Solution

  • Bezier curves are pretty straightforward to implement:

    http://www.codeproject.com/KB/recipes/BezirCurves.aspx

    Luckily you also have them as part of the GraphicsPath class if you wanna omit the gory details:

    http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.graphicspath.addbezier.aspx

    And you can also look into splines:

    http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.graphicspath.addcurve.aspx