## 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.

XAML example:

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

C# example:

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.

XAML example:

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

C# example:

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.

public double ArcRoundness { get; set; }
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`

.

public bool IsClosed { get; set; }

The `Points`

property represents the collection of points of the polygon.

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:

private static void ConnectLinePoints(PathFigure pathFigure, Point p1,
Point p2, Point p3,
double roundness, bool usePercentage)
{
Point backPoint;
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;
((LineSegment)(pathFigure.Segments[lastSegmentIndex])).Point = backPoint;
var curve = new QuadraticBezierSegment { Point1 = p2, Point2 = nextPoint };
pathFigure.Segments.Add(curve);
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:

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.