## Introduction

Recently I found myself in need of a rounded corner polygon, which I needed to build from the user interface, at runtime. WPF has a polygon shape, but it does not provide round corners. I started searching the internet for examples, but didn't find many examples about this, so I started implementing my own custom rounded corners polygon. This article describes the implementation of the polygon and the way you can use it in your own applications. You can find the full implementation in the `RoundedCornersPolygon`

class in the attached demo application source code.

The attached demo application has two main sections of examples:

- Rounded corners polygons built in XAML.
- Rounded corners polygon built at runtime.

## Background

Polygons can be thought of as an equiangular set of points along a circle specified by a radius and a number of points/sides.

In WPF, you can build a polygon by adding a list of points to the `Points`

property of the `Polygon`

object.

```
<Canvas>
<Polygon Points="10,50 180,50 180,150 10,150"
StrokeThickness="1" Stroke="Black" />
</Canvas>
```

```
var cnv = new Canvas();
var polygon = new Polygon {StrokeThickness = 1, Fill = Brushes.Black};
polygon.Points.Add(new Point(10, 50));
polygon.Points.Add(new Point(180, 50));
polygon.Points.Add(new Point(180, 150));
polygon.Points.Add(new Point(10, 150));
cnv.Children.Add(polygon);
this.Content = cnv;
```

And the output will be the following rectangle:

## Using the Code

`RoundedCornersPolygon`

is very similar to a normal polygon, but we have a few more properties used for rounding the corners. First, let's look at an example of how we build the same rectangle, but with rounded corners.

```
<Canvas>
<CustomRoundedCornersPolygon:RoundedCornersPolygon Points="10,50 180,50 180,150 10,150"
StrokeThickness="1" Stroke="Black" ArcRoundness="25"
UseAnglePercentage="False" IsClosed="True"/>
<Canvas>
```

```
var cnv = new Canvas();
var roundedPolygon = new RoundedCornersPolygon
{
Stroke = Brushes.Black, StrokeThickness = 1,
ArcRoundness = 25, UseAnglePercentage = false, IsClosed = true
};
roundedPolygon.Points.Add(new Point(10, 50));
roundedPolygon.Points.Add(new Point(180, 50));
roundedPolygon.Points.Add(new Point(180, 150));
roundedPolygon.Points.Add(new Point(10, 150));
cnv.Children.Add(roundedPolygon);
this.Content = cnv;
```

And the output will be the following rectangle with rounded corners:

The polygon has four main properties:

The `ArcRoundness`

property specifies at what distance, from the `LineSegment`

end, starts the curve. This property is used together with the `UseRoundnessPercentage`

property. The `UseRoundnessPercentage`

property specifies if the `ArcRoundness`

value is a percentage or a value from the connecting segments.

If, for example, `ArcRoundness`

is set to 10 and `UseRoundnessPercentage`

is set to `false`

, then the curve will start at a distance of 10 before the end of the first segment, and it will end at a distance of 10 after the second segment starts. If `UseRoundnessPercentage`

is set to `true`

, then the distance will be 10% from those segments.

```
/// <summary>
/// Gets or sets a value that specifies the arc roundness.
/// </summary>
public double ArcRoundness { get; set; }
/// <summary>
/// Gets or sets a value that specifies if the ArcRoundness property
/// value will be used as a percentage of the connecting segment or not.
/// </summary>
public bool UseRoundnessPercentage { get; set; }
```

The `IsClosed`

property specifies if the polygon connects the last point with the first point; to finish the polygon, this property should be set to `true`

.

```
/// <summary>
/// Gets or sets a value that specifies if the polygon will be closed or not.
/// </summary>
public bool IsClosed { get; set; }
```

The `Points`

property represents the collection of points of the polygon.

```
/// <summary>
/// Gets or sets a collection that contains the points of the polygon.
/// </summary>
public PointCollection Points{ get; set; }
```

## Implementation

The control implements the `Shape`

class. The shape used to draw the polygon is a `Path`

