In my WPF project I would like to draw some polygons or polylines. They need to be polygons with rounded corners passing outside given vertex points as the following image example:
It is also needed that there is a hook in the first point (top-left of the polygon), but the main problem is about passing outside given points.
For example, if the points has the given coordinates:
p1(10,10)
p2(10,100)
p3(100,100)
p4(100,10)
How can I create the polygon?
I've found an example here but vertex points given are outside the shape and not inside it.
Is there a way to build this kind of shapes in WPF? No matter if it is a library or native code.
Seems that you should implement some sort of equidistant algorithm. Let's say that polygon line is at distance d from each vertex. Algorithm steps are:
UPDATE
Created sample code for illustration of the algorythm.
XAML
<Canvas Name="canvas">
<Polygon Name="poly" Stroke="Black" >
<Polygon.Points>
<Point X="110" Y="110" />
<Point X="110" Y="200" />
<Point X="200" Y="200" />
<Point X="300" Y="110" />
<Point X="200" Y="110" />
</Polygon.Points>
</Polygon>
</Canvas>
C#
double _distance = 10.0;
Line CreateLine(Point pointStart, Point pointEnd)
{
Line line = new Line();
line.Stroke = Brushes.Red;
line.X1 = pointStart.X;
line.Y1 = pointStart.Y;
line.X2 = pointEnd.X;
line.Y2 = pointEnd.Y;
return line;
}
Path CreateArc(Point pointStart, Point pointEnd, double radius)
{
ArcSegment arc = new ArcSegment();
arc.Point = pointEnd;
arc.Size = new Size(radius, radius);
var pathFigure = new PathFigure(pointStart, new PathSegment[] { arc }, false);
Path path = new Path();
path.Data = new PathGeometry(new PathFigure[] { pathFigure });
path.Stroke = Brushes.Red;
return path;
}
private void CreateDrawing()
{
for (int i = 0; i < poly.Points.Count; i++)
{
int lastPointIndex = (i > 0) ? i - 1 : poly.Points.Count - 1;
int nextPointIndex = (i < poly.Points.Count - 1) ? i + 1 : 0;
var pointsPair = GetPerpendicularPoint(poly.Points[i].X, poly.Points[i].Y, poly.Points[nextPointIndex].X, poly.Points[nextPointIndex].Y, _distance);
var lastPointsPair = GetPerpendicularPoint(poly.Points[lastPointIndex].X, poly.Points[lastPointIndex].Y, poly.Points[i].X, poly.Points[i].Y, _distance);
canvas.Children.Add(CreateLine(pointsPair.Item1, pointsPair.Item2));
canvas.Children.Add(CreateArc(lastPointsPair.Item2, pointsPair.Item1, _distance));
}
}
private Tuple<Point, Point> GetPerpendicularPoint(double startX, double startY, double stopX, double stopY, double distance)
{
Point p = new Point(startX - stopX, startY - stopY);
Point n = new Point(p.Y, -p.X);
double norm_length = Math.Sqrt((n.X * n.X) + (n.Y * n.Y));
n.X /= norm_length;
n.Y /= norm_length;
return new Tuple<Point, Point>(new Point(startX + (distance * n.X), startY + (distance * n.Y)), new Point(stopX + (distance * n.X), stopY + (distance * n.Y)));
}
RESULTS
UPDATE for non-convex polygons
If polygon is non-convex then arcs should not be added. To check if to add arc or not we may use convex polygon's feature, that all it's vertexes are on one side of the 2D plane divided by line, that defined by any every 2 neighbor vertexes. So, if polygon vertexes are give in clock wise order, then we have to check if next point is on the right side, and only in that case draw an arc.
Code:
if (IsRight(poly.Points[lastPointIndex], poly.Points[i], poly.Points[nextPointIndex]))
{
canvas.Children.Add(CreateArc(lastPointsPair.Item2, pointsPair.Item1, _distance));
}
...
public bool IsRight(Point a, Point b, Point c)
{
return ((b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X)) > .0;
}