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;
}
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
This url has a description of how to draw rounded rectangles that might help you start out.
But I would think that if nothing else you would be able to add more points to your path, to give the illusion of rounded corners. So add in several points between 0,0 and 100,0. An example might be:
(0,0) (90,0) (95,5) (95,10) (0,100)
I have not tested that path in any way, just pulled some numbers that might work out of the air :).
This is the function I use to draw a rectangle with rounded corners... from this you may calculate the angle of each line.
Hope this help you in the harder part of trigonometry ;)