object where we add segments of type `LineSegment`

and `QuadraticBezierSegment`

. The second segment, `QuadraticBezierSegment`

, represents a *Quadric Bézier curve*, which is defined by three points. More information about *Bézier curves* can be found at http://en.wikipedia.org/wiki/B%C3%A9zier_curve.

For a normal polygon, only `LineSegment`

would be necessary, but to make the rounded corner the *Bézier curve* is needed. Every time a Poi`n`

t is added or a property is modified, the shape is redrawn. The main method that does the rounding of the corners is the `ConnectLinePoints`

method:

```
/// <summary>
/// Method used to connect 2 segments with a common point,
/// defined by 3 points and aplying a rounding between them
/// </summary>
/// <param name="pathFigure"></param>
/// <param name="p1">First point, of the first segment</param>
/// <param name="p2">Second point, the common point</param>
/// <param name="p3">Third point, the second point of the second segment</param>
/// <param name="roundness">The roundness of the arc</param>
/// <param name="usePercentage">A value that indicates
/// if the roundness of the arc will be used as a percentage or not</param>
private static void ConnectLinePoints(PathFigure pathFigure, Point p1,
Point p2, Point p3,
double roundness, bool usePercentage)
{
//The point on the first segment where the curve will start.
Point backPoint;
//The point on the second segment where the curve will end.
Point nextPoint;
if (usePercentage)
{
backPoint = GetPointAtDistancePercent(p1, p2, roundness, false);
nextPoint = GetPointAtDistancePercent(p2, p3, roundness, true);
}
else
{
backPoint = GetPointAtDistance(p1, p2, roundness, false);
nextPoint = GetPointAtDistance(p2, p3, roundness, true);
}
int lastSegmentIndex = pathFigure.Segments.Count - 1;
//Set the ending point of the first segment.
((LineSegment)(pathFigure.Segments[lastSegmentIndex])).Point = backPoint;
//Create and add the curve.
var curve = new QuadraticBezierSegment { Point1 = p2, Point2 = nextPoint };
pathFigure.Segments.Add(curve);
//Create and add the new segment.
var line = new LineSegment { Point = p3 };
pathFigure.Segments.Add(line);
}
```

There are two methods that calculate the point where the curve should start: `GetPointAtDistance`

and `GetPointAtDistancePercent`

, one by value and the other by percent:

```
/// <summary>
/// Gets a point on a segment, defined by two points, at a given distance.
/// </summary>
/// <param name="p1">First point of the segment</param>
/// <param name="p2">Second point of the segment</param>
/// <param name="distance">Distance to the point</param>
/// <param name="firstPoint">A value that indicates
/// if the distance is calculated by the first or the second point</param>
/// <returns>The point calculated.</returns>
private static Point GetPointAtDistance(Point p1, Point p2,
double distance, bool firstPoint)
{
double totalDistance = Math.Sqrt(Math.Pow((p2.X - p1.X), 2) +
Math.Pow((p2.Y - p1.Y), 2));
double rap = firstPoint ? distance / totalDistance :
(totalDistance - distance) / totalDistance;
return new Point(p1.X + (rap * (p2.X - p1.X)), p1.Y + (rap * (p2.Y - p1.Y)));
}
private static Point GetPointAtDistancePercent(Point p1, Point p2,
double distancePercent, bool firstPoint)
{
double rap = firstPoint ? distancePercent / 100 : (100 - distancePercent) / 100;
return new Point(p1.X + (rap * (p2.X - p1.X)), p1.Y + (rap * (p2.Y - p1.Y)));
}
```

## Conclusion

There are many other details that could be implemented. This is just a first attempt for a rounded corners polygon. For example, in other cases, a random rounding would be needed for each corner; WPF offers tools to make almost anything possible when it comes to graphical interfaces. My purpose for this article was to create a rounded corners polygon that people could use and, if needed, improve further for their use.

## History

- 18
^{th}November, 2010: Initial post.