.NET GDI+:Рисование линий со скругленными углами
-
05-07-2019 - |
Вопрос
Учитывая массив точек, легко нарисовать линию на их основе, напримериспользуя класс GraphicsPath.
Например, следующий массив точек...
[0]: (0,0)
[1]: (100,0)
[2]: (0,100)
[3]: (100,100)
...описывает линию , напоминающую букву Z.
Но вот тут-то и возникает проблема;Мне нужно нарисовать закругленные углы радиусом, например,10 пикселей.Под углами я подразумеваю точки на линии, которые не являются начальной или конечной точками.В этом случае есть два угла на (0,100)
и (100,0)
.
Я поиграл с безье, кривыми и дугами, некоторые из которых могли бы содержать решение - я просто пока не смог найти его сам, поскольку мне нужно уметь обрабатывать линии, нарисованные под всеми углами, а не только горизонтальные или вертикальные.
Установка LineJoin
из числа Pen
возражать против Round
этого недостаточно, поскольку это видно только при использовании более широких ручек.
Редактировать: Чтобы внести ясность, я хорошо осведомлен о возможностях bezier, curve и arc класса GraphicsPath.Я ищу несколько более конкретных советов относительно построения алгоритма, который может принимать любое количество точек и соединять их вместе со скругленными углами.
Решение
Я собрал следующую функцию, которая возвращает путь, представляющий линию со скругленными углами.Функция использует функцию LengthenLine, которую можно найти здесь.
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;
}
Решение
Кривые Безье довольно просты в реализации:
http://www.codeproject.com/KB/recipes/BezirCurves.aspx р>
К счастью, у вас также есть они как часть класса GraphicsPath, если вы хотите пропустить кровавые подробности:
И вы также можете посмотреть сплайны:
Другие советы
Это функция, которую я использую для рисования прямоугольника с закругленными углами ... Исходя из этого, вы можете рассчитать угол каждой линии.
Public Sub DrawRoundRect(ByVal g As Graphics, ByVal p As Pen, ByVal x As Single, ByVal y As Single, ByVal width As Single, ByVal height As Single, ByVal radius As Single)
Dim gp As GraphicsPath = New GraphicsPath
gp.AddLine(x + radius, y, x + width - (radius * 2), y)
gp.AddArc(x + width - (radius * 2), y, radius * 2, radius * 2, 270, 90)
gp.AddLine(x + width, y + radius, x + width, y + height - (radius * 2))
gp.AddArc(x + width - (radius * 2), y + height - (radius * 2), radius * 2, radius * 2, 0, 90)
gp.AddLine(x + width - (radius * 2), y + height, x + radius, y + height)
gp.AddArc(x, y + height - (radius * 2), radius * 2, radius * 2, 90, 90)
gp.AddLine(x, y + height - (radius * 2), x, y + radius)
gp.AddArc(x, y, radius * 2, radius * 2, 180, 90)
gp.CloseFigure()
g.DrawPath(p, gp)
gp.Dispose()
End Sub
Надеюсь, это поможет вам в сложной части тригонометрии;)
Это url содержит описание того, как рисовать закругленные прямоугольники, которое может помочь вам начать.
Но я бы подумал, что, по крайней мере, вы смогли бы добавить больше точек на свой путь, создать иллюзию закругленных углов.Итак, прибавьте несколько баллов от 0,0 до 100,0.Примером может быть:
(0,0) (90,0) (95,5) (95,10) (0,100)
Я никоим образом не тестировал этот путь, просто извлек несколько цифр, которые могли бы сработать из воздуха :